Reputation: 3517
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
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
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