Skip to content

Commit

Permalink
Merge pull request #11035 from kbond/feat/lazy-first
Browse files Browse the repository at this point in the history
Make `PersistentCollection::first()` "extra" lazy
  • Loading branch information
greg0ire authored Oct 21, 2024
2 parents 74155c8 + c9253ef commit 0f11a97
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/en/tutorials/extra-lazy-associations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ can be called without triggering a full load of the collection:
- ``Collection#contains($entity)``
- ``Collection#containsKey($key)``
- ``Collection#count()``
- ``Collection#first()``
- ``Collection#get($key)``
- ``Collection#slice($offset, $length = null)``

Expand Down
14 changes: 14 additions & 0 deletions src/PersistentCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,20 @@ public function __wakeup(): void
$this->em = null;
}

/**
* {@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
57 changes: 57 additions & 0 deletions tests/Tests/ORM/Functional/ExtraLazyCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,63 @@ 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 0f11a97

Please sign in to comment.