-
-
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 a51ee30
Showing
10 changed files
with
301 additions
and
10 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,75 @@ | ||
<?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)); | ||
|
||
// 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 ($container->findTaggedServiceIds(self::CONFIGURATION_TAG) as $id => $tags) { | ||
$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 ($metadataDriverImpl === null) { | ||
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,28 @@ | ||
<?php | ||
|
||
namespace Doctrine\Bundle\DoctrineBundle\Mapping; | ||
|
||
use Doctrine\ORM\Mapping\ClassMetadata; | ||
use Doctrine\ORM\Mapping\ClassMetadataFactory as BaseClassMetadataFactory; | ||
|
||
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,86 @@ | ||
<?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 PHPUnit\Framework\TestCase; | ||
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'); | ||
} | ||
|
||
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; | ||
} | ||
} |
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