Skip to content

Eager-loading with a limit on collection only loads for last element in collection #18014

Closed
@jstoone

Description

@jstoone
  • Laravel Version: 5.3.*
  • PHP Version: 5.6 + 7.0 + 7.1
  • Database Driver & Version: sqlite (3.14.0) + mysql (5.7.17 Homebrew)

Description:

There are some issues that might relate to this one, but for me they either seemed like the examples were not clear or contained domain logic that might not relate to the bug itself. See a list of such issues at the bottom.

Below I will try to explain the bug in what I see as its simplest form.

We have

  • A user model
  • A books model

The user has many books, but sometimes we only want 5 books, so In our User model we have the following:

/**
 * Defines HasMany relationship between the user and its books
 */
public function books()
{
    return $this->hasMany(\App\Books::class);
}

/**
 * Limit the relationship to only include 5 results
 */
public function onlyFiveBooks()
{
    return $this->books()->take(5);
}
  • We have two users
  • Both users have 10 books

What I expected

// Given
$users = \App\User::all();

// Act
$users->load('onlyFiveBooks');

// Assert we have two users
$users->count() == 2 // true

// Assert first user has 5 loaded books
$users->first()->getRelation('books')->count() == 5 // false
// Assert last user has 5 loaded books
$users->last()->getRelation('books')->count() == 5 // true

So I expected both users to have 5 loaded books, given the relationship defined above.

What have I tried?

// FAIL 1: Using the previously defined `books()` relationship, same as above
\App\User::with('onlyFiveBooks')->get();

// FAIL 2: Still only loads 5 books to the last of the two users
\App\User::with(['books' => function($query) {
    $query->take(5);
}])->get();

// FAIL 3: Still only loads 5 books to the last of the two users
$users = \App\User::all();
$users->load(['books' => function($query) {
    $query->take(5);
}]);

// SUCCESS: Being explicit (and maybe redundant) does produce the correct outcome
$users = \App\User::all();
$users->each(function($user) {
    $user->load('onlyFiveBooks');
});
// Assert first user has 5 loaded books
$users->first()->getRelation('books')->count() == 5 // true
// Assert last user has 5 loaded books
$users->last()->getRelation('books')->count() == 5 // true

In the wild

I've setup a project with a failing test to illustrate this bug.

git clone https://github.com/jstoone/laravel \
  -b bug-in-a-limited-eager-load-example \
  jstoone-laravel-example

cd jstoone-laravel-example

composer install

cp .env.example .env

phpunit

Somewhat similar issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions