diff --git a/lib/Model.php b/lib/Model.php index b20f5762..462cba0c 100644 --- a/lib/Model.php +++ b/lib/Model.php @@ -1687,13 +1687,11 @@ public static function from(string $from): Relation } /** - * @param string|array $include - * * @return Relation */ - public static function include(string|array $include): Relation + public static function includes(): Relation { - return static::Relation()->include($include); + return static::Relation()->includes(...func_get_args()); } /** diff --git a/lib/Relation.php b/lib/Relation.php index afab6b7d..1e5d7ce1 100644 --- a/lib/Relation.php +++ b/lib/Relation.php @@ -247,13 +247,39 @@ public function from(string $from): Relation } /** - * @param string|array $include + * The purpose of includes is to solve N+1 problems in relational situations. + * Let's say you have an Author, and authors write many Books. You would + * ordinarily need to execute one query (+1) to retrieve the Author, + * then one query for each of his books (N). + * + * You can avoid this problem by specifying relationships to be included in the + * result set. For example: + * + * $users = User::includes('address'); + * foreach($users as $user) { + * $user->address->city + * } + * + * ...allows you to access the address attribute of the User model without + * firing an additional query. This will often result in a performance + * improvement over a simple join. You can also specify multiple + * relationships, like this: + * + * $users = User::includes('address', 'friends'); + * + * Loading nested relationships is possible using a Hash: + * + * $users = User::includes( + * 'address', + * 'friends' => ['address', 'followers'] + * ) * * @return Relation */ - public function include(string|array $include): Relation + public function includes(): Relation { - $this->options['include'] = $include; + $includes = static::toSingleArg(...func_get_args()); + $this->options['include'] = $includes; return $this; } diff --git a/test/ActiveRecordIncludesTest.php b/test/ActiveRecordIncludesTest.php new file mode 100644 index 00000000..52eec592 --- /dev/null +++ b/test/ActiveRecordIncludesTest.php @@ -0,0 +1,42 @@ +first(); + + $this->assertEquals('Monday Night Music Club feat. The Shivers', $venue->events[0]->title); + } + + public function testChainSimpleString(): void + { + $venue = Venue::includes('events') + ->includes('hosts') + ->first(); + + $this->assertEquals('Monday Night Music Club feat. The Shivers', $venue->events[0]->title); + $this->assertEquals('David Letterman', $venue->hosts[0]->name); + } + + public function testArrayOfSimpleStrings(): void + { + $venue = Venue::includes(['events', 'hosts']) + ->first(); + $this->assertEquals('Monday Night Music Club feat. The Shivers', $venue->events[0]->title); + $this->assertEquals('David Letterman', $venue->hosts[0]->name); + } + + public function testListOfSimpleStrings(): void + { + $venue = Venue::includes('events', 'hosts') + ->first(); + $this->assertEquals('Monday Night Music Club feat. The Shivers', $venue->events[0]->title); + $this->assertEquals('David Letterman', $venue->hosts[0]->name); + } +} diff --git a/test/HasManyThroughTest.php b/test/HasManyThroughTest.php index c28e2914..119d09a7 100644 --- a/test/HasManyThroughTest.php +++ b/test/HasManyThroughTest.php @@ -27,7 +27,7 @@ public function testHasManyThrough() public function testHasManyThroughInclude() { - $user = User::include([ + $user = User::includes([ 'user_newsletters' ])->find(1); @@ -37,16 +37,16 @@ public function testHasManyThroughInclude() public function testHasManyThroughIncludeEager() { - $venue = Venue::include('events')->find(1); + $venue = Venue::includes('events')->find(1); $this->assertEquals(1, $venue->events[0]->id); - $venue = Venue::include('hosts')->find(1); + $venue = Venue::includes('hosts')->find(1); $this->assertEquals(1, $venue->hosts[0]->id); } public function testHasManyThoughIncludeEagerWithNamespace() { - $user = User::include('newsletters')->find(1); + $user = User::includes('newsletters')->find(1); $this->assertEquals(1, $user->id); $this->assertEquals(1, $user->newsletters[0]->id); diff --git a/test/RelationshipTest.php b/test/RelationshipTest.php index 4b24d63e..ddb93643 100644 --- a/test/RelationshipTest.php +++ b/test/RelationshipTest.php @@ -124,7 +124,7 @@ public function testHasManyBasic() public function testEagerLoadingThreeLevelsDeep() { /* Before fix Undefined offset: 0 */ - $venue = Venue::include([ + $venue = Venue::includes([ 'events'=>[ 'host'=>[ 'events' @@ -706,7 +706,7 @@ public function testEagerLoadingRespectsAssociationOptions() ] ] ]; - $venues = Venue::include('events')->find([2, 6]); + $venues = Venue::includes('events')->find([2, 6]); $this->assert_sql_has('WHERE (length(title) = ?) AND (venue_id IN(?,?)) ORDER BY id asc', ActiveRecord\Table::load(Event::class)->last_sql); $this->assertEquals(1, count($venues[0]->events)); @@ -724,13 +724,13 @@ public function testEagerLoadingWithThrough() 'order' => 'hosts.id asc' ] ]; - $venues = Venue::include('hosts')->find([2, 6]); + $venues = Venue::includes('hosts')->find([2, 6]); $this->assertEquals(2, count($venues)); } public function testEagerLoadingHasManyX() { - $venues = Venue::include('events')->find([2, 6]); + $venues = Venue::includes('events')->find([2, 6]); $this->assert_sql_has('WHERE venue_id IN(?,?)', ActiveRecord\Table::load(Event::class)->last_sql); foreach ($venues[0]->events as $event) { @@ -742,7 +742,7 @@ public function testEagerLoadingHasManyX() public function testEagerLoadingHasManyWithNoRelatedRows() { - $venues = Venue::include('events')->find([7, 8]); + $venues = Venue::includes('events')->find([7, 8]); foreach ($venues as $v) { $this->assertTrue(empty($v->events)); @@ -758,7 +758,7 @@ public function testEagerLoadingHasManyArrayOfIncludes() 'books' => true, 'awesome_people' => true ]; - $authors = Author::include(['books', 'awesome_people']) + $authors = Author::includes(['books', 'awesome_people']) ->find([1, 2]); $assocs = ['books', 'awesome_people']; @@ -782,7 +782,7 @@ public function testEagerLoadingHasManyArrayOfIncludes() public function testEagerLoadingHasManyNested() { - $venues = Venue::include(['events' => ['host']]) + $venues = Venue::includes(['events' => ['host']]) ->find([1, 2]); $this->assertEquals(2, count($venues)); @@ -803,7 +803,7 @@ public function testEagerLoadingHasManyNested() public function testEagerLoadingBelongsTo() { - $events = Event::include('venue') + $events = Event::includes('venue') ->find([1, 2, 3, 5, 7]); foreach ($events as $event) { @@ -815,7 +815,7 @@ public function testEagerLoadingBelongsTo() public function testEagerLoadingBelongsToArrayOfIncludes() { - $events = Event::include(['venue', 'host'])->find([1, 2, 3, 5, 7]); + $events = Event::includes(['venue', 'host'])->find([1, 2, 3, 5, 7]); foreach ($events as $event) { $this->assertEquals($event->venue_id, $event->venue->id); @@ -836,7 +836,7 @@ public function testEagerLoadingBelongsToNested() 'author' => true ]; - $books = Book::include([ + $books = Book::includes([ 'author' => [ 'awesome_people' ] @@ -857,7 +857,7 @@ public function testEagerLoadingBelongsToWithNoRelatedRows() $e1 = Event::create(['venue_id' => 200, 'host_id' => 200, 'title' => 'blah', 'type' => 'Music']); $e2 = Event::create(['venue_id' => 200, 'host_id' => 200, 'title' => 'blah2', 'type' => 'Music']); - $events = Event::include('venue') + $events = Event::includes('venue') ->find([$e1->id, $e2->id]); foreach ($events as $e) { @@ -870,7 +870,7 @@ public function testEagerLoadingBelongsToWithNoRelatedRows() public function testEagerLoadingClonesRelatedObjects() { - $events = Event::include('venue') + $events = Event::includes('venue') ->find([2, 3]); $venue = $events[0]->venue; @@ -883,7 +883,7 @@ public function testEagerLoadingClonesRelatedObjects() public function testEagerLoadingClonesNestedRelatedObjects() { - $venues = Venue::include(['events' => ['host']]) + $venues = Venue::includes(['events' => ['host']]) ->find([1, 2, 6, 9]); $unchanged_host = $venues[2]->events[0]->host; diff --git a/test/phpstan/ModelToRelation.php b/test/phpstan/ModelToRelation.php index 015680d1..49fc26d8 100644 --- a/test/phpstan/ModelToRelation.php +++ b/test/phpstan/ModelToRelation.php @@ -36,8 +36,8 @@ public function where(): Relation /** * @return Relation */ - public function include(): Relation + public function includes(): Relation { - return Book::include([]); + return Book::includes([]); } }