Dependency Injection in Event Listeners

Hi,

I would like to use DI in Event Listener class which is part of a plugin:

class MyEvents implements EventListenerInterface
{
    /**
     * @return void
     */
    public function __construct(
        private readonly AttributesServiceInterface $attributesService
    ) {
    }

    /**
     * @return string[]
     */
    public function implementedEvents(): array
    {
        return [
            'Model.initialize'                        => 'bindModels'
            (...)
        ];
    }
}
class Plugin extends BasePlugin
{
    public function bootstrap(PluginApplicationInterface $app): void
    {
        parent::bootstrap($app);
    }

    public function services(ContainerInterface $container): void
    {
        $container->addShared(AttributesServiceInterface::class, AttributesService::class);

        $container->addShared(MyEvents::class)
            ->addArgument(AttributesServiceInterface::class);

        parent::services($container);
    }
}

Events should work for any controller in the app and as I want to keep code separated from any other plugins and app-core code I would like to attach listeners in some place like plugin’s bootstrap file, but then I cannot use DI. Is there any solution for that? Is this even possible?

The place where you register your event listener is most likely the config/bootstrap.php or the bootstrap() method of the plugin class.

In both cases you have access to the base application class and therefore access to the container instance:

// bootstrap.php
<?php
/**
 * @var \YourPlugin\Plugin $this
 * @var \App\Application $app
 */
$container = $app->getContainer();
\Cake\Event\EventManager::instance()->on(new \App\EventListeners\SomeEventListener($container));
public function bootstrap(PluginApplicationInterface $app): void
{
    $container = $app->getContainer();
    \Cake\Event\EventManager::instance()->on(new \App\EventListeners\SomeEventListener($container));
}

which you then can leverage in your Event via

class SomeEventListener implements EventListenerInterface
{
    public function __construct(
        private \Cake\Core\ContainerInterface $container
    ) {
    }

    public function someMethod(): void
    {
        $service = $this->container->get(MyServiceClass::class);
    }
}
1 Like

This is exactly what I was looking for! Thank you very much!