16

Can my configuration node source support both string and array values?

Sourcing from string:

# Valid configuration 1
my_bundle:
    source: %kernel.root_dir%/../Resources/config/source.json

Sourcing from array:

# Valid configuration 2
my_bundle:
    source:
        operations: []
        commands: []

The extension class would be able to distinguish between them:

if (is_array($config['source']) {
    // Bootstrap from array
} else {
    // Bootstrap from file
}

I might use something like this:

$rootNode->children()
    ->variableNode('source')
        ->validate()
            ->ifTrue(function ($v) { return !is_string($v) && !is_array($v); })
            ->thenInvalid('Configuration value must be either string or array.')
        ->end()
    ->end()
->end();

But how ca I add constraints on the structure of source (operations, commands, etc...) to the variable node (that should only be enforced when its value is of type array)?

2 Answers 2

21
+300

I think you can use the config normalization by refactoring your extension.

In your extension change your code to check if a path is set

if ($config['path'] !== null) {
    // Bootstrap from file.
} else {
    // Bootstrap from array.
}

And allow the user to use a string for config.

$rootNode
    ->children()
        ->arrayNode('source')
            ->beforeNormalization()
            ->ifString()
                ->then(function($value) { return array('path' => $value); })
            ->end()
            ->children()
                ->scalarNode('foo')
                // ...
            ->end()
        ->end()
    ->end()
;

Like this you can allow the user to use a string or an array which can be validate.

See the symfony documentation for normalisation

Hope it's helpful. Best regard.

Sign up to request clarification or add additional context in comments.

1 Comment

Not bad. I should handle the case when path and operations and commands are setted. +1 for me
7

It is possible to use a variable node type in combination with some custom validation logic:

<?php
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;

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

    $rootNode
        ->children()
            ->variableNode('source')
                ->validate()
                    ->ifTrue(function ($v) {
                        return false === is_string($v) && false === is_array($v);
                    })
                    ->thenInvalid('Here you message about why it is invalid')
                ->end()
            ->end()
        ->end();
    ;

    return $treeBuilder;
}

This will succesfully process:

my_bundle:
    source: foo

# and

my_bundle:
    source: [foo, bar]

but it won't process:

my_bundle:
    source: 1

# or

my_bundle
    source: ~

Of course, you won't get the nice validation rules a normal configuration node will provide you and you won't be able to use those validation rules on the array (or string) passed but you will be able to validate the passed data in the closure used.

2 Comments

Yes, I've already used something like this. But this time the array node is very complex.
I'm not sure if it's possible. Can't you use another structure like the one used by the JMSDiExtraBundle?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.