4

I have hasmany relation from category to contents, and I want limitation 4 content for each category.

I would like to limit the result of relation contents that has sub relation to languages

My Code

Category::with(['contents.languages' => function($query){
        $query->limit(4);
    }])
        ->get();

But I see in log the limit works on languages relation, not contents, that I wanted is limit on contents

Muhamad Yulianto
  • 1,573
  • 3
  • 22
  • 32

6 Answers6

2

take() and limit() functions will not work with eager loading if you retrieve parent model more than one using get().

So you have to do another way,

$categories = Category::with('contents')->get();

After retrieving $categories, you can do foreach loop like below,

$contents = [];
foreach($categories as $category){
   $category->limitedContents = $category->contents()->with('languages')->limit(4);
}

And by doing this you will get 4 contents per category in all categories with limitedContents.

Note: Here I used name as 'limitedContents' because you have already defined contents relationship.

Chirag Patel
  • 1,545
  • 2
  • 9
  • 17
  • oh thanks, I think your statement is correct, but if I use your code, It will get all relation in backend and maybe will makes heavy load if I have lots of contents data, get all categories only, and then looping for finding the contents with limitation maybe the best way to optimize – Muhamad Yulianto Apr 09 '18 at 13:31
  • 1
    yeah it will take little more time then eager loading but we get the best result with this :) – Chirag Patel Apr 09 '18 at 13:33
  • I had to add ->get() after the limit(4) – Jacob Hyde Mar 26 '20 at 21:28
1

This question is basically something akin to Get top n records for each group of grouped results

As far as I can see there's not much choice but to perform N+1 queries. You can achieve this by doing:

  $categories = Category::get(); 
  $categories->each(function ($category) {
     $category->load([ 'contents' => function ($q) {
             return $q->limit(4);
     }, 'contents.languages']);
  });

Can we do better? I doubt it doubtful, though I am open to ideas. While we can optimise this to send a less queries to the database, the database internally will still need to compute the N+1 queries.

apokryfos
  • 38,771
  • 9
  • 70
  • 114
  • not working, the contents just showing 4, I have hasmany relation between categories and contents, I want limit 4 content for each categories – Muhamad Yulianto Apr 09 '18 at 13:13
  • Do you mean that contents is just showing the number 4? – apokryfos Apr 09 '18 at 13:15
  • I mean 4 items, I have many category and hasmany contents, I want to get all categories but only 4 content for each category – Muhamad Yulianto Apr 09 '18 at 13:17
  • You just said that you wanted to get 4 contents for each category and you are getting 4 contents for each category. What's not working? – apokryfos Apr 09 '18 at 13:18
  • It just showing 4 contents for all category, not for each category. in log I see this: select * from `content` where `content`.`category_id` in ('2', '3') limit 4 – Muhamad Yulianto Apr 09 '18 at 13:19
  • I thought it was possible but it probably isn't with a single query eager load query. It might be but I don't really know how. At any rate this update should work but it will do N+1 queries – apokryfos Apr 09 '18 at 13:45
1

This also works

foreach(Category::with('contents')->get() as $category)
   {
        foreach($category->contents->take(4) as $content)
        {
            $languages = $content->with('languages')->get();
            foreach($languages as $language)
             {
                //your code
             }
        }
   }
afsal c
  • 612
  • 4
  • 12
0

You should try this:

Category::with(['contents.languages'])->limit(4)->get();
AddWeb Solution Pvt Ltd
  • 21,025
  • 5
  • 26
  • 57
0

You could set additional method in Category model and make a call with that one:

public function contentsTake4()  
{
    return $this->hasMany('App\Content')->limit(4);
}

Then you would call it in controller as :

Category::with('contentsTake4.products')->get();
Tpojka
  • 6,996
  • 2
  • 29
  • 39
  • I just try this before, and the result is only get 4 contents for all categories, not for each categories – Muhamad Yulianto Apr 09 '18 at 13:52
  • mysql log is like this, select * from content where content.category_id in ('2', '3') limit 4; it will showing only 4 contents for category id 2 and 3, that I want is 4 contents limit for each category (2 and 3) – Muhamad Yulianto Apr 09 '18 at 14:00
-1

Try this

Category::with([
   'contents' => function($q) { $q->limit(4); },
   'contents.languages'
])->get();
Ankit
  • 1
  • 1