From 32a66d9e82237da83e182a8ebea718f57c5da81c Mon Sep 17 00:00:00 2001 From: Angus McRitchie Date: Fri, 22 Aug 2025 19:07:44 +1000 Subject: [PATCH 1/2] init --- .../Database/Eloquent/Collection.php | 32 ++--- .../EloquentCollectionLoadMissingTest.php | 128 ++++++++++++++++++ 2 files changed, 144 insertions(+), 16 deletions(-) diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index 62e84780a7d6..f47130da5a2e 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -221,28 +221,28 @@ public function loadMissing($relations) $relations = func_get_args(); } - foreach ($relations as $key => $value) { - if (is_numeric($key)) { - $key = $value; - } + if ($this->isNotEmpty()) { + $query = $this->first()->newQueryWithoutRelationships()->with($relations); - $segments = explode('.', explode(':', $key)[0]); + foreach ($query->getEagerLoads() as $key => $value) { + $segments = explode('.', explode(':', $key)[0]); - if (str_contains($key, ':')) { - $segments[count($segments) - 1] .= ':'.explode(':', $key)[1]; - } + if (str_contains($key, ':')) { + $segments[count($segments) - 1] .= ':'.explode(':', $key)[1]; + } - $path = []; + $path = []; - foreach ($segments as $segment) { - $path[] = [$segment => $segment]; - } + foreach ($segments as $segment) { + $path[] = [$segment => $segment]; + } - if (is_callable($value)) { - $path[count($segments) - 1][end($segments)] = $value; - } + if (is_callable($value)) { + $path[count($segments) - 1][end($segments)] = $value; + } - $this->loadMissingRelation($this, $path); + $this->loadMissingRelation($this, $path); + } } return $this; diff --git a/tests/Integration/Database/EloquentCollectionLoadMissingTest.php b/tests/Integration/Database/EloquentCollectionLoadMissingTest.php index e95d7aca9b41..42260013969e 100644 --- a/tests/Integration/Database/EloquentCollectionLoadMissingTest.php +++ b/tests/Integration/Database/EloquentCollectionLoadMissingTest.php @@ -118,6 +118,133 @@ public function testLoadMissingWithoutInitialLoad() $this->assertEquals(1, $user->posts[1]->postRelation->postSubRelations[0]->postSubSubRelations->count()); $this->assertInstanceOf(PostSubSubRelation::class, $user->posts[1]->postRelation->postSubRelations[0]->postSubSubRelations[0]); } + + public function testLoadMissingWithNestedArraySyntax() + { + $posts = Post::with('user')->get(); + + DB::enableQueryLog(); + + $posts->loadMissing([ + 'comments' => ['parent'], + 'user', + ]); + + $this->assertCount(2, DB::getQueryLog()); + $this->assertTrue($posts[0]->comments[0]->relationLoaded('parent')); + $this->assertTrue($posts[0]->relationLoaded('user')); + } + + public function testLoadMissingWithMultipleDotNotationRelations() + { + $posts = Post::with('comments')->get(); + + DB::enableQueryLog(); + + $posts->loadMissing([ + 'comments.parent', + 'user.posts', + ]); + + $this->assertCount(3, DB::getQueryLog()); + $this->assertTrue($posts[0]->comments[0]->relationLoaded('parent')); + $this->assertTrue($posts[0]->relationLoaded('user')); + $this->assertTrue($posts[0]->user->relationLoaded('posts')); + } + + public function testLoadMissingWithComplexNestedArraySyntax() + { + $posts = Post::all(); + + DB::enableQueryLog(); + + $posts->loadMissing([ + 'comments' => [ + 'parent' => function ($query) { + $query->select('id', 'parent_id', 'post_id'); + }, + 'revisions', + ], + 'user:id,name', + ]); + + $this->assertCount(4, DB::getQueryLog()); + $this->assertTrue($posts[0]->relationLoaded('comments')); + $this->assertTrue($posts[0]->comments[0]->relationLoaded('parent')); + $this->assertTrue($posts[0]->relationLoaded('user')); + } + + + public function testLoadMissingWithNestedArray() + { + $posts = Post::with('comments')->get(); + + DB::enableQueryLog(); + + $posts->loadMissing(['comments' => ['parent']]); + + $this->assertCount(1, DB::getQueryLog()); + $this->assertTrue($posts[0]->comments[0]->relationLoaded('parent')); + } + + public function testLoadMissingWithNestedArrayWithClosure() + { + $posts = Post::with('comments')->get(); + + DB::enableQueryLog(); + + $posts->loadMissing(['comments' => ['parent' => function ($query) { + $query->select('id'); + }]]); + + $this->assertCount(1, DB::getQueryLog()); + $this->assertTrue($posts[0]->comments[0]->relationLoaded('parent')); + $this->assertArrayNotHasKey('post_id', $posts[0]->comments[1]->parent->getAttributes()); + } + + public function testLoadMissingWithMultipleNestedArrays() + { + $users = User::get(); + $users->loadMissing([ + 'posts' => [ + 'postRelation' => [ + 'postSubRelations' => [ + 'postSubSubRelations', + ], + ], + ], + ]); + + $user = $users->first(); + $this->assertEquals(2, $user->posts->count()); + $this->assertNull($user->posts[0]->postRelation); + $this->assertInstanceOf(PostRelation::class, $user->posts[1]->postRelation); + $this->assertEquals(1, $user->posts[1]->postRelation->postSubRelations->count()); + $this->assertInstanceOf(PostSubRelation::class, $user->posts[1]->postRelation->postSubRelations[0]); + $this->assertEquals(1, $user->posts[1]->postRelation->postSubRelations[0]->postSubSubRelations->count()); + $this->assertInstanceOf(PostSubSubRelation::class, $user->posts[1]->postRelation->postSubRelations[0]->postSubSubRelations[0]); + } + + public function testLoadMissingWithMultipleNestedArraysCombinedWithDotNotation() + { + $users = User::get(); + $users->loadMissing([ + 'posts' => [ + 'postRelation' => [ + 'postSubRelations.postSubSubRelations', + ], + ], + ]); + + $user = $users->first(); + $this->assertEquals(2, $user->posts->count()); + $this->assertNull($user->posts[0]->postRelation); + $this->assertInstanceOf(PostRelation::class, $user->posts[1]->postRelation); + $this->assertEquals(1, $user->posts[1]->postRelation->postSubRelations->count()); + $this->assertInstanceOf(PostSubRelation::class, $user->posts[1]->postRelation->postSubRelations[0]); + $this->assertEquals(1, $user->posts[1]->postRelation->postSubRelations[0]->postSubSubRelations->count()); + $this->assertInstanceOf(PostSubSubRelation::class, $user->posts[1]->postRelation->postSubRelations[0]->postSubSubRelations[0]); + } } class Comment extends Model @@ -200,6 +327,7 @@ class Revision extends Model class User extends Model { public $timestamps = false; + protected $guarded = []; public function posts() { From 008631e82f09196dac2b7e337031053634beca38 Mon Sep 17 00:00:00 2001 From: Angus McRitchie Date: Fri, 22 Aug 2025 19:49:31 +1000 Subject: [PATCH 2/2] fix colon select test --- .../EloquentCollectionLoadMissingTest.php | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/tests/Integration/Database/EloquentCollectionLoadMissingTest.php b/tests/Integration/Database/EloquentCollectionLoadMissingTest.php index 42260013969e..0b37f4868772 100644 --- a/tests/Integration/Database/EloquentCollectionLoadMissingTest.php +++ b/tests/Integration/Database/EloquentCollectionLoadMissingTest.php @@ -152,29 +152,19 @@ public function testLoadMissingWithMultipleDotNotationRelations() $this->assertTrue($posts[0]->user->relationLoaded('posts')); } - public function testLoadMissingWithComplexNestedArraySyntax() + public function testLoadMissingWithNestedArrayWithColon() { - $posts = Post::all(); + $posts = Post::with('comments')->get(); DB::enableQueryLog(); - $posts->loadMissing([ - 'comments' => [ - 'parent' => function ($query) { - $query->select('id', 'parent_id', 'post_id'); - }, - 'revisions', - ], - 'user:id,name', - ]); + $posts->loadMissing(['comments' => ['parent:id']]); - $this->assertCount(4, DB::getQueryLog()); - $this->assertTrue($posts[0]->relationLoaded('comments')); + $this->assertCount(1, DB::getQueryLog()); $this->assertTrue($posts[0]->comments[0]->relationLoaded('parent')); - $this->assertTrue($posts[0]->relationLoaded('user')); + $this->assertArrayNotHasKey('post_id', $posts[0]->comments[1]->parent->getAttributes()); } - public function testLoadMissingWithNestedArray() { $posts = Post::with('comments')->get();