framework icon indicating copy to clipboard operation
framework copied to clipboard

[9.x] Constraint withWhereHas on nested relationships

Open cosmeoes opened this issue 3 years ago • 0 comments

Related to this issue: https://github.com/laravel/framework/issues/44159

Calling withWherehas with a nested relationship and without a callback, the method returns all related models from the parent relationship even if it has no children.

SomeModel::withWhereHas('parent.child');

In that case, it will return only SomeModel where the parent has child but it would eagerly load every parent related to SomeModel not only those that have a child.

Another less abstract example:

    $user = User::factory()->create();

    Comment::factory()
        ->for($user)
        ->for(Thread::factory()->trashed()) // soft deleted thread
        ->create();

    Comment::factory()
        ->for($user)
        ->for(Thread::factory())
        ->create();

    User::query()
        ->withWhereHas('comments.thread')
        ->get();

Here it would be expected for the query to return the User model with exactly one comment (the one that has the non-deleted thread). But, instead, we get the User with the two comments, one with the thread as null.

You can circumvent this by passing a callback that calls withWhereHas with the nested relationship:

    User::query()
        ->withWhereHas('comments', function ($query) => {
            $query->withWhereHas('thread');
        })
        ->get();

With this pull request that is the generated query when calling withWhereHas with a nested relationship and no callback.

If you think the current behavior makes more sense feel free to close this pull request.

cosmeoes avatar Sep 18 '22 03:09 cosmeoes