Skip to content

Commit

Permalink
Make ServiceEntityRepository lazy on Symfony 6.2+
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Dec 30, 2022
1 parent 0421ebc commit 5546ade
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 31 deletions.
62 changes: 62 additions & 0 deletions Repository/LazyServiceEntityRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Repository;

use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use LogicException;
use Symfony\Component\VarExporter\LazyGhostTrait;

use function sprintf;

/**
* Optional EntityRepository base class with a simplified constructor (for autowiring).
*
* To use in your class, inject the "registry" service and call
* the parent constructor. For example:
*
* class YourEntityRepository extends ServiceEntityRepository
* {
* public function __construct(ManagerRegistry $registry)
* {
* parent::__construct($registry, YourEntity::class);
* }
* }
*
* @internal to be renamed ServiceEntityRepository when PHP 8.1 / Symfony 6.2 becomes required
*
* @template T of object
* @template-extends EntityRepository<T>
*/
class LazyServiceEntityRepository extends EntityRepository implements ServiceEntityRepositoryInterface
{
use LazyGhostTrait;

/**
* @param string $entityClass The class name of the entity this repository manages
* @psalm-param class-string<T> $entityClass
*/
public function __construct(ManagerRegistry $registry, string $entityClass)
{
$initializer = function ($instance, $property) use ($registry, $entityClass) {
$manager = $registry->getManagerForClass($entityClass);

if ($manager === null) {
throw new LogicException(sprintf(
'Could not find the entity manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this entity’s metadata.',
$entityClass
));
}

parent::__construct($manager, $manager->getClassMetadata($entityClass));

return $this->$property;
};

self::createLazyGhost([
"\0*\0_em" => $initializer,
"\0*\0_class" => $initializer,
"\0*\0_entityName" => $initializer,
], null, $this);
}
}
68 changes: 38 additions & 30 deletions Repository/ServiceEntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,51 @@
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use LogicException;
use Symfony\Component\VarExporter\LazyGhostTrait;

use function sprintf;
use function trait_exists;

/**
* Optional EntityRepository base class with a simplified constructor (for autowiring).
*
* To use in your class, inject the "registry" service and call
* the parent constructor. For example:
*
* class YourEntityRepository extends ServiceEntityRepository
* {
* public function __construct(ManagerRegistry $registry)
* {
* parent::__construct($registry, YourEntity::class);
* }
* }
*
* @template T of object
* @template-extends EntityRepository<T>
*/
class ServiceEntityRepository extends EntityRepository implements ServiceEntityRepositoryInterface
{
if (trait_exists(LazyGhostTrait::class)) {
class ServiceEntityRepository extends LazyServiceEntityRepository
{
}
} else {
/**
* @param string $entityClass The class name of the entity this repository manages
* @psalm-param class-string<T> $entityClass
* Optional EntityRepository base class with a simplified constructor (for autowiring).
*
* To use in your class, inject the "registry" service and call
* the parent constructor. For example:
*
* class YourEntityRepository extends ServiceEntityRepository
* {
* public function __construct(ManagerRegistry $registry)
* {
* parent::__construct($registry, YourEntity::class);
* }
* }
*
* @template T of object
* @template-extends EntityRepository<T>
*/
public function __construct(ManagerRegistry $registry, string $entityClass)
class ServiceEntityRepository extends EntityRepository implements ServiceEntityRepositoryInterface
{
$manager = $registry->getManagerForClass($entityClass);
/**
* @param string $entityClass The class name of the entity this repository manages
* @psalm-param class-string<T> $entityClass
*/
public function __construct(ManagerRegistry $registry, string $entityClass)
{
$manager = $registry->getManagerForClass($entityClass);

if ($manager === null) {
throw new LogicException(sprintf(
'Could not find the entity manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this entity’s metadata.',
$entityClass
));
}
if ($manager === null) {
throw new LogicException(sprintf(
'Could not find the entity manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this entity’s metadata.',
$entityClass
));
}

parent::__construct($manager, $manager->getClassMetadata($entityClass));
parent::__construct($manager, $manager->getClassMetadata($entityClass));
}
}
}
3 changes: 2 additions & 1 deletion Tests/Repository/ServiceEntityRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function testConstructorThrowsExceptionWhenNoManagerFound(): void
EXCEPTION
);
/** @psalm-suppress UndefinedClass */
new ServiceEntityRepository($registry, TestEntity::class);
$repo = new ServiceEntityRepository($registry, TestEntity::class);
$repo->getClassName();
}
}
1 change: 1 addition & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<!-- We use the "Foo" namespace in unit tests. We are aware that those classes don't exist. -->
<referencedClass name="Foo\*"/>
<referencedClass name="Symfony\Bridge\Doctrine\Attribute\MapEntity"/>
<referencedClass name="Symfony\Component\VarExporter\LazyGhostTrait"/>
<referencedClass name="Symfony\Component\VarExporter\LazyObjectInterface"/>
</errorLevel>
</UndefinedClass>
Expand Down

0 comments on commit 5546ade

Please sign in to comment.