Why can't I access the property in an eager loaded senario?
I am trying to access the photo location for my facilities model. They have a Many:Many relationship. When I load the facility info using
$facilities = Facility::with('photos')->get();
When I try to access
foreach($facilities as $facility)
{
echo $facility->photos->id;
}
I get the Undefined property: Illuminate\Database\Eloquent\Collection::$id
If I echo $facilities->photos I end up with.
[{"id":"3",
"name":null,
"location":"facilities\/facility1.jpg\r",
"created_at":"2013-07-18 14:15:19",
"deleted_at":null,
"updated_at":null,
"pivot":{"facility_id":"5","photo_id":"3"}}]
Whats the best way to access any property in this array?
With eager loading the photos you get a photos collection and in order to get the photo you need to iterate through the photos collection e.g.
foreach($facilities as $facility)
{
foreach($facility->photos as $photo)
{
echo $photo->id;
}
}
If you only want just the first photo you can get it by calling the first() method on the collection
foreach($facilities as $facility)
{
$facility->photos->first()->id;
}
What is received from the database is an array with one object. This is why a foreach works. To access it the way you want you should add the first() method just as Atrim says, you can even drop the get() method. Only I would suggest doing it in the controller.
$facilities = Facility::with('photos')->first();
{{ $facilities->location }}
Thanks Altim, I just hit this problem too and the nested foreach is one of the solutions.
Nevertheless, I'll share with you guys the solution that I used at the end.
Instead of the nested foreach or even the eager loader, I used a Join Query Builder. In my case:
$posts = DB::table('posts')
->leftJoin('users', 'posts.user_id', '=', 'users.id')
->get();
This allowed me to use one foreach, and all the data is at the same level, in my case for instance would be something like:
#foreach ($posts as $post)
<p>{{ $post->first_name }}</p> # From users table
<p>{{ $post->title }}</p> # From posts table
<p>{{ $post->body }}</p>
#endforeach
Maybe it is not answering this question exactly , but hope it helps to others like me that ended up in this particular question.
Related
Reference: Laravel Eloquent relationship not an object when doing anything but dd()
I am trying to output a Laravel relationship in Blade. However, this, {{ $video->channel->id }} returns a non-object error. But, when dded, like so, {{ dd($video->channel->id) }}, a value is there. I've been pulling my hair so hard because of this... What is going on? Why is there an output only when the variable is dded?
I'm testing on PHP 7.2 with Laravel 5.6. Relationships are established as per Eloquent documentation. I've tried fetching the data like this:
$videos = Video::where('foo', 'bar')->with('channel')->take(100)->get();
and
$videos = Video::where('foo', 'bar')->take(100)->get();
The same thing; same output/error.
$tokens = preg_split("/[\s,]+/", $q);
$videos = Video::with('channel')
->where(function ($query) use ($tokens) {
foreach ($tokens as $token) {
$query->orWhere('title', 'LIKE', "%$token%");
}
})
->take(100)
->get();
// foreach ($videos as $video)
{{ $video->channel->id }} // non-object error
{{ dd($video->channel->id) }} // WORKS! IDK Why...
Trying to get property 'id' of non-object is the actual error log.
EDIT:
Below is an image of the $videos collection fetched from Database.
If in dd(), something gets outputted fine, I can only assume I'm fetching the data right, yea?
As you're doing this in a loop, the error
Trying to get property 'id' of non-object
means that 1 iteration of $video->channel returns a non-object (likely null), and you can't access the property ->id of it (as null doesn't have any properties, etc.)
The reason dd() works is that it's dumping and dieing on the first iteration, which has a channel. To handle this, simply add an #if() clause:
#foreach($videos AS $video)
#if($video->channel)
{{ $video->channel->id }}
#else
...
#endif
...
#endforeach
Or, in the query to get $videos, enforce relationship:
$videos = Video::where('foo', 'bar')->has('channel')->with('channel')->take(100)->get();
This way, $video->channel will not be null.
Just an example:
let's say I have Post model, and the Comment model. Post, of course, have Comments, one-to-many relation.
I have to display list of posts with comments below it.
I'll get my posts in the controller:
$posts = Post::get(), I'll pass it to the blade view and then I'll loop through it
#foreach($posts as $post)
{{ $post->title }}
{{ $post->comments }}
#endforeach
where $post->comments is some relation
public function comments()
{
return $this->hasMany(Comment::class);
}
As we know, that query will be executed many times.
Now my question: how we should optimize it?
Return Cache::remember in the getter?
Get (somehow?) those comments, when getting the posts in one query? Something like join query? I know that I can write that kind of query, but I'm talking about Eloquent's query builder. And then how get the comments within the loop? Wouldn't {{ $post->comments }} call the relation again instead of getting stored data?
Different solution?
You can do $posts = Post::with('comments')->get() to eager load the comments with the post. Read more about it in the documentation: https://laravel.com/docs/5.7/eloquent-relationships#eager-loading
Also, to display the comments you would want to add another foreach loop. It would look something like this:
#foreach($posts as $post)
{{ $post->title }}
#foreach($post->comments as $comment)
{{ $comment->title }}
#endforeach
#endforeach
You’ve probably cached some model data in the controller before, but I am going to show you a Laravel model caching technique that’s a little more granular using Active Record models
Note that we could also use the Cache::rememberForever() method and rely on our caching mechanism’s garbage collection to remove stale keys. I’ve set a timer so that the cache will be hit most of the time, with a fresh cache every fifteen minutes.
The cacheKey() method needs to make the model unique, and invalidate the cache when the model is updated. Here’s my cacheKey implementation:
public function cacheKey()
{
return sprintf(
"%s/%s-%s",
$this->getTable(),
$this->getKey(),
$this->updated_at->timestamp
);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function getCachedCommentsCountAttribute()
{
return Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
return $this->comments->count();
});
}
yes u can do like that in controller
$minutes = 60;
$posts = Cache::remember('posts', $minutes, function () {
return Post::with('comments')->get()
});
in blade u can get like that
#foreach($posts as $post)
{{ $post->title }}
#foreach($post->comments as $comment)
{{ $comment->title }}
#endforeach
#endforeach
for more information read this article
I have set up two model with its row in table. And made a single form to fill both tables and it works perfectly
Tour.php
public function featuredImage()
{
return $this->hasOne('App\FeaturedImage');
}
tours table
id|name|content|featured_status
featuredImage.php
public function tour()
{
return $this->belongsTo('App\Tour');
}
Featured_images table
id|tour_id|path|name
Code in my controller to pass data to view.
$tours = Tour::where('featured', 1)->get();
return view('public.pages.index')
->withTours($tours);
Code in my view
#foreach($tours as $featured)
<div class="thumbnail">
<img src="{{$featured->featuredimage->path}}" alt="{{$featured->featuredImage->name}}">
</div>
<h4>{{$featured-name}}</h4>
#endforeach
The trouble is I'm not able to fetch featured images by writing
{{$featured->featuredimage->path}}
and the error is
Trying to get property of non-object
on the line {{$featured->featuredimage->path}}. I have used this method in my previous project and it had worked perfectly but it isn't going well in this one.
I tried replacing {{$featured->featuredimage->path}} with {{$featured->featuredImage->path}} but didn't worrked out.
Do this:
{{ $featured->featuredImage()->path }}
Also, you're creating a lot of additional queries here. You should use eager loading to solve N + 1 problem:
$tours = Tour::with('featuredImage')->where('featured', 1)->get();
And display data with:
{{ $featured->featuredImage->path }}
I need to get each comment of news . Its working good for firstorFail() item
{{$news->comments()->firstOrFail()->name}}
But bring me empty result when im try this one with foreach:
#foreach($news->comments() as $comment)
{{$comment->name}}
#endforeach
The function firstOrFail will return exactly one comment. Seems like you actually want all comments? You should use this in your controller
$news->comments; // yeah that's it, it will load all comments
Also return news
return view('my.view', compact('news'));
Then use it in blade
#foreach($news->comments as $comment)
{{$comment->name}}
#endforeach
the line
$news->comments()
would require you to also call ->get() , because comments() will return the relation instead of the actual data.
This: $news->comments() bringing you query builder probably. So what you need is to ->get() for execute query like this:
#foreach($news->comments()->get() as $comment)
{{$comment->name}}
#endforeach
Edit
or as a #Roy Philips mentioned: $news->comments
I have a laravel query as the one below:
$campaign = Campaign::with(array('tracks.flights' => function($q) use ($dates)
{
$q->whereRaw("flights.start_date BETWEEN '". $dates['start']."' AND '".$dates['end']."'")->orderBy('start_date')
->with('asset')
->with('comments');
}
))
->with('tracks.group')
->with('tracks.media')
->orderBy('created_at', 'desc')
->find($id);
I am very new to laravel and right now the response from this query returns all the data required including the comments with the comments attributes from the DB.
What i want to achieve is manipulate the comments object so it includes the user name from the users table as the comments table has the user_id only as an attribute.
How can I achieve this? I am very new to laravel.
Your Comment model must have a relationship with the User model, such as:
public function user()
{
return $this->belongsTo('App\User');
}
Now when you are iterating comments you are able to do $comment->user->name or in your case it will be something like this:
#if(!$campaign->tracks->flights->isEmpty())
#foreach($campaign->tracks->flights as $flight)
#if(!$flight->comments->isEmpty())
#foreach($flight->comments as $comment)
{!! $comment->user->name !!}
#endforeach
#endif
#endforeach
#endif
Once you can do this, next step is to understand eager load with eloquent.