0

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;
    }
}
tirithen
  • 3,219
  • 11
  • 41
  • 65
  • Bundle::boot gets executed after the container is built. Which sort of answers your question. However, doing it in the build phase is the proper approach. – Cerad Jul 29 '13 at 15:28

2 Answers2

3

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

rpg600
  • 2,800
  • 18
  • 24
  • This works to get the service, but I'm having problems with the service properties being reset when I try to get the values I have set in the CompilerPass class from a controller, it seems that I get another instance of the class when getting the service from a controller. What I want to do is register Theme instances when the bundle is instantiated and then access the Theme instances from a controller. Why are the service class properties reset? – tirithen Jul 29 '13 at 13:48
  • @Tirithen See my edit, you need to get the service definition and use addMethodCall method. – rpg600 Jul 29 '13 at 14:58
2

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__));
    }
}
Léo Benoist
  • 2,511
  • 1
  • 20
  • 18
  • When I tried this I got an error stating that the service "organization.themes" could not be found. – tirithen Jul 29 '13 at 14:31