2

I want the next Symfony configuration for a bundle:

'foo' => [
    'bar' => [
        'item_1' => [
            [
                'id' => '25',
                'url' => 'https://foo',
            ],
            [
                'id' => '26',
                'url' => 'https://bar',
            ],
        ],
    ],
]

and I want to get keep all keys, so I used useAttributeAsKey:

class Configuration implements ConfigurationInterface
{
    /**
     * Generates the configuration tree builder.
     *
     * @return TreeBuilder $builder The tree builder
     */
    public function getConfigTreeBuilder()
    {
        $builder = new TreeBuilder();

        $rootNode = $builder->root('foo');
        $rootNode->children()
            ->arrayNode('bar')
                ->useAttributeAsKey('name')
                ->arrayPrototype()
                    ->children()
                        ->scalarNode('id')->end()
                        ->scalarNode('url')->end()
                    ->end()
                ->end()
            ->end()
            ->end();

        return $builder;
     }
}

However, I'm getting this error:

"Unrecognized options "0, 1" under "foo.bar.item_1""

Reading https://symfony.com/doc/4.4/components/config/definition.html I understand this should work. Any idea?

1 Answer 1

3

Here is an example of input array that your Configuration class expects you to provide:

'foo' => [
    'bar' => [
        'item_1' => [
            'id' => '25',
            'url' => 'https://foo',
        ],
        'item_2' => [
            'id' => '26',
            'url' => 'https://bar',
        ],
    ],
]

There seems to be no out-of-the-box way to create a config like shown in the question. Here are a couple of options though:

Option 1

If you are ok with creating an additional nesting level under item_{1}, then you can do something like this:

'foo' => [
    'bar' => [
        'item_1' => [
            // `rows` is an additional nesting level
            'rows' => [
                [
                    'id' => '25',
                    'url' => 'https://foo',
                ],
                [
                    'id' => '26',
                    'url' => 'https://bar',
                ]
            ]
        ],
        'item_2' => [
            'rows' => [
                [
                    'id' => '27',
                    'url' => 'https://foo',
                ]
            ]
        ]
    ]
]
$rootNode->children()
    ->arrayNode('bar')
        ->useAttributeAsKey('name')
        ->arrayPrototype()
            ->children()
                ->arrayNode('rows')
                    ->arrayPrototype()
                        ->children()
                            ->scalarNode('id')->end()
                            ->scalarNode('url')->end()
                        ->end()
                    ->end()
                ->end()
            ->end()

            // If you'd like, you can git rid of the extra 
            // nesting level in the parsed configuration
            ->validate()
                ->always()
                ->then(function ($v) {
                    return $v['rows'] ?? [];
                })
            ->end()
        ->end()
    ->end();

Option 2

If you absolutely have to use the input configuration structure that you provided in the question (i.e. without extra nesting levels), looks like the only option is to use variableNode for bar and perform validation manually:

$rootNode->children()
    ->variableNode('bar')
        ->validate()
            ->ifTrue( function($barConfig) {
                // the config under `bar` has to be an array
                if (!is_array($barConfig)) {
                    return true;
                }
                return 0 < count(array_filter($barConfig, function ($rowConfig) {
                    // every item under `item_1`, `item_2`... keys also has to be an array
                    if (!is_array($rowConfig)) {
                        return true;
                    }
                    return 0 < count(array_filter($rowConfig, function ($cellConfig) {
                        // every cell has to be an array with `id` and `url` keys
                        return !is_array($cellConfig) || empty($cellConfig['id']) || empty($cellConfig['url']);
                    }));
                }));
            })
            ->thenInvalid('Invalid matrix config')
        ->end()
    ->end()
Sign up to request clarification or add additional context in comments.

Comments

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.