tirithen
tirithen

Reputation: 3517

Symfony: Cannot access a service inside bundle constructor

How can I access a service inside of a Bundle constructor? I'm trying to create a system where a theme bundle can register itself automatically with the theme service, see small example below (the simpler solution the better):

<?php

namespace Organization\Theme\BasicBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class ThemeBasicBundle extends Bundle
{
    public function __construct() {
        $themes = $this->get('organization.themes');
        $themes->register(new Organization\Theme\BasicBundle\Entity\Theme(__DIR__));
    }
}

However, $this->get does not work, this might be because there is no guarantee that all bundles has been registered yet, are there any post bundle registration "hooks" that I can use instead? Are there any special method names that I can add to the bundle class that gets executed after all bundles has been instantiated?

The service class looks like this:

<?php

namespace Organization\Theme\BasicBundle;

use Organization\Theme\BasicBundle\Entity\Theme;

class ThemeService
{
    private $themes = array();

    public function register(Theme $theme) {
        $name = $theme->getName();

        if (in_array($name, array_keys($this->themes))) {
            throw new Exception('Unable to register theme, another theme with the same name ('.$name.') is already registered.');
        }

        $this->themes[$name] = $theme;
    }

    public function findAll() {
        return $this->themes;
    }

    public function findByName(string $name) {
        $result = null;

        foreach($this->themes as $theme) {
            if ($theme->getName() === $name) {
                $result = $theme;
            }
        }

        return $result;
    }
}

Upvotes: 0

Views: 1871

Answers (2)

rpg600
rpg600

Reputation: 2850

It's normal that you can't access to the service container, because services are not compiled yet. To inject tagged services into that bundle, you need to create a new compiler pass.

To create a compiler pass it needs to implements the CompilerPassInterface.

Put the class in the DependencyInjection/Compiler folder of the bundle.

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class CustomCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if ($container->has('organization.themes')) {
            $container->getDefinition('organization.themes')->addMethodCall('register', array(new Organization\Theme\BasicBundle\Entity\Theme(__DIR__)));
        }
    }
}

Then override the build method of your bundle definition class.

class ThemeBasicBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new CustomCompilerPass());
    }
}

Some links:

http://symfony.com/doc/current/components/dependency_injection/compilation.html http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html http://symfony.com/doc/current/components/dependency_injection/tags.html

Upvotes: 3

L&#233;o Benoist
L&#233;o Benoist

Reputation: 2541

Try that it could work :) :

<?php

namespace Organization\Theme\BasicBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class ThemeBasicBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $themes = $container->get('organization.themes');
        $themes->register(new Organization\Theme\BasicBundle\Entity\Template(__DIR__));
    }
}

Upvotes: 2

Related Questions