From df2a02e57a6ba63c456e07e49496448740e3ed96 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Wed, 19 Jul 2023 17:22:58 +1200 Subject: [PATCH] FIX Trigger eagerloading for first() and last() --- src/ORM/DataList.php | 19 +++++++++++++++---- tests/php/ORM/DataListEagerLoadingTest.php | 19 +++++++++++++++++++ tests/php/ORM/DataListTest.php | 22 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/ORM/DataList.php b/src/ORM/DataList.php index 4b1598877be..01c4865794d 100644 --- a/src/ORM/DataList.php +++ b/src/ORM/DataList.php @@ -1408,8 +1408,13 @@ public function sum($fieldName) */ public function first() { - foreach ($this->dataQuery->firstRow()->execute() as $row) { - return $this->createDataObject($row); + // We need to trigger eager loading by iterating over the list, rather than just fetching + // the first row from the dataQuery. + // This limit and offset logic mimics that $this->dataQuery->firstRow() would ultimately do. + $limitOffset = $this->dataQuery->query()->getLimit() ?? []; + $offset = array_key_exists('start', $limitOffset) ? $limitOffset['start'] : 0; + foreach ($this->limit(1, $offset) as $record) { + return $record; } return null; } @@ -1423,8 +1428,14 @@ public function first() */ public function last() { - foreach ($this->dataQuery->lastRow()->execute() as $row) { - return $this->createDataObject($row); + // We need to trigger eager loading by iterating over the list, rather than just fetching + // the last row from the dataQuery. + // This limit and offset logic mimics that $this->dataQuery->lastRow() would ultimately do. + $limitOffset = $this->dataQuery->query()->getLimit() ?? []; + $offset = array_key_exists('start', $limitOffset) ? $limitOffset['start'] : 0; + $index = max($this->count() + $offset - 1, 0); + foreach ($this->limit(1, $index) as $record) { + return $record; } return null; } diff --git a/tests/php/ORM/DataListEagerLoadingTest.php b/tests/php/ORM/DataListEagerLoadingTest.php index 256daec6918..380f86c29e2 100644 --- a/tests/php/ORM/DataListEagerLoadingTest.php +++ b/tests/php/ORM/DataListEagerLoadingTest.php @@ -4,6 +4,7 @@ use InvalidArgumentException; use SilverStripe\Dev\SapphireTest; +use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DB; @@ -1115,4 +1116,22 @@ public function provideEagerLoadingEmptyRelationLists(): array ], ]; } + + public function testFirstHasEagerloadedRelation() + { + $record = EagerLoadObject::create(['Title' => 'My obj']); + $record->write(); + $record->HasManyEagerLoadObjects()->add(HasManyEagerLoadObject::create(['Title' => 'My related obj'])); + $obj = EagerLoadObject::get()->eagerLoad('HasManyEagerLoadObjects')->first(); + $this->assertInstanceOf(ArrayList::class, $obj->HasManyEagerLoadObjects()); + } + + public function testLastHasEagerloadedRelation() + { + $record = EagerLoadObject::create(['Title' => 'My obj']); + $record->write(); + $record->HasManyEagerLoadObjects()->add(HasManyEagerLoadObject::create(['Title' => 'My related obj'])); + $obj = EagerLoadObject::get()->eagerLoad('HasManyEagerLoadObjects')->last(); + $this->assertInstanceOf(ArrayList::class, $obj->HasManyEagerLoadObjects()); + } } diff --git a/tests/php/ORM/DataListTest.php b/tests/php/ORM/DataListTest.php index 20ded987eaf..5d663d14853 100755 --- a/tests/php/ORM/DataListTest.php +++ b/tests/php/ORM/DataListTest.php @@ -2081,6 +2081,28 @@ public function testColumnFromRelatedTable() ], $productTitles); } + public function testFirst() + { + $list = Sortable::get()->sort('Sort'); + $this->assertGreaterThanOrEqual( + 3, + $list->count(), + 'We must have at least 3 items for this test to be valid' + ); + $this->assertSame('Steve', $list->first()->Name); + } + + public function testLast() + { + $list = Sortable::get()->sort('Sort'); + $this->assertGreaterThanOrEqual( + 3, + $list->count(), + 'We must have at least 3 items for this test to be valid' + ); + $this->assertSame('John', $list->last()->Name); + } + public function testChunkedFetch() { $expectedIDs = Team::get()->map('ID', 'ID')->toArray();