Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify doctrine integration manner #285

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 40 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,50 @@ 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;
}
// The parameter "doctrine.entity_managers" can be missed when not actually using ORM even if DoctrineBundle loaded.
if (!$container->hasParameter('doctrine.entity_managers')) {
return;
}

// Can't support Doctrine/ORM older (< 2.4) version.
$reflection = new \ReflectionClass('Doctrine\ORM\Configuration');
if (!$reflection->hasMethod('setRepositoryFactory')) {
return;
}

$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));

if ('doctrine.orm.entity_manager.abstract' !== $definition->getParent()) {
continue;
$originalRepositoryFactoryRef = null;

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

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;
}
}
121 changes: 0 additions & 121 deletions Generator/RepositoryInjectionGenerator.php

This file was deleted.

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"sensio/framework-extra-bundle": "~2.0|~3.0",
"jms/security-extra-bundle": "~1.0",
"doctrine/doctrine-bundle": "~1.5",
"doctrine/orm": "~2.3",
"doctrine/orm": "~2.4",
"phpcollection/phpcollection": ">=0.2,<0.3-dev",
"symfony/phpunit-bridge": "~3.3",
"phpunit/phpunit": "^4.8.35|^5.4.4|^6.0.0",
Expand Down