-
-
Notifications
You must be signed in to change notification settings - Fork 455
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable
@CustomIdGenerator()
to reference services tagged as "doctri…
…ne.id_generator"
- Loading branch information
1 parent
91f0139
commit 8f92b94
Showing
9 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler; | ||
|
||
use Doctrine\Bundle\DoctrineBundle\Mapping\ClassMetadataFactory; | ||
use Doctrine\Bundle\DoctrineBundle\Mapping\MappingDriver; | ||
use Doctrine\ORM\Mapping\ClassMetadataFactory as ORMClassMetadataFactory; | ||
use Symfony\Component\DependencyInjection\Alias; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
final class IdGeneratorPass implements CompilerPassInterface | ||
{ | ||
const ID_GENERATOR_TAG = 'doctrine.id_generator'; | ||
const CONFIGURATION_TAG = 'doctrine.orm.configuration'; | ||
|
||
public function process(ContainerBuilder $container): void | ||
{ | ||
$generatorIds = array_keys($container->findTaggedServiceIds(self::ID_GENERATOR_TAG)); | ||
$configurationIds = array_keys($container->findTaggedServiceIds(self::CONFIGURATION_TAG)); | ||
|
||
foreach ($configurationIds as $id) { | ||
$container->getDefinition($id)->clearTag(self::CONFIGURATION_TAG); | ||
} | ||
|
||
// when ORM is not enabled | ||
if (! $container->hasDefinition('doctrine.orm.configuration') || ! $generatorIds) { | ||
return; | ||
} | ||
|
||
$generatorRefs = array_map(static function ($id) { | ||
return new Reference($id); | ||
}, $generatorIds); | ||
|
||
$ref = ServiceLocatorTagPass::register($container, array_combine($generatorIds, $generatorRefs)); | ||
$container->setAlias('doctrine.id_generator_locator', new Alias((string) $ref, false)); | ||
|
||
foreach ($configurationIds as $id) { | ||
$configurationDef = $container->getDefinition($id); | ||
$methodCalls = $configurationDef->getMethodCalls(); | ||
$metadataDriverImpl = null; | ||
|
||
foreach ($methodCalls as $i => [$method, $arguments]) { | ||
if ($method === 'setMetadataDriverImpl') { | ||
$metadataDriverImpl = (string) $arguments[0]; | ||
} | ||
|
||
if ($method !== 'setClassMetadataFactoryName') { | ||
continue; | ||
} | ||
|
||
if ($arguments[0] !== ORMClassMetadataFactory::class && $arguments[0] !== ClassMetadataFactory::class) { | ||
$class = $container->getReflectionClass($arguments[0]); | ||
|
||
if ($class && $class->isSubclassOf(ClassMetadataFactory::class)) { | ||
break; | ||
} | ||
|
||
continue 2; | ||
} | ||
|
||
$methodCalls[$i] = ['setClassMetadataFactoryName', [ClassMetadataFactory::class]]; | ||
} | ||
|
||
if (null === $metadataDriverImpl) { | ||
continue; | ||
} | ||
|
||
$configurationDef->setMethodCalls($methodCalls); | ||
$container->register('.' . $metadataDriverImpl, MappingDriver::class) | ||
->setDecoratedService($metadataDriverImpl) | ||
->setArguments([ | ||
new Reference(sprintf('.%s.inner', $metadataDriverImpl)), | ||
new Reference('doctrine.id_generator_locator'), | ||
]); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\Mapping; | ||
|
||
use Doctrine\ORM\EntityManagerInterface; | ||
use Doctrine\ORM\Mapping\ClassMetadata; | ||
use Doctrine\ORM\Mapping\ClassMetadataFactory as BaseClassMetadataFactory; | ||
use Doctrine\Persistence\Mapping\Driver\MappingDriver as MappingDriverInterface; | ||
|
||
class ClassMetadataFactory extends BaseClassMetadataFactory | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
*/ | ||
protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents): void | ||
{ | ||
parent::doLoadMetadata($class, $parent, $rootEntityFound, $nonSuperclassParents); | ||
|
||
$customGeneratorDefinition = $class->customGeneratorDefinition; | ||
|
||
if (! isset($customGeneratorDefinition['instance'])) { | ||
return; | ||
} | ||
|
||
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_CUSTOM); | ||
$class->setIdGenerator($customGeneratorDefinition['instance']); | ||
unset($customGeneratorDefinition['instance']); | ||
$class->setCustomGeneratorDefinition($customGeneratorDefinition); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\Mapping; | ||
|
||
use Doctrine\ORM\Mapping\ClassMetadataInfo; | ||
use Doctrine\Persistence\Mapping\ClassMetadata; | ||
use Doctrine\Persistence\Mapping\Driver\MappingDriver as MappingDriverInterface; | ||
use Psr\Container\ContainerInterface; | ||
|
||
class MappingDriver implements MappingDriverInterface | ||
{ | ||
/** @var MappingDriverInterface */ | ||
private $driver; | ||
|
||
/** @var ContainerInterface */ | ||
private $idGeneratorLocator; | ||
|
||
public function __construct(MappingDriverInterface $driver, ContainerInterface $idGeneratorLocator) | ||
{ | ||
$this->driver = $driver; | ||
$this->idGeneratorLocator = $idGeneratorLocator; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function getAllClassNames() | ||
{ | ||
return $this->driver->getAllClassNames(); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function isTransient($className): bool | ||
{ | ||
return $this->driver->isTransient($className); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function loadMetadataForClass($className, ClassMetadata $metadata): void | ||
{ | ||
$this->driver->loadMetadataForClass($className, $metadata); | ||
|
||
if ( | ||
$metadata->generatorType !== ClassMetadataInfo::GENERATOR_TYPE_CUSTOM | ||
|| ! isset($metadata->customGeneratorDefinition['class']) | ||
|| ! $this->idGeneratorLocator->has($metadata->customGeneratorDefinition['class']) | ||
) { | ||
return; | ||
} | ||
|
||
$idGenerator = $this->idGeneratorLocator->get($metadata->customGeneratorDefinition['class']); | ||
$metadata->setCustomGeneratorDefinition(['instance' => $idGenerator] + $metadata->customGeneratorDefinition); | ||
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_NONE); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\Tests; | ||
|
||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\IdGeneratorPass; | ||
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension; | ||
use Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\CustomIdGenerator; | ||
use Doctrine\Common\Annotations\AnnotationReader; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
use Fixtures\Bundles\AnnotationsBundle\AnnotationsBundle; | ||
use Fixtures\Bundles\AnnotationsBundle\Entity\TestCustomIdGeneratorEntity; | ||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; | ||
|
||
class CustomIdGeneratorTest extends TestCase | ||
{ | ||
public static function setUpBeforeClass(): void | ||
{ | ||
if (interface_exists(EntityManagerInterface::class)) { | ||
return; | ||
} | ||
|
||
self::markTestSkipped('This test requires ORM'); | ||
} | ||
|
||
/** | ||
* https://github.com/doctrine/orm/pull/7953 needed, otherwise ORM classes we define services for trigger deprecations | ||
* | ||
* @group legacy | ||
*/ | ||
public function testRepositoryServiceWiring(): void | ||
{ | ||
$container = new ContainerBuilder(new ParameterBag([ | ||
'kernel.debug' => false, | ||
'kernel.bundles' => ['AnnotationsBundle' => AnnotationsBundle::class], | ||
'kernel.cache_dir' => sys_get_temp_dir(), | ||
'kernel.environment' => 'test', | ||
'kernel.runtime_environment' => '%%env(default:kernel.environment:APP_RUNTIME_ENV)%%', | ||
'kernel.build_dir' => __DIR__ . '/../../../../', // src dir | ||
'kernel.root_dir' => __DIR__ . '/../../../../', // src dir | ||
'kernel.project_dir' => __DIR__ . '/../../../../', // src dir | ||
'kernel.bundles_metadata' => [], | ||
'kernel.charset' => 'UTF-8', | ||
'kernel.container_class' => ContainerBuilder::class, | ||
'kernel.secret' => 'test', | ||
'container.build_id' => uniqid(), | ||
'env(base64:default::SYMFONY_DECRYPTION_SECRET)' => 'foo', | ||
])); | ||
$container->set('annotation_reader', new AnnotationReader()); | ||
|
||
$extension = new FrameworkExtension(); | ||
$container->registerExtension($extension); | ||
$extension->load(['framework' => []], $container); | ||
|
||
$extension = new DoctrineExtension(); | ||
$container->registerExtension($extension); | ||
$extension->load([ | ||
[ | ||
'dbal' => [ | ||
'driver' => 'pdo_sqlite', | ||
'charset' => 'UTF8', | ||
], | ||
'orm' => [ | ||
'mappings' => [ | ||
'AnnotationsBundle' => [ | ||
'type' => 'annotation', | ||
'dir' => __DIR__ . '/DependencyInjection/Fixtures/Bundles/AnnotationsBundle/Entity', | ||
'prefix' => 'Fixtures\Bundles\AnnotationsBundle\Entity', | ||
], | ||
], | ||
], | ||
], | ||
], $container); | ||
|
||
$def = $container->register('my_id_generator', CustomIdGenerator::class) | ||
->setPublic(false); | ||
|
||
$def->setAutoconfigured(true); | ||
|
||
$container->addCompilerPass(new IdGeneratorPass()); | ||
$container->compile(); | ||
|
||
$em = $container->get('doctrine.orm.default_entity_manager'); | ||
assert($em instanceof EntityManagerInterface); | ||
|
||
$metadata = $em->getClassMetadata(TestCustomIdGeneratorEntity::class); | ||
$this->assertInstanceOf(CustomIdGenerator::class, $metadata->idGenerator); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...ndencyInjection/Fixtures/Bundles/AnnotationsBundle/Entity/TestCustomIdGeneratorEntity.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
namespace Fixtures\Bundles\AnnotationsBundle\Entity; | ||
|
||
use Doctrine\ORM\Mapping as ORM; | ||
|
||
/** | ||
* @ORM\Entity | ||
*/ | ||
class TestCustomIdGeneratorEntity | ||
{ | ||
/** | ||
* @ORM\Id | ||
* @ORM\GeneratedValue(strategy="CUSTOM") | ||
* @ORM\CustomIdGenerator("my_id_generator") | ||
* @ORM\Column(type="integer") | ||
* | ||
* @var int | ||
*/ | ||
public $id; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures; | ||
|
||
use Doctrine\ORM\EntityManager; | ||
use Doctrine\ORM\Id\AbstractIdGenerator; | ||
|
||
class CustomIdGenerator extends AbstractIdGenerator | ||
{ | ||
public function generate(EntityManager $em, $entity) | ||
{ | ||
return 42; | ||
} | ||
} |