symfony 4 configuration default value ( no file ) - symfony

I'm trying to create a default configuration for my bundle. I created the bundle, bundle extension and configuration class. Inside the configuration class, I define a couple of keys with a default value. No yaml files are created. When I try to dump the configuration, I got the error:
"The extension with alias "foxtrot_alpha_users" does not have configuration."
But if I create the matching yaml file, and define at least one of the keys, the other key takes the default value as stated in the configuration class the value can be overridden.
Is it possible to define a default configuration with no yaml file at all?
Bundle class:
class UsersBundle extends Bundle
{
/**
* this is to have a custom alias
*
* #return void
*/
public function getContainerExtension()
{
return( new UsersExtension() );
}
}
Extension class:
class UsersExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
foreach($config as $key => $value)
{
$parameterKey = $this->getAlias().'.'.$key;
$container->setParameter($parameterKey, $value);
}
$container->setParameter('default_password', $config['default_password']);
}
/**
* customized alias
*
* #return string
*/
public function getAlias()
{
return('foxtrot_alpha_users');
}
Configuration class:
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('foxtrot_alpha_users');
$rootNode = $treeBuilder->getRootNode();
$this->addUserParameters( $rootNode );
return $treeBuilder;
}
private function addUserParameters( ArrayNodeDefinition $rootNode )
{
$rootNode
->addDefaultsIfNotSet()
->children()
->scalarNode('default_password')
->defaultValue('248')
->end()
->scalarNode('x')
->defaultValue('1')
->end()
->end()
;
return( $rootNode );
}

Related

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

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?
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

How can I access config parameters defined in a custom extension?

I created a custom extension in my app for the sole purpose of being able to define config in yaml. I am unable to retrieve configuration params with ParameterBagInterface. What am I missing with the following approach?
config/packages/myapp.yaml
myapp:
foo:
bar: '%env(MYAPP_BAR)%'
I created a command to test if I could retrieve the config. When run, I get the error:
The parameter "myapp" must be defined.
class FooCommand extends Command
{
...
public function __construct(ParameterBagInterface $params, $name = '')
{
$this->params = $params;
parent::__construct();
}
...
protected function execute(InputInterface $input, OutputInterface $output): int
{
dd($this->params->get('myapp'));
...
I set all this up by creating the following files and modifying Kernel.php
src/DependencyInjection/Configuration.php
src/DependencyInjection/MyappExtension.php
Note that in the extension file below, the dd($config) prints everything defined in myapp.yaml.
src/DependencyInjection/MyappExtension.php
class MyappExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// This dumps my config as an array
// dd($config);
}
}
src/DependencyInjection/Configuration.php
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('myapp');
$treeBuilder->getRootNode()
->children()
->arrayNode('foo')
->children()
->scalarNode('bar')->end()
->end()
->end()
->end();
return $treeBuilder;
}
src/Kernel.php (1 line modification)
...
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
{
$container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php'));
$container->registerExtension(new MyappExtension());
...

Symfony - Get custom param in bundle controller

I'm running a website under Symfony 3.4.12 and I created my own custom bundle. I have a custom config file in Yaml :
# src/CompanyBundle//Resources/config/config.yml
company_bundle:
phone_number
... and it is launched this way :
<?php
# src/CompanyBundle/DependencyInjection/CompanyExtension.php
namespace CompanyBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class CompanyExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('config.yml');
}
}
?>
I would like to retrieve my custom parameters in my controller file, what is the best way to do it ? I tried this way, with no success :
$this->getParameter('company_bundle.phone_number')
Thanks.
You have to define your own DependencyInjection/Configuration.php: http://symfony.com/doc/3.4/bundles/configuration.html#processing-the-configs-array
Like that:
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('company_bundle');
$rootNode
->children()
->scalarNode('phone_number')
->end()
->end()
;
}
And then process it into your DependencyInjection/...Extension.php file. If you want to make this option as parameter you have to do it like that:
public function load(array $configs, ContainerBuilder $container)
{
// Some default code
$container->setParameter('company_bundle.phone_number', $config['phone_number']);
}
And then you can get this parameter in your controller like you do.

How to load, process and use custom parameters from Yaml configuration files in DI Extension class?

I'm trying to import a yaml configuration file in my App following the documentation provided here http://symfony.com/doc/current/bundles/extension.html but I always have the error message:
There is no extension able to load the configuration for "app"
My file is located here : config/packages/app.yaml and has the following structure :
app:
list:
model1:
prop1: value1
prop2: value2
model2:
...
As this is a simple App, all the files are in src/. So I have src/DependencyInjection/AppExtension.php
<?php
namespace App\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class AppExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
}
}
And src/DependencyInjection/Configuration.php
<?php
namespace App\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('app');
// Node definition
$rootNode
->children()
->arrayNode('list')
->useAttributeAsKey('name')
->requiresAtLeastOneElement()
->prototype('array')
->children()
->requiresAtLeastOneElement()
->prototype('scalar')
->end()
->end()
->end()
->end()
->end();
return $treeBuilder;
}
}
I'm not able to access my parameters :(
Any idea?
If you want to load a custom configuration file to process it's parameters using an Extension class (like in Symfony bundle extension but without to create a bundle), to eventually "create" and add one or more of it to the "container" (before it will be compiled) you can register your Extension class manually in the configureContainer method contained in the Kernel.php file:
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader)
{
// to avoid the same error you need to put this line at the top
// if your file is stored under "$this->getProjectDir().'/config'" directory
$container->registerExtension(new YourAppExtensionClass());
// ----- rest of the code
}
Then you can use your params as usual registering a Compiler Pass.
Hope this helps.

How to override SyliusCoreBundle Model User

I try to add a new field "phone" in model User (SyliusCoreBundle/Model/User).
Avoiding to touch SyliusCoreBundle,
I create a new bundle 'ShopBundle' which is beside of the others sylius bundles to override base user class :
src/Sylius/Bundle/ShopBundle
in the folder ShopBundle :
> /Controller(empty)
> /DependencyInjection(empty)
> /Model
> /User.php
> /Resources
> /config/doctrine/model/user.orm.xml
> /config/service.xml (empty)
> SyliusShopBundle.php
In file src/Sylius/Bundle/ShopBundle/Model/User.php, I have :
<?php
namespace Sylius\Bundle\ShopBundle\Model;
use Sylius\Bundle\CoreBundle\Model\User as BaseUser;
class User extends BaseUser
{
protected $mobile;
/**
* {#inheritdoc}
*/
public function setMobile($mobile)
{
$this->mobile = $mobile;
}
/**
* {#inheritdoc}
*/
public function getMobile()
{
return $this->mobile;
}
}
In file src/Sylius/Bundle/ShopBundle/Resources/config/doctrine/model/user.orm.xml, I have :
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<mapped-superclass name="Sylius\Bundle\ShopBundle\Model\User" table="sylius_user">
<field name="mobile" column="mobile" type="string" nullable="true" />
</mapped-superclass>
</doctrine-mapping>
In file src/Sylius/Bundle/ShopBundle/SyliusShopBundle.php, I have :
class SyliusShopBundle extends Bundle
{
/**
* Return array with currently supported drivers.
*
* #return array
*/
public static function getSupportedDrivers()
{
return array(
SyliusResourceBundle::DRIVER_DOCTRINE_ORM
);
}
}
I add this line in app/AppKernel.php
new Sylius\Bundle\ShopBundle\SyliusShopBundle(),
Final, I do commend like :
php app/console doctrine:schema:update --dump-sql
I got nothing to update in database.
Which part i missed ? What can i do to make it works ? Thanks !!
I added two files in folder DependencyInjection
Configuration.php
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('sylius_shop');
$rootNode
->addDefaultsIfNotSet()
->children()
->scalarNode('driver')->cannotBeOverwritten()->isRequired()->cannotBeEmpty()->end()
->end()
;
$this->addClassesSection($rootNode);
return $treeBuilder;
}
/**
* Adds `classes` section.
*
* #param ArrayNodeDefinition $node
*/
private function addClassesSection(ArrayNodeDefinition $node)
{
$node
->children()
->arrayNode('classes')
->addDefaultsIfNotSet()
->children()
->arrayNode('user')
->addDefaultsIfNotSet()
->children()
->scalarNode('model')->defaultValue('Sylius\\Bundle\\ShopBundle\\Model\\User')->end()
->end()
->end()
->end()
->end()
->end()
;
}
}
SyliusShopExtension.php
<?php
namespace Sylius\Bundle\ShopBundle\DependencyInjection;
use Sylius\Bundle\ResourceBundle\DependencyInjection\SyliusResourceExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class SyliusShopExtension extends SyliusResourceExtension
{
/**
* #var array
*/
private $bundles = array();
/**
* {#inheritdoc}
*/
public function load(array $config, ContainerBuilder $container)
{
$this->configDir = __DIR__.'/../Resources/config';
$this->configure($config, new Configuration(), $container, self::CONFIGURE_LOADER | self::CONFIGURE_DATABASE | self::CONFIGURE_PARAMETERS);
}
}
It should be User.orm.xml, not user.orm.xml.
You have to configure the class under sylius_core -> classes -> user -> model node.
You should not inspire your bundle by Sylius bundles, and definitely not put in under "Sylius" namespace. Just create a very basic Symfony bundle and put your User entity under Entity namespace, Symfony won't see it under Model.
I would add that in order to use Sylius fixtures you have to configure the user resource too:
sylius_resource:
resources:
sylius.user:
classes:
model: MyBundle\UserBundle\Entity\User

Resources