Skip to content

Commit

Permalink
Fix doctrine integration
Browse files Browse the repository at this point in the history
  • Loading branch information
issei-m committed Oct 4, 2017
1 parent 1e5edb1 commit 1543171
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 55 deletions.
56 changes: 29 additions & 27 deletions DependencyInjection/Compiler/IntegrationPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;

/**
Expand All @@ -41,36 +40,39 @@ public function process(ContainerBuilder $container)
}
}

/**
* Integrates the DiAwareObjectManager with Doctrine.
*
* This is a bit trickier... mostly because Doctrine uses many factories,
* and we cannot directly inject the EntityManager. We circumvent this
* problem by renaming the original entity manager definition, and then
* placing our definition in its place.
*
* Note that this also currently only supports the ORM, for the ODM flavors
* a similar integration should be possible.
*
* @param ContainerBuilder $container
*/
private function integrateWithDoctrine($container)
private function integrateWithDoctrine(ContainerBuilder $container)
{
foreach ($container->getDefinitions() as $id => $definition) {
if (!$definition instanceof DefinitionDecorator) {
continue;
}
$entityManagerNames = array_keys($container->getParameter('doctrine.entity_managers'));

foreach ($entityManagerNames as $emName) {
// See: https://github.com/doctrine/DoctrineBundle/blob/c9f8cc06153a70433d2c67393f10725959f7bb43/DependencyInjection/DoctrineExtension.php#L384-L385
$ormConfigDef = $container->getDefinition(sprintf('doctrine.orm.%s_configuration', $emName));

$originalRepositoryFactoryRef = null;

foreach ($ormConfigDef->getMethodCalls() as $methodCall) {
list($methodName, $arguments) = $methodCall;

if ('doctrine.orm.entity_manager.abstract' !== $definition->getParent()) {
continue;
if ('setRepositoryFactory' === $methodName) {
$originalRepositoryFactoryRef = $arguments[0];

$ormConfigDef->removeMethodCall($methodName);

break;
}
}

$definition->setPublic(false);
$container->setDefinition($id.'.delegate', $definition);
$container->register($id, $container->getParameter('jms_di_extra.doctrine_integration.entity_manager.class'))
->setFile($container->getParameter('jms_di_extra.doctrine_integration.entity_manager.file'))
->addArgument(new Reference($id.'.delegate'))
->addArgument(new Reference('service_container'));
$replacedRepositoryFactoryId = sprintf('jms_di_extra.doctrine.orm.%s.repository_factory', $emName);

$container->register($replacedRepositoryFactoryId, 'JMS\DiExtraBundle\Doctrine\ORM\ContainerAwareRepositoryFactoryDecorator')
->setPublic(false)
->setArguments(array(
new Reference('service_container'),
$originalRepositoryFactoryRef,
))
;

$ormConfigDef->addMethodCall('setRepositoryFactory', array(new Reference($replacedRepositoryFactoryId)));
}
}
}
28 changes: 0 additions & 28 deletions DependencyInjection/JMSDiExtraExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@

namespace JMS\DiExtraBundle\DependencyInjection;

use CG\Core\DefaultNamingStrategy;
use CG\Proxy\Enhancer;
use JMS\DiExtraBundle\Exception\RuntimeException;
use JMS\DiExtraBundle\Generator\RepositoryInjectionGenerator;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Alias;
Expand Down Expand Up @@ -67,10 +64,6 @@ public function load(array $configs, ContainerBuilder $container)
$this->configureMetadata($config['metadata'], $container, $config['cache_dir'].'/metadata');
$this->configureAutomaticControllerInjections($config, $container);

if ($config['doctrine_integration']) {
$this->generateEntityManagerProxyClass($config, $container);
}

if (PHP_VERSION_ID < 70000) {
$this->addClassesToCompile(array(
'JMS\\DiExtraBundle\\HttpKernel\ControllerResolver',
Expand All @@ -83,27 +76,6 @@ public function blackListControllerFile($filename)
$this->blackListedControllerFiles[] = realpath($filename);
}

private function generateEntityManagerProxyClass(array $config, ContainerBuilder $container)
{
$cacheDir = $container->getParameterBag()->resolveValue($config['cache_dir']);

if (!is_dir($cacheDir.'/doctrine')) {
if (false === @mkdir($cacheDir.'/doctrine', 0777, true)) {
throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir.'/doctrine'));
}
}

$enhancer = new Enhancer($ref = new \ReflectionClass('Doctrine\ORM\EntityManager'), array(), array(new RepositoryInjectionGenerator()));
$uniqid = uniqid(); // We do have to use a non-static id to avoid problems with cache:clear.
if (strtoupper(PHP_OS) == 'CYGWIN') {
$uniqid = preg_replace('/\./', '_', $uniqid); // replace dot; cygwin always generates uniqid's with more_entropy
}
$enhancer->setNamingStrategy(new DefaultNamingStrategy('EntityManager'.$uniqid));
$enhancer->writeClass($file = $cacheDir.'/doctrine/EntityManager_'.$uniqid.'.php');
$container->setParameter('jms_di_extra.doctrine_integration.entity_manager.file', $file);
$container->setParameter('jms_di_extra.doctrine_integration.entity_manager.class', $enhancer->getClassName($ref));
}

private function configureAutomaticControllerInjections(array $config, ContainerBuilder $container)
{
if (!isset($config['automatic_controller_injections'])) {
Expand Down
89 changes: 89 additions & 0 deletions Doctrine/ORM/ContainerAwareRepositoryFactoryDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

namespace JMS\DiExtraBundle\Doctrine\ORM;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
use Doctrine\ORM\Repository\RepositoryFactory;
use Metadata\MetadataFactory;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;

/**
* @author Issei Murasawa <[email protected]>
*/
final class ContainerAwareRepositoryFactoryDecorator implements RepositoryFactory
{
/**
* @var ContainerInterface
*/
private $container;

/**
* @var RepositoryFactory
*/
private $wrappedFactory;

/**
* @var MetadataFactory
*/
private $metadataFactory;

public function __construct(ContainerInterface $container, RepositoryFactory $wrappedFactory = null)
{
$this->container = $container;
$this->wrappedFactory = $wrappedFactory ?: new DefaultRepositoryFactory();
}

/**
* {@inheritdoc}
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName)
{
$repository = $this->wrappedFactory->getRepository($entityManager, $entityName);

if ($repository instanceof ContainerAwareInterface) {
$repository->setContainer($this->container);

return $repository;
}

if (null !== $metadata = $this->getMetadataFactory()->getMetadataForClass(get_class($repository))) {
foreach ($metadata->classMetadata as $classMetadata) {
foreach ($classMetadata->methodCalls as $call) {
list($method, $arguments) = $call;
call_user_func_array(array($repository, $method), $this->prepareArguments($arguments));
}
}
}

return $repository;
}

private function getMetadataFactory()
{
if (!$this->metadataFactory) {
$this->metadataFactory = $this->container->get('jms_di_extra.metadata.metadata_factory');
}

return $this->metadataFactory;
}

private function prepareArguments(array $arguments)
{
$processed = array();
foreach ($arguments as $arg) {
if ($arg instanceof Reference) {
$processed[] = $this->container->get((string) $arg, $arg->getInvalidBehavior());
} elseif ($arg instanceof Parameter) {
$processed[] = $this->container->getParameter((string) $arg);
} else {
$processed[] = $arg;
}
}

return $processed;
}
}

0 comments on commit 1543171

Please sign in to comment.