Description
- Laravel Version: 5.2.x
- PHP Version: 7
- Database Driver & Version: MySQL 5.7
Description:
Constraint on eager-load only retrieves 1 record, when ->take(10) is specified.
Steps To Reproduce:
- Each User model has their instagram data updated daily, and inserted into an AccountData model:
User::hasMany(AccountData::class); - User has at least 400 sibling update records inserted. Going through every user to perform updates kills memory, some have up to 2000. Only the last/previous record is needed, so the hasMany relation is constrained to take only the last 10 or so (to be safe).
Example code, before constraint:
User::with(['account_data'])->chunk(10, function ($users) {
foreach ($users as $user) {
// do something with $user->account_data->last().
// could have 400+ account_data
}
}
This will produce 400 records in the hasMany relation.
Then with optimized version:
User::with([
'account_data' => function($query) {
$query->orderBy('created_at', 'DESC')->take(10);
}])->chunk(10, function ($users) {
foreach ($users as $user) {
// zero records, 1 record, or the first ever record
// account_data->count() is 1
}
}
This is expected to eager load the previous 10 records for each user, but only ever returns a) no records, or b) the first ever record. The result is unpredictable.
When manually pulling the user's relation count, the results are consistent:
// test here is:
$user->account_data->count(); // eager-loaded
$user->account_data()->count(); // go to db
Eager-loaded count is 1 data records.
Forced count from DB is 3 records.
Eager-loaded count is 0 data records.
Forced count from DB is 0 records.
Eager-loaded count is 0 data records.
Forced count from DB is 0 records.
Eager-loaded count is 4 data records.
Forced count from DB is 552 records.
Eager-loaded count is 1 data records.
Forced count from DB is 3 records.
Eager-loaded count is 1 data records.
Forced count from DB is 29 records.
Eager-loaded count is 0 data records.
Forced count from DB is 0 records.
Edit: SQL generated by DB::getQueryLog() is:
array (
'query' => 'select * from "account_data" where "account_data"."user_id" in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) and "account_data"."deleted_at" is null order by "created_at" desc limit 10',
'bindings' =>
array (
0 => 128,
1 => 115,
2 => 116,
3 => 48,
4 => 129,
5 => 130,
6 => 69,
7 => 125,
8 => 66,
9 => 120,
),
'time' => 39.350000000000001,
),
Say there are 1000 users being chunked - appears that the eager load constraint is being applied to the child records, rather than for each user individually, i.e. to gets the last 10 records from the hasMany relation for each parent item.
Is this a case of me not applying the constraint correctly?