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

Autowire (dependency injection) not working for an entity listener. #25003

Closed
heigold1 opened this issue Nov 16, 2017 · 13 comments
Closed

Autowire (dependency injection) not working for an entity listener. #25003

heigold1 opened this issue Nov 16, 2017 · 13 comments

Comments

@heigold1
Copy link

Q A
Bug report? yes
Feature request? no
BC Break report? no
RFC? no
Symfony version 3.3.10

Hi,

The autowiring is not working for a basic file entity listener.

I have a File entity class which uses annotations to specify the listener, like:

/**

  • @Orm\Entity(repositoryClass="root\CoreBundle\Repository\FileRepository")
  • @Orm\EntityListeners({"root\CoreBundle\EventListener\Entity\FileEntityListener"})
  • @Orm\Table(

The FileEntityListener class starts off as follows:

class FileEntityListener
{
private $encoderFactory;
private $logger;

public function __construct(FilesystemMap $filesystemMap, LoggerInterface $logger)
{
    $this->setFilesystemMap($filesystemMap);
    $this->logger = $logger;
}

When the listener kicks in, I get an error for the constructor, saying:
Type error: Too few arguments to function Epcvip\CoreBundle\EventListener\Entity\FileEntityListener::__construct(), 0 passed in /var/www/html/accounting/vendor/doctrine/doctrine-bundle/Mapping/ContainerAwareEntityListenerResolver.php on line 83 and exactly 2 expected

The bundle is autowired and yet the dependencies are not being injected.

Would anyone know why this isn't working? Maybe a slight configuration step I am missing?

Brent.
symfony_issue_auto_wire_file_listener

@ro0NL
Copy link
Contributor

ro0NL commented Nov 17, 2017

The bundle is autowired

What about root\CoreBundle\EventListener\Entity\FileEntityListener? Did you configure a service definition for it?

See https://symfony.com/doc/current/bundles/DoctrineBundle/entity-listeners.html

@chalasr
Copy link
Member

chalasr commented Nov 17, 2017

Closing as this seems to be a support issue and we use Github issues for feature requests and bug reports only. Please use our support platforms instead.

@chalasr chalasr closed this as completed Nov 17, 2017
@ThomasLandauer
Copy link
Contributor

I can confirm the issue with Symfony 4.1. I see it as a bug too, cause with services being autowired, I'd expect dependency injection to work just like @heigold1 showed above.

Anyway, here's the solution for now: https://stackoverflow.com/a/49836264/1668200 or https://symfony.com/doc/current/bundles/DoctrineBundle/entity-listeners.html

@stof
Copy link
Member

stof commented Aug 16, 2018

Autowiring works for services. If your entity listener is not registered as a service, the DI component cannot autowire, as it does not know about it (and then, instantiating the class will be performed by Doctrine itself)

@ThomasLandauer
Copy link
Contributor

Well, but with Symfony 4's default configuration, everything should be a service automatically, shouldn't it? That's the purpose of resource: '../src/*' in services.yaml

@xabbuh
Copy link
Member

xabbuh commented Aug 16, 2018

@ThomasLandauer But you still need to tag the service yourself with the doctrine.orm.entity_listener tag. If you don't do that, Doctrine will not pick up your service.

@ThomasLandauer
Copy link
Contributor

I created a PR for the docs at doctrine/DoctrineBundle#839
Please check if it's right.

When I register the listener in the entity (@ORM\EntityListeners({"..."})), it works even without the tag in services.yaml. So I still don't understand why it's not being autowired in this case.

@xabbuh
Copy link
Member

xabbuh commented Aug 16, 2018

What do you mean with "it works"? Does your event listener have mandatory constructor arguments? If not, the class can just be instantiated by Doctrine IIRC.

@ThomasLandauer
Copy link
Contributor

I mean it works as listener; not as service. I don't have a constructor.

You mean, if I tag it as a service, Symfony instantiates it? And later, when Doctrine needs it, everything is already there? Is that the way dependency injection works?

So it can't work like @heigold1 and me expected, cause it's not Symfony itself who ultimately needs the class but Doctrine?

@stof
Copy link
Member

stof commented Aug 16, 2018

@ThomasLandauer if it gets the right tag, DoctrineBundle knows that this service is being used as an entity listener, and so should be injected in the class responsible for hooking into the Doctrine instantiation (to make it use the service rather than delegating to the Doctrine logic).

The way ContainerAwareEntityListenerResolver works is that we configure it with a list of entity listeners in it. When being asked to resolve an entity listener, it first tries in this list before delegating to the new $class() implementation of Doctrine.
But this class uses dependency injection. Anything it needs gets injected into it, not magically taken from a magic lamp. And this tag on the service is what tells DoctrineBundle that your own service needs to be injected in the ContainerAwareEntityListenerResolver.

@stof
Copy link
Member

stof commented Aug 16, 2018

and as Doctrine does not have a specific interface to implement on entity listeners, Symfony's autoconfiguration system cannot add the tag automatically for you (as it does for other things, like the Symfony event subscribers for instance)

@Yolo-plop
Copy link

I'd like some updates on this to.
Is this the expected behaviour? There's not much documentation about entity event subscribers on Symfony's website.
One thing i especially like with Symfony 3.3>= is not having to fill out tedious configuration files and since everything except entities is supposed to be a service, it is strange that those listeners don't follow the rule.

@zacharyzh
Copy link

zacharyzh commented Aug 6, 2019

@stof @xabbuh

Looks like the EntityListener is a original implementation from Doctrine. So I defined a new EntityListenerResolver instead and replaced default resolver through Compiler Pass.

class AppEntityListenerResolver extends DefaultEntityListenerResolver
{
    // ...
    
    public function resolve($className)
    {
        $listener = parent::resolve($className);
        if ($listener instanceof ContainerAwareInterface) {
            $listener->setContainer($this->container);
        }
        return $listener;
    }
}

Maybe Symfony could create a new resolver to replace the original by default. Is this easy to implement?

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants