Search code examples
phpsymfonybundle

Symfony 6.1 get another bundle configuration data


I'm trying to get the configuration of one bundle from within my bundle, configured using the new, simplified bundle configuratation at https://symfony.com/blog/new-in-symfony-6-1-simpler-bundle-extension-and-configuration

My bundle has symfony/maker-bundle as a dependency, and I'd like to know maker-bundle's configuration

maker: 
  root_namespace: MyApp

I'm trying to figure out where I have access to that information, especially since I no longer have an explicit MyBundleExtension class. When my bundle is loading, the parameter 'maker.root_namespace' is not in the container parameterBag. So I added a CompilerPass, and it's not visible there either.

I've tried using the prepend() and prependExtension() calls, but am not sure how to wire those in to the new bundle configuration, or even if that's the right place.

While I'm using the symfony/maker-bundle in the particular case, I often want to know the values of other bundle configurations, and it seems like a pretty common thing. I suspect it's possible without creating an Extension class, but not sure how.


Solution

  • Accessing other bundle configurations is currently possible in the prependExtension() method and it's the recommended place to do it:

    class MyMakerBundle extends AbstractBundle
    {
        public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void
        {
            $configs = $builder->getExtensionConfig('maker');
            
            // ...
        }
    }
    

    However, this method is meant to prepend more configs not to get/process these configs. So you have to take into account that it's a list of unprocessed and unmerged configs which can change if another extension is prepending a new config to the MakerBundle after your own extension.

    Further, the value of this root_namespace can be (less probable in this case) a parameter or env var (makes no sense but is still possible) which is not resolved at this point (raw value).

    Option 1 (Safest)

    Create a similar configuration in your MyMakerBundle::configure() method and prepend the one obtained from the MakerBundle extension:

    public function configure(DefinitionConfigurator $definition): void
    {
        $definition->rootNode()
            ->children()
                ->scalarNode('root_namespace')->end()
            ->end();
    }
    
    public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        $configs = $builder->getExtensionConfig('maker');
            
        // iterate in reverse to preserve the original order after prepending the config
        foreach (array_reverse($configs) as $config) {
            $builder->prependExtensionConfig('my_maker', [
                'root_namespace' => $config['root_namespace'],
            ]);
        }
    }
    
    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        // do something with 
        $config['root_namespace'];
    }
    

    https://symfony.com/doc/current/bundles/prepend_extension.html

    Option 2 (Risky)

    Because it's a simple configuration used in dev mode only, you can do a simple $config = array_merge(...$configs) directly to get the $config['root_namespace'] value:

    public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        $configs = $builder->getExtensionConfig('maker');
    
        $config = array_merge(...$configs);
        $container->parameters()
            ->set('my_maker.root_namespace', $config['root_namespace']);
    }
    
    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        // do something with 
        $builder->getParameter('my_maker.root_namespace'));
    
        $builder->getParameterBag()->remove('my_maker.root_namespace');
    }
    

    This second option can be done in a CompilerPass too with the benefit that you wouldn't need to create a temporal parameter.

    Option 3 (Risky)

    In a CompilerPass class, get the root namespace value from one of the following services belonging to the MakerBundle:

    • maker.generator argument 1 (e.g $container->getDefinition('maker.generator')->getArgument(1))
    • maker.autoloader_finder argument 0.

    I know, so far it's not as simple and intuitive as you might expect.