From 3fbd5e7d247f7eabdb6af774f0e6caa763ba2e3d Mon Sep 17 00:00:00 2001 From: Derek MacDonald Date: Mon, 19 Mar 2018 01:52:47 -0400 Subject: [PATCH] Add Eloquent Collection loadMorph() Allow nested relations to be eager loaded for morphTo() relationships of mixed classes. e.g., class ActivityFeed { function parentable() { return $this->morphTo(); } } $collection = ActivityFeed::with('parentable') ->get() ->loadMorph('parentable', [ Event::class => 'calendar', Photo::class => 'tags', Post::class => ['author', 'commentsCount'], ]); or $paginator = ActivityFeed::with('parentable') ->paginate() ->loadMorph('parentable', [ // etc. ]); --- .../Database/Eloquent/Collection.php | 24 ++++++++++++++++ .../Pagination/AbstractPaginator.php | 14 ++++++++++ ...baseEloquentPolymorphicIntegrationTest.php | 28 +++++++++++++++++-- tests/Pagination/PaginatorLoadMorphTest.php | 27 ++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 tests/Pagination/PaginatorLoadMorphTest.php diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index 5e10cd1cde6a..30e8ef0baa69 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -62,6 +62,30 @@ public function load($relations) return $this; } + /** + * Load a set of relationships onto the mixed relationship collection. + * + * @param string $relation + * @param array $relations + * @return $this + */ + public function loadMorph($relation, $relations) + { + $this->pluck($relation) + ->groupBy(function ($model) { + return get_class($model); + }) + ->filter(function ($models, $className) use ($relations) { + return Arr::has($relations, $className); + }) + ->each(function ($models, $className) use ($relations) { + $className::with($relations[$className]) + ->eagerLoadRelations($models->all()); + }); + + return $this; + } + /** * Add an item to the collection. * diff --git a/src/Illuminate/Pagination/AbstractPaginator.php b/src/Illuminate/Pagination/AbstractPaginator.php index 78b2e0743305..2cd926637fe2 100644 --- a/src/Illuminate/Pagination/AbstractPaginator.php +++ b/src/Illuminate/Pagination/AbstractPaginator.php @@ -521,6 +521,20 @@ public function setCollection(Collection $collection) return $this; } + /** + * Load a set of relationships onto the mixed relationship collection. + * + * @param string $relation + * @param array $relations + * @return $this + */ + public function loadMorph($relation, $relations) + { + $this->getCollection()->loadMorph($relation, $relations); + + return $this; + } + /** * Determine if the given item exists. * diff --git a/tests/Database/DatabaseEloquentPolymorphicIntegrationTest.php b/tests/Database/DatabaseEloquentPolymorphicIntegrationTest.php index cfc01ef384b2..b26fcb720e4c 100644 --- a/tests/Database/DatabaseEloquentPolymorphicIntegrationTest.php +++ b/tests/Database/DatabaseEloquentPolymorphicIntegrationTest.php @@ -4,7 +4,6 @@ use PHPUnit\Framework\TestCase; use Illuminate\Database\Connection; -use Illuminate\Database\Query\Builder; use Illuminate\Database\Capsule\Manager as DB; use Illuminate\Database\Eloquent\Model as Eloquent; @@ -119,6 +118,26 @@ public function testItLoadsNestedRelationshipsOnDemand() $this->assertEquals(TestUser::first(), $like->likeable->owner); } + public function testItLoadsNestedMorphRelationshipsOnDemand() + { + $this->seedData(); + + TestPost::first()->likes()->create([]); + + $likes = TestLike::with('likeable.owner')->get()->loadMorph('likeable', [ + TestComment::class => ['commentable'], + TestPost::class => 'comments', + ]); + + $this->assertTrue($likes[0]->relationLoaded('likeable')); + $this->assertTrue($likes[0]->likeable->relationLoaded('owner')); + $this->assertTrue($likes[0]->likeable->relationLoaded('commentable')); + + $this->assertTrue($likes[1]->relationLoaded('likeable')); + $this->assertTrue($likes[1]->likeable->relationLoaded('owner')); + $this->assertTrue($likes[1]->likeable->relationLoaded('comments')); + } + /** * Helpers... */ @@ -144,7 +163,7 @@ protected function connection() /** * Get a schema builder instance. * - * @return Schema\Builder + * @return \Illuminate\Database\Schema\Builder */ protected function schema() { @@ -183,6 +202,11 @@ public function owner() { return $this->belongsTo(TestUser::class, 'user_id'); } + + public function likes() + { + return $this->morphMany(TestLike::class, 'likeable'); + } } /** diff --git a/tests/Pagination/PaginatorLoadMorphTest.php b/tests/Pagination/PaginatorLoadMorphTest.php new file mode 100644 index 000000000000..0fdb069202b6 --- /dev/null +++ b/tests/Pagination/PaginatorLoadMorphTest.php @@ -0,0 +1,27 @@ + 'photos', + 'App\\Company' => ['employees', 'calendars'], + ]; + + $items = Mockery::mock(Collection::class); + $items->shouldReceive('loadMorph')->with('parentable', $relations); + + $p = (new class extends AbstractPaginator { + })->setCollection($items); + + $this->assertSame($p, $p->loadMorph('parentable', $relations)); + } +}