Symfony2配置组件-树构建器如何验证提供的单个选项


Symfony2 Config component —How can a tree builder validate a single option provided?

我的路由配置提供了两个选项:模板和选项。我想验证用户的配置必须只提供一个选项,而不是两个,但我不知道如何做到这一点:

routes:
  hello:
    title: Hello world
    template: %my_module/templates/hello.html.twig
  hello/%name:
    title: Hello again
    content: 'Hello {{name}}!'

默认情况下,ArrayNode不提供此功能,但我们可以扩展此功能。

<?php
namespace AndyTruong'Config'Definition;
use Symfony'Component'Config'Definition'Builder'ArrayNodeDefinition;
use Symfony'Component'Config'Definition'Exception'InvalidConfigurationException;
class RadioArrayNodeDefinition extends ArrayNodeDefinition
{
    private $radioOptions = [];
    public function mustFillOnlyOneOfTheseKeys(array $keys)
    {
        $this->radioOptions[] = $keys;
    }
    protected function createNode()
    {
        $node = parent::createNode();
        $node->setFinalValidationClosures([
            [$this, 'validateRadioOptions']
        ]);
        return $node;
    }
    public function validateRadioOptions($value)
    {
        foreach ($this->radioOptions as $radioOptions) {
            $count = 0;
            foreach ($radioOptions as $radioKey) {
                if (!is_null($value[$radioKey])) {
                    $count++;
                }
            }
            switch ($count) {
                case 1:
                    return $value;
                case 0:
                    throw new InvalidConfigurationException('One of these keys is required but none given: ' . implode(', ', $radioOptions));
                default:
                    throw new InvalidConfigurationException('Only one of these keys is allowed: ' . implode(', ', $radioOptions));
            }
        }
    }
}

那么,我们可以使用新的定义如下:

<?php
use Symfony'Component'Config'Definition'Builder'NodeBuilder;
use Symfony'Component'Config'Definition'Builder'TreeBuilder;
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('routing');
/* @var $route_item NodeBuilder */
$route_item = $rootNode->children()
  ->setNodeClass('radioArray', 'AndyTruong'Config'Definition'RadioArrayNodeDefinition')
  ->arrayNode('routes')
  ->prototype('radioArray');
$route_item->mustFillOnlyOneOfTheseKeys(['content', 'template']);