Skip to content

Commit

Permalink
feat: make PersistentCollection::first() "extra" lazy
Browse files Browse the repository at this point in the history
  • Loading branch information
kbond committed Nov 2, 2023
1 parent 16028e4 commit 40c633d
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
14 changes: 14 additions & 0 deletions lib/Doctrine/ORM/PersistentCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,20 @@ public function __sleep(): array
return ['collection', 'initialized'];
}

/**
* {@inheritDoc}
*/
public function first()
{
if (! $this->initialized && ! $this->isDirty && $this->getMapping()['fetch'] === ClassMetadata::FETCH_EXTRA_LAZY) {
$persister = $this->getUnitOfWork()->getCollectionPersister($this->getMapping());

return array_values($persister->slice($this, 0, 1))[0] ?? false;
}

return parent::first();
}

/**
* Extracts a slice of $length elements starting at position $offset from the Collection.
*
Expand Down
59 changes: 59 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,65 @@ public function testCountOneToManyJoinedInheritance(): void
self::assertCount(2, $otherClass->childClasses);
}

/**
* @group non-cacheable
*/
public function testFirstWhenInitialized(): void
{
$user = $this->_em->find(CmsUser::class, $this->userId);
$this->getQueryLog()->reset()->enable();
$user->groups->toArray();

self::assertTrue($user->groups->isInitialized());
self::assertInstanceOf(CmsGroup::class, $user->groups->first());
$this->assertQueryCount(1, 'Should only execute one query to initialize collection, no extra query for first().');
}

public function testFirstOnEmptyCollectionWhenInitialized(): void
{
foreach ($this->_em->getRepository(CmsGroup::class)->findAll() as $group) {
$this->_em->remove($group);
}

$this->_em->flush();

$user = $this->_em->find(CmsUser::class, $this->userId);
$this->getQueryLog()->reset()->enable();
$user->groups->toArray();

self::assertTrue($user->groups->isInitialized());
self::assertFalse($user->groups->first());
$this->assertQueryCount(1, 'Should only execute one query to initialize collection, no extra query for first().');
}

public function testFirstWhenNotInitialized(): void
{
$user = $this->_em->find(CmsUser::class, $this->userId);
$this->getQueryLog()->reset()->enable();

self::assertFalse($user->groups->isInitialized());
self::assertInstanceOf(CmsGroup::class, $user->groups->first());
self::assertFalse($user->groups->isInitialized());
$this->assertQueryCount(1, 'Should only execute one query for first().');
}

public function testFirstOnEmptyCollectionWhenNotInitialized(): void
{
foreach ($this->_em->getRepository(CmsGroup::class)->findAll() as $group) {
$this->_em->remove($group);
}

$this->_em->flush();

$user = $this->_em->find(CmsUser::class, $this->userId);
$this->getQueryLog()->reset()->enable();

self::assertFalse($user->groups->isInitialized());
self::assertFalse($user->groups->first());
self::assertFalse($user->groups->isInitialized());
$this->assertQueryCount(1, 'Should only execute one query for first().');
}

/** @group DDC-546 */
public function testFullSlice(): void
{
Expand Down

0 comments on commit 40c633d

Please sign in to comment.