Hello,
With the following services configuration we get this exception :
ORMException: It's a requirement to specify a Metadata Driver and pass it to Doctrine\ORM\Configuration::setMetadataDriverImpl().
_Services configuration_
services:
acme.invoice.listener:
class: %acme.invoice.listener.class%
arguments:
- "@acme.invoice.manager"
tags:
- { name: doctrine.orm.entity_listener }
acme.invoice.manager:
class: %acme.invoice.manager.class%
arguments:
- "@doctrine.orm.entity_manager"
I think that DoctrineBundle get all services related to my listener before Doctrine Initialization then the Entity Manager is not yet fully configure. I try to use lazy service but I have the same issue.
Hope this report can help you to make DoctrineBundle more awesome ;)
This would be a circular reference anyway, as the listener is injected in the entity manager. Btw, it is weird that the circular reference is not detected before this exception is thrown by Doctrine.
Yep, and the message is not pretty clear then if you have a complex project with many bindles and services it's not easy to find the issue.
Hope this report could help other people in the future.
@mehdi-ghezal How did you solve the problem?
We passed the _service_container_ to the listener.
It's not the best solution but we don't have time to find a better one.
Maybe you can inject your manager in the repository and after that in listener get the repository with doctrine entity manager and after all get your manager.
It should possible to use lazy services if you are using SF23 or higher.
http://symfony.com/doc/current/components/dependency_injection/lazy_services.html
Closing
no, it is impossible with "lazy" services... the lazy classname dosnt match the listener-classname, so it will always try to create a new object:
i have the circulate-DI-injecten problem too, but just with the doctrine-bundle 1.3 implementation. with this style, there is no problem: egeloen.fr
maybe we can change it ?
A service-locator based listener should not base itself on class names, but on service names. I don't get what the problem here is...
Right now we don't have any other solution that injecting the @service_container to suppress this error.
Basically we use a combination of @egeloen solution and injecting the service container.
It's a shame because all our factories are defined as services and it's hard to inject them know since they often cause this circular error.
Our full solution:
app/config/config.yml
# Doctrine Configuration
doctrine:
orm:
auto_generate_proxy_classes: %kernel.debug%
entity_managers:
default:
auto_mapping: true
entity_listener_resolver: acme.doctrine.entity_listener_resolver
AcmeBundle/Resources/config/services.yml
acme.doctrine.entity_listener_resolver:
class: Acme\AcmeBundle\Doctrine\EntityListenerResolver
arguments: [ '@service_container' ]
Acme/AcmeBundle/Doctrine/EntityListenerResolver
<?php
namespace Acme\AcmeBundle\Doctrine;
use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Class EntityListenerResolver
*/
class EntityListenerResolver extends DefaultEntityListenerResolver
{
private $container;
private $mapping;
/**
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
$this->mapping = array();
}
/**
* @param string $className
* @param string $service
*/
public function addMapping($className, $service)
{
$this->mapping[$className] = $service;
}
/**
* @param string $className
*
* @return object
*/
public function resolve($className)
{
if (isset($this->mapping[$className]) && $this->container->has($this->mapping[$className])) {
return $this->container->get($this->mapping[$className]);
}
return parent::resolve($className);
}
}
Acme/AcmeBundle/DependencyInjection/Compiler/DoctrineEntityListenerPass.php
<?php
namespace Acme\AcmeBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Class DoctrineEntityListenerPass
*/
class DoctrineEntityListenerPass implements CompilerPassInterface
{
/**
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('acme.doctrine.entity_listener_resolver');
$services = $container->findTaggedServiceIds('doctrine.orm.entity_listener');
foreach ($services as $service => $attributes) {
$definition->addMethodCall(
'addMapping',
array($container->getDefinition($service)->getClass(), $service)
);
}
}
}
* And to actually use an Entity Listener as a service *
acme.user_listener:
class: Acme\AcmeBundle\Listener\Entity\UserListener
arguments: [ '@service_container' ]
tags:
- { name: doctrine.orm.entity_listener, event: postLoad, method: onPostLoad }
Rinse and repeat for all other entity listeners.
and now ? is there any plan to change the current way ?
+1 for a solution
@caramba1337 My solution above works but you will need to inject the service_container itself (shrugs)
@sverraest yes thank you i solved it in this way. But injecting the whole service container should only be a temporary solution.
I am running into the same problem. Has this not been fixed yet?
+1
Still waiting for proper solution...
Then, how to use it, help me, [email protected]
+1
You can make the listener lazy, fwiw. If you do that, then you simply get rid of the problem.
@kimhemsoe would that satisfy this issue enough to close this?
@Ocramius making listener lazy isn't working for me.
+1
+!
@Ocramius It doesn't work...
Needs test case or example scenario.
There is an open PR which is trying to fix this issue but it need some love.
https://github.com/doctrine/DoctrineBundle/pull/443
On top that i am like 99% sure you can fix this with lazy services, if you make the right services lazy, ex not the listener but the listener's arguments.
@kimhemsoe I marked the listener's argument as lazy, but I still get the error message :-(
I followed the steps from here: https://symfony.com/doc/2.8/service_container/lazy_services.html
How can I verify that the service is really lazy?
Lazy service helped but this issue costed me quite some time to figure out what's going on and to get to this github page.
I hope this gets improved some day.
the PR https://github.com/doctrine/DoctrineBundle/pull/443 has been merged
For me it works like a charm. I think the issue can be closed.
@ThomasLandauer now just add lazy: true in your listener tags :
tags:
- { name: doctrine.orm.entity_listener, lazy: true }
closed by #433
Had the same issue. No need to tag as lazy for me. Just require symfony/proxy-manager-bridge and the entity managers services will be directly lazy loaded.
Issue solved with that. :+1:
Ref: https://symfony.com/doc/current/service_container/lazy_services.html
Most helpful comment
the PR https://github.com/doctrine/DoctrineBundle/pull/443 has been merged
For me it works like a charm. I think the issue can be closed.
@ThomasLandauer now just add
lazy: truein your listener tags :