Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel - Eager load many-to-many, get one record only (not a collection)

My posts have images (many-to many since images can have other relations as well). In my pivot table I have a boolean field called 'featured' which designates the main image for that post. I want to display in the posts index page all the posts associated with the current user. I only want to get one image from the DB and that should be the featured image. Currently I can only get the featured images as a collection. The reason for this is if the user has lots of posts I don't want to go ahead and retrieve the featured image for all their posts (N+1) but rather using eager loading get the featured imaged with only 2 queries.

\\Post Model
public function images() {
    return $this->belongsToMany(Image::class);
}

public function image(){
    return $this->images()->where('featured', '=', true)->first();
}

public function featured_image(){
    return $this->images()->where('featured', '=', true);
}


\\Controller

$user = Auth::user();

$posts = $user->posts()->with('image')->get();

// throws error
//Call to undefined method Illuminate\Database\Query\Builder::addEagerConstraints()


// if I do this
$posts = $user->posts()->with('featured_image')->get();

// I get all the user's posts, I get the featured image but as a collection even if I only have one record there

How can I do this?

like image 301
Cristian Avatar asked Nov 22 '15 17:11

Cristian


1 Answers

I think this is probably the solution you want:

\\Post Model

public function images() {
    return $this->belongsToMany(Image::class);
}

public function getFeaturedImageAttribute() {
    return $this->images->where('featured', true)->first();
}


\\Controller    

$user = Auth::user();

$posts = $user->posts()->with('images')->get();

In the resulting collection of posts, each post will have a 'featured_image' attribute that can be accessed like this:

foreach ( $posts as $post )
{
    $featured_image = $post->featured_image;

    // do something with the image...
}

IMPORTANT: Because the accessor method uses '$this->images' instead of '$this->images()', it will run using the eager loaded 'images' Collection's where() and first() methods instead of the query builder. This results in a decent chunk of PHP processing but no new queries.

like image 158
Trip Avatar answered Oct 25 '22 21:10

Trip