diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index e5c6c383addb..fba7eecd66f9 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -221,7 +221,7 @@ public function loadMissing($relations) $relations = func_get_args(); } - foreach ($relations as $key => $value) { + foreach ($this->prepareLoadMissingRelationships($relations) as $key => $value) { if (is_numeric($key)) { $key = $value; } @@ -248,6 +248,40 @@ public function loadMissing($relations) return $this; } + /** + * Prepare nested load missing relationships. + * + * @param array $relations + * @param string $prefix + * @return array + */ + protected function prepareLoadMissingRelationships($relations, $prefix = '') + { + $preparedRelationships = []; + + foreach ($relations as $key => $value) { + $fullKey = $prefix ? "$prefix.$key" : $key; + + // If the value is not an array, it can be used as-is + if (! is_array($value)) { + $preparedRelationships[$fullKey] = $value; + } else { + if (array_values($value) === $value) { + // If the array has a depth of 1, we simply flatten the array + foreach ($value as $subValue) { + $preparedRelationships["$fullKey.$subValue"] = null; + } + } else { + // We now know that the remaining relationships are nested arrays + // so we must prepare them recursively + $preparedRelationships = array_merge($preparedRelationships, $this->prepareLoadMissingRelationships($value, $fullKey)); + } + } + } + + return $preparedRelationships; + } + /** * Load a relationship path if it is not already eager loaded. * diff --git a/tests/Integration/Database/EloquentCollectionLoadMissingTest.php b/tests/Integration/Database/EloquentCollectionLoadMissingTest.php index e95d7aca9b41..e418221f2c63 100644 --- a/tests/Integration/Database/EloquentCollectionLoadMissingTest.php +++ b/tests/Integration/Database/EloquentCollectionLoadMissingTest.php @@ -92,6 +92,77 @@ public function testLoadMissingWithClosure() $this->assertArrayNotHasKey('post_id', $posts[0]->comments[1]->parent->getAttributes()); } + 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]); + } + public function testLoadMissingWithDuplicateRelationName() { $posts = Post::with('comments')->get();