twigg
twigg

Reputation: 3993

Laravel Eloquent limit results for relationship

I have a simple set-up of Albums and Images, each album has many images. I can get all the data fine but I want to limit the returned number of images to 3. I have tried passing a closure like so:

Album::with(['images' =>  function($query) { $query->take(3);}])->get();

This does limit the number of images to 3 but it limits the total count of images to 3 but I want to limit each album to 3 images. So the first album will show 3 images as expected but all the other albums have no images.

I have tried adding a new method to my model like so:

public function limitImages()
{
    return $this->hasMany('App\Image')->limit(3);
}

And I call this in my controller:

Album::with('limitImages')->get();

But this doesn't limit the image count returned at all

Upvotes: 11

Views: 18362

Answers (4)

Lukas Pierce
Lukas Pierce

Reputation: 1311

Laravel 11+ allows limiting the number of eager loading results per parent using window functions.

The staudenmeir/eloquent-eager-limit package has been merged into Laravel 11+ and eager loading limits are now supported natively.

Links:

Upvotes: 0

Wande Shokunbi
Wande Shokunbi

Reputation: 91

For those who don't mind the N + 1 issue, and maybe even prefer it.

On the Album model, have a custom attribute that returns the limited relations, i.e.

class Album extends Model
{
    public function images(): hasMany
    {
        return $this->hasMany('App\Image');
    }

    public function getLimitedImagesAttribute()
    {
        return $this->images()->take(3)->get();
    }
}

In your controller:

class MainController extends Controller
{
    return Album::select('name')->get()->each->append('limited_images')
}

NOTE: This method will run N + 1 queries, where N is the number of albums in the database. This method may be preferred if the number of rows in the Album table is not too large, but the related image table is too large.

Upvotes: 3

erbelion
erbelion

Reputation: 74

the take() eloquent method just adds 'limit' word at the end of the query. this type of query is more complex and isn't supported by vanilla eloquent.

fortunately, there is an additional package called eloquent-eager-limit, which helps with this problem. in order to make it work, install that package by using composer require staudenmeir/eloquent-eager-limit command and put use \Staudenmeir\EloquentEagerLimit\HasEagerLimit; line inside both parent and children model classes.

Upvotes: 0

Eric Tucker
Eric Tucker

Reputation: 6345

I feel you'd quickly run into an N+1 issue trying to accomplish this. Just do it in the collection that it returns:

Album::with('images')->get()->map(function($album) {
    $album->setRelation('images', $album->images->take(3));
    return $album;
});

Upvotes: 18

Related Questions