Skip to content

Adding some information on normalisation and appending sections in a con... #1721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions components/config/definition.rst
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,168 @@ For all nodes:
``cannotBeOverwritten()``
don’t let other configuration arrays overwrite an existing value for this node

Appending sections
------------------

If you have a complex configuration to validate then the tree can grow to
be large and you may want to split it up into sections. You can do this by
making a section a separate node and then appending it into the main tree
with ``append()``::

public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('database');

$rootNode
->arrayNode('connection')
->children()
->scalarNode('driver')
->isRequired()
->cannotBeEmpty()
->end()
->scalarNode('host')
->defaultValue('localhost')
->end()
->scalarNode('username')->end()
->scalarNode('password')->end()
->booleanNode('memory')
->defaultFalse()
->end()
->end()
->append($this->addParametersNode())
->end()
;

return $treeBuilder;
}

public function addParametersNode()
{
$builder = new TreeBuilder();
$node = $builder->root('parameters');

$node
->isRequired()
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('name')->isRequired()->end()
->scalarNode('value')->isRequired()->end()
->end()
->end()
;

return $node;
}

This is also useful to help you avoid repeating yourself if you have sections
of the config that are repeated in different places.

Normalization
-------------

When the config files are processed they are first normalized, they are then
merged and then the tree is used to validate the resulting array. The normalization
is to remove some of the differences that result from different configuration formats,
mainly the differences between Yaml and XML.

The separator used in keys is typically ``_`` in Yaml and ``-`` in XML. For
example, ``auto_connect`` in Yaml and ``auto-connect``. The normalization would
make both of these ``auto_connect``.

Another difference between Yaml and XML is in the way arrays of values may
be represented. In Yaml you may have:

.. code-block:: yaml

twig:
extensions: ['twig.extension.foo', 'twig.extension.bar']

and in XML:

.. code-block:: xml

<twig:config>
<twig:extension>twig.extension.foo</twig:extension>
<twig:extension>twig.extension.bar</twig:extension>
</twig:config>

This difference can be removed in normalization by pluralizing the key used
in XML. You can specify that you want a key to be pluralized in this way with
``fixXmlConfig()``::

$rootNode
->fixXmlConfig('extension')
->children()
->arrayNode('extensions')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incomplete. A working example should be

->arrayNode('extensions')
    ->prototype('scalar')->end()
->end()

Btw, fixXmlConfig is only meaningful for prototyped nodes as its goal is to take care of the special case for XML (node vs array of node depending of the count)

->prototype('scalar')->end()
->end()
->end()
;

If it is an irregular pluralization you can specify the plural to use as
a second argument::

$rootNode
->fixXmlConfig('child', 'children')
->children()
->arrayNode('children')
->end()
;

As well as fixing this it ensures that single xml elements are still turned into an array.
So you may have:

.. code-block:: xml

<connection>default</connection>
<connection>extra</connection>

and sometimes only:

.. code-block:: xml

<connection>default</connection>

By default ``connection`` would be an array in the first case and a string
in the second making it difficult to validate. You can ensure it is always
an array with with ``fixXmlConfig``.

You can further control the normalization process if you need to. For example
you may want to allow a string to be set and used as a particular key or several
keys to be set explicitly. So that, if everything apart from id is optional in this
config:

.. code-block:: yaml

connection:
name: my_mysql_connection
host: localhost
driver: mysql
username: user
password: pass

you can allow the following as well:

.. code-block:: yaml

connection: my_mysql_connection

By changing a string value into an associative array with ``name`` as the key::

$rootNode
->arrayNode('connection')
->beforeNormalization()
->ifString()
->then(function($v) { return array('name'=> $v); })
->end()
->scalarValue('name')->isRequired()
// ...
->end()
;

Validation rules
----------------

Expand Down Expand Up @@ -278,3 +440,4 @@ Otherwise the result is a clean array of configuration values::
$processor = new Processor();
$configuration = new DatabaseConfiguration;
$processedConfiguration = $processor->processConfiguration($configuration, $configs);