Laravel 5.5 - Deep Relations calling by --> - laravel-5

I now spent hours googling and experimenting on trying to get an relation with two intermediate tables working.
My database looks like this:
(apt_id is apartment_id in real, was shorter to write)
I have every relation one away setup correctly with belongsTo and and hasMany:
EXAMPLE FROM House.php
public function user()
{
return $this->belongsTo('App\User');
}
public function apartments()
{
return $this->hasMany('App\Apartment');
}
Isn't there a way to access these relations like:
$house->apartments->tenants->entries
in Blade:
#foreach ( $house->apartments->tenants->entries as $entry )
, since I want to display all house entries on house.show (Blade View)
The only way it's working is by using a bunch of foreach inside each others... :/ and they define the order...
Using my wanted relation calling produces:
Property [tenants] does not exist on this collection instance.
displayed on the page.
Greetings,
Pat

I don't think you can achieve what you want using the code you posted, because when calling, for example, $house->apartments it returns a Collection object. So, it is not dealing with database anymore, that's why you would need to use a bunch of #foreachs.
I don't know if this is the best way to solve this, or if it will help you in your actual problem, but you could think this problem backwards and try something like this:
$entries = \App\Entry::whereHas('tenants', function($q) use ($house) {
$q->whereHas('apartments', function($q1) use ($house) {
$q1->where('apartments.house_id', $house->id);
});
})->get();
And in the view:
#foreach ($entries as $entry)
{{ $entry->tenant->apartment->house->name }}
#endforeach

Related

Laravel paginate (with foreign key)

I am trying to fetch some data out of a database with Laravel. The fetching part works fine, untill I want to use paginate for the data.
In my controller (RepairController) I use this function to get everyting (without pagination):
$pendingRepairs = Repair::get()->where('status.completed', 0);
This works fine. Untill I add the pagination function. Then it gives me an error:
BadMethodCallException
Method Illuminate\Database\Eloquent\Collection::paginate does not exist.
Some research on the internet shows me that I need to remove the ::get() part. But how? What is the alternative to use the get part, since I use the status.completed foreign key value?
My RepairController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Repair;
class RepairController extends Controller
{
public function index()
{
$allRepairs = Repair::get();
$pendingRepairs = Repair::get()->where('status.completed', 0)->paginate(2);
$doneRepairs = $allRepairs->where('status.completed', 1);
return view('repair.index', [
'pendingRepairs' => $pendingRepairs, 'doneRepairs' => $doneRepairs
]);
}
....
}
In Repair.php I have a link to the Statuses:
public function status()
{
return $this->belongsTo(\App\Status::class);
}
Any ideas? Thanks :)
You can use whereHas() to add constraints on relationship.
$pendingRepairs = Repair::whereHas('status', function($query) {
$query->where('completed', 0);
})
->paginate();
There is many ways to do that. First thing is whereHas as Anurat answered.
There is also another way to just join table you need
In different situations they will work in different complexity.
Note, that whereHas will do subselect
$pendingRepairs = Repair::query()
->join('statuses', 'statuses.repair_id', 'repairs.id')
->where('statuses.completed', 0)
->select('repairs.*')
->paginate();
In case when your table names are: repairs statuses and you have repair_id foreign key, if not just replace them :)
Thanks for all the replies.
I think they all will work. But I've found out the use of datatables (datatables.net).
Since that will add the pagination itself, I don't need the Larvel once anymore.

Laravel Bulk Detach

First, I use detach() in laravel with this approach and this is work!
$student = \App\StudentRegistrars::withTrashed()->findOrFail($id);
$student->father_registrars()->detach();
$student->mother_registrars()->detach();
But when I am trying to mass detach in laravel, I can't do that.
$students = \App\StudentRegistrars::whereIn('id', $ids);
$student->father_registrars()->detach();
$student->mother_registrars()->detach();
Any solution about this problem?
There's quite a few issues with your code. You're close, but not quite there.
First, you need to be aware of what your variable is. Currently, this is not working, as $student is not defined. You called your variable $students (with an 's'), so make sure you're using the right variable.
Secondly, at the point you're calling detach(), your $students variable is an instance of the Builder class, not a single StudentRegistrars instance. You need to finalize your query with a closure:
$students = \App\StudentRegistrars::whereIn('id', $ids)->get();
Thirdly, the detach() method only works on a single instance, so if you called $students->father_registrars()->detach(), it would still fail.
You need to iterate your results and call detach() one-by-one:
foreach ($students as $student) {
$student->father_registrars()->detach();
$student->mother_registrars()->detach();
}
You final code would therefore be:
$students = \App\StudentRegistrars::whereIn('id', $ids)->get();
foreach ($students as $student) {
$student->father_registrars()->detach();
$student->mother_registrars()->detach();
}
There are more efficient approaches to mass detachment, such as directly querying and deleting records from the pivot table (or tables):
DB::table('pivot')->whereIn('column', $ids)->delete();
But this answer serves to fix your logic issues.

Laravel Eloquent can increment ($collection->increment('field')) fail?

I have a redirect function that looks like this. For some reason, it looks like it's sometimes missing to increment the item. I'm wondering if it sometimes can be so that it misses this because some caching or something?
public function redirect($key)
{
$item = $this->items->findOrFail($key);
$item->increment('redirects');
$encode = urlencode($item->_url);
return view('item.redirect', ['url' => ($encode)]);
}
I'm not sure about topic of your question. Do you get such method somewhere from Laravel?
I don't know what is $key here but you should make sure this is single id (for example 2) and not array.
For single model:
$item->increment('redirects');
will work
but if $key sometimes is array then
$item = $this->item->findOrFail($key);
will be collection, so increment method would cause error, because there is no increment method on Eloquent collection.

Switching from Limit() to First() in Laravel Model

I'm stuck on an issue where I'm trying to clean up code presented to me, and I feel like I must've just been at this too long and can't see this issue.
At the moment I have the following code in one of my models:
public function attachments(){
return $this->hasMany(volunteerAttachment::class, 'volunteerID','id');
}
public function photoID() {
$photoID= $this->attachments()->where('category','Drivers License')->orderBy('endDate','desc')->limit(1);
return $photoID;
}
What I would like to do is what I believed was relatively simple in replacing the limit(1) of the function photoIdentification with a simple first().
But when I try that and simply put {{$volunteer->photoID->id}} in my blade, it simply returns the error App\volunteer::photoID must return a relationship instance.
I do however know that there is a relationship because if I continue to use the limit(1) and put:
#foreach($volunteer->photoID as $id)
{{$id->id}}
#endforeach
It returns the relation and document correctly.
$volunteer is the variable for that particular model App\volunteer, and the following is how it is defined in the controller:
public function show(Volunteer $volunteer){
return view('volunteer.show', compact('volunteer'));
}
The function photoID() will still return a full volunteerAttachment object. If you want to get the id using the first() function you will have to select the id property on the object like so:
{{ $volunteer->photoID()->id }}
You could also create an accessor on the model that directly returns this property:
public function getPhotoidAttribute($value)
{
return $this->attachments()->where('category','Drivers License')->orderBy('endDate','desc')->first()->id;
}
Now in blade you can just use:
{{ $volunteer->photoid }}
When you call a function of a model as a variable Laravel assumes you try to return a related model. Try with an accessor instead:
public function getPhotoidAttribute($value) {
$photoID= $this->attachments()->where('category','Drivers License')->orderBy('endDate','desc')->first();
return $photoID->id;
}
and call it like this:
{{ $volunteer->photoid }}

Passing ArrayAccess from Controller to View in Laravel 4

I try to learn Laravel 4 and I have found it - so far - pretty great. But Blade templaes seem dfficult to me, less intuitive than Smarty for instance.
I have a small problem. I try to push my data from Controller to a View. I do this:
public function getGame($id, $slug = null) {
$game['info'] = Game::find($id);
$game['genres'] = Game::find($id)->genres()->get();
$game['dev'] = Game::find($id)->developers()->get();
$this->layout = View::make('user')->with('game',$game);
}
Pretty straightforward, isn't it? For now, my View is just {{ $game['info']->title }}. But it seems that it doesn't see my variable (throwing "Undefined variable: game"). What can I do? Can I post the data in that array format (I assumed from the docs that I can).
I think the problem is in the last line of your code. You either return a view like
return View::make('user')->with('game', $game);
or do something like this:
Create protected $layout = 'my_layout.blade.php' in your controller and create the file my_layout.blade.php and put it into views folder. In it write something like
#yield('content')
Then, create your user.blade.php and create a section like this
#section('content')
....
#stop
Finaly, at the end of your getGame function
$this->layout->content = View::make('user')->with('game', $game);
Home this make sense to you.
Btw, why not define 'genres' and 'developers' relationships in your Game model and then use eager loading like
$game = Game::with('genres', 'developers')->find( $id );
and pass this to the view. Then access title like $game->title, genres like $game->genres->... etc.
Regards,
Vlad

Resources