为矩阵定义 Symfony 配置

Define Symfony Configuration for matrix

我想要捆绑包的下一个 Symfony 配置:

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

我想保留所有密钥,所以我使用了 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;
     }
}

但是,我收到了这个错误:

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

正在阅读 https://symfony.com/doc/4.4/components/config/definition.html 我知道这应该行得通。有什么想法吗?

这是您的配置 class 希望您提供的输入数组示例:

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

似乎没有开箱即用的方法来创建问题中所示的配置。不过,这里有几个选项:

选项 1

如果您可以在 item_{1} 下创建额外的嵌套级别,那么您可以这样做:

'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();

选项 2

如果您绝对必须使用您在问题中提供的输入配置结构(即没有额外的嵌套级别),看起来唯一的选择是对 bar 使用 variableNode 并手动执行验证:

$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()