PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Friday, May 20, 2022

[FIXED] How to provide default values for Symfony bundle parameters / config values?

 May 20, 2022     autowired, composer-php, symfony     No comments   

Issue

I have created a Symfony 5.3+ bundle which should be used to add common code to different projects. The bundle contains some services which should be configurable using parameters / options as described in the Symfony docs.

How to provide default values for these options? Defaults set in the bundles Configuration.php do not have any effect.


Details:

I have created a bundle project using the following structure and added it to my Symfony project using composer:

path/to/bundles/XYCommonsBundle/
    config/
        services.yaml
    src/
        Service/
            SomeService.php
        DependencyInjection
            Configuration.php
            XYCommensExtension.php
    XYCommensBundle.php
    composer.json
    ...


// src/DependencyInjection/XYCommensExtension.php
<?php

namespace XY\CommensBundle\DependencyInjection;

use ...

class XYCommensExtension extends Extension {
    public function load(array $configs, ContainerBuilder $container) {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        // make config available as parameters. Necessary?
        foreach ($config as $key => $value) {
            $container->setParameter('xy_commons.' . $key, $value);
        }


        $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../../config'));
        $loader->load('services.yaml');
    }
}


// src/DependencyInjection/Configuration.php
class Configuration implements ConfigurationInterface {
    public function getConfigTreeBuilder() {
        $treeBuilder = new TreeBuilder('xy_commons');
        
        $treeBuilder->getRootNode()
            ->children()
                ->arrayNode('params')
                    ->children()
                        ->integerNode('paramA')->defaultValue(100)->end()
                        ->integerNode('ParamB')->defaultValue(200)->end()
                    ->end()
                ->end()
            ->end()
        ;
        
        return $treeBuilder;
    }
}


// config/services.yaml
services:
    xy_commons.service.some_service:
        class:  XY\CommonsBundle\Service\SomeService
        arguments:   
            - $paramA: '%xy_commons.params.paramA%'
            - $paramB: '%xy_commons.params.paramB%' 


// src/Service/SomeService.php
<?php

namespace XY\CommensBundle\Service;

use ...

class SomeService {
    public function __construct(LoggerInterface $logger, $paramA, $paramB) {
}    

Problem: How to use default values of parameters?

paramA and paramB are defined in the bundles Configuration.php with default values of 100 and 200. I would like to use these defaults in the project without specifying custom values. However, I do not create a config/packages/xy_commons.yaml file in the project and explicitly specify values, I get the following error:

You have requested a non-existent parameter "xy_commons.params.paramA".

When creating a config/packages/xy_commons.yaml file, I cannot use ~ to use the default value:

xy_commons:
    params:
        paramA: ~
        paramB: ~

Invalid type for path "xy_commons.params.paramA". Expected "int", but got "null".

Only when explicitly specifying a value it works:

xy_commons:
    params:
        paramA: 300
        paramB: 400

How to use the default values defined in Configuration.php?


Solution

There are three problems here. The first deals with what is possibly my least favorite class in Symfony. The dreaded configuration object. In particular, you need to use addDefaultsIfNotSet when dealing with arrays:

class Configuration implements ConfigurationInterface 
{
  public function getConfigTreeBuilder() 
  {
      $treeBuilder = new TreeBuilder('my');

      $rootNode = $treeBuilder->getRootNode();

      $rootNode
          ->children()
              ->arrayNode('params')->addDefaultsIfNotSet() # ADD THIS
                  ->children()
                      ->integerNode('paramA')->defaultValue(100)->end()
                      ->integerNode('paramB')->defaultValue(200)->end()
                  ->end()
              ->end()
          ->end()
      ;
      
      return $treeBuilder;
  }
}

The second problem is that you are not defining your parameters correctly. In particular the parameters will be grouped in an array called params. You almost had it:

class MyExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        foreach($config['params'] as $key => $value) {
           $container->setParameter('xy_commons.params.' . $key, $value);
        }

# Use bin/console dump:container --parameters to verify

The final thing is the use of tilde to indicate default values. I think this might be a works as designed issue. Technically, tilde on yaml means null and it is up to the processor to get it a meaning. The ArrayNode works as expected but the IntegerNode does not. So this works:

# config/packages/my.yaml
my:
    params: ~
#        paramA: ~
#        paramB: 666


Answered By - Cerad
Answer Checked By - David Marino (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing