简体   繁体   中英

Laravel: Limit Nested Relationships

Below is the code I have to fetch all Discussions with the associated threads and posts . I am trying to limit the number of posts to 1. When I do this, I expect for each thread to have only 1 post in it, however 9 threads have no posts in them and 1 thread has 1 post in it.

App\Discussion::with([
    'threads' => function ($query) => {
        // Correctly limits the number of threads to 10.
        $query->latest()->take(10);
    },
    'threads.posts' => function ($query) {
        // Limits all posts to 1, not each post to 1 for each thread.  
        $query->take(1);
    }
])->where(['slug' => 'something'])->firstOrFail();

The code above runs the following SQL queries in the database.

select * from `discussions` where (`slug` = ?) and `discussions`.`deleted_at` is null limit 1

select * from `threads` where `threads`.`discussion_id` in (?) and `threads`.`deleted_at` is null order by `created_at` desc limit 10

select * from `posts` where `posts`.`thread_id` in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) and `posts`.`deleted_at` is null order by `created_at` desc limit 1

I can see the third query is the one causing the issue as it's limiting all posts to 1.

I expect to see the following; one discussion, with 10 threads and 1 post in that thread.

{
  "id": 1,
  "title": "Discussion Title",
  ...
  "threads": [
    {
      "id": 1,
      "title": "Thread Title",
      ...
      "posts": [
        {
          "id": 1,
          "title": "Post Title",
          ...
        }
      ]
    }
    ...
  ]
}

Is there a way to do this within the Laravel framework or will I have to run raw queries? I would prefer to stick with the Eloquent ORM as much as possible.

You need to make a few "tweaks", as Eloquent does not have a query method to limit the number results in a relation.

First create this function on your Thread model.

public function one_post()
{
    //OrderBy is optional
    return $this->hasOne(Post::class)->orderBy('id', 'asc');
}

Now you have a relation that will only return one post. You can have your query this way:

    App\Discussion::with([
        'threads' => function ($query) {
            // Correctly limits the number of threads to 10.
            $query
                ->latest()
                ->take(10)
                ->with('one_post');
        }
        ])->where(['slug' => 'something'])->firstOrFail();

Eager result cannot be easily limited, but according to: https://laracasts.com/discuss/channels/eloquent/laravel-51-eloquent-relationship-hasmany-limit-records can be mapped using the model relation....in other words your code could look something like:

 App\Discussion::with([
        'threads' => function ($query) => {
            // Correctly limits the number of threads to 10.
            $query->latest()->take(10);
        }])->where(['slug' => 'something'])->firstOrFail()
        ->map(function ($thread) {
            $thread->setRelation('posts', $thread->posts->take(1));
            return $thread;
        });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM