Search code examples
phpsymfonyconfigurationbundlesymfony4

Symfony bundle configuration array node default value merge


I have a problem with Symfony4 bundle configuration. I have a configuration class with:

$rootNode
        ->children()
            ->arrayNode('mapping')
                ->useAttributeAsKey('code')
                ->prototype('scalar')->end()
                ->defaultValue(['default' => 'test'])
            ->end()
            .....
        ->end();

This returns a default configuration like:

array(1) {
   ["default"]=> string(4) "test"
}

But when I add a configuration file:

bundle:
    mapping:
        test: newvalue
        test2: newvalue2

Im getting a config:

array(2) {
   ["test"]=> string(8) "newvalue"
   ["test2"]=> string(9) "newvalue2"
}

But I expect to merge these two configs to get:

array(3) {
   ["default"]=> string(4) "test"
   ["test"]=> string(8) "newvalue"
   ["test2"]=> string(9) "newvalue2"
}

How can I set this defaults to be merged with provided configurations? Of course I want to let "default" config to be overridden, but by deafult to be merged.

I cant find any solution on docs https://symfony.com/doc/current/components/config/definition.html#array-node-options

Please help :)


Solution

  • If I understood correctly you want the default entry to be always defined. Applicaions will be able to overwrite the default value DEFAULT of default key.

    Ok, there are two solutions :

    A bad solution

        $rootNode = $treeBuilder->getRootNode()
            ->children()
                ->arrayNode('mapping')
                    ->useAttributeAsKey('code')
                    ->prototype('scalar')->end()
                    ->beforeNormalization()
                        ->ifArray()
                        ->then(function ($mapping) {
                            return $mapping + ['default' => 'DEFAULT'];
                        })
                    ->end()
                ->end();
    

    If default key is not defined it will be added along with default DEFAULT value. It will function for ONE configuration file parsed. BUT you will get the problem if you have two and more configuraion files:

    # config.yml
    mapping:
        some: value
        default: MY_DEVELOPMENT_DEFAULT
    
    # prod/config.yml
    mapping:
        some: value_prod
    

    You will have :

    ['some' => 'value_prod', 'default' => 'DEFAULT']
    

    This is wrong. Default value replaces MY_DEVELOPMENT_DEFAULT because it was added to prod/config.yml and merged with config.yml.

    Unfortunately, tree builder doesn't permit to define callback after merge.

    A good solution

    You can add your default entries after the configuraion values are merged (e.g. in compiler passes).