Symfony 配置验证原型节点中键值对中的键

gin*_*nja 5 php dependency-injection symfony symfony-2.8

在 Symfony 2.8 中,给定这个 YAML 配置块

my_bundle:
  profiles:
    'abc': 123
    'def: 456
    '...': ...
Run Code Online (Sandbox Code Playgroud)

如何验证配置文件下键值中的键是否为非空字符串?

在这种情况下,'abc' 和 'def' 只是示例,它们是任意的,不能预先定义。

这是我的配置文件 -

->arrayNode('profiles')
    ->isRequired()
    ->requiresAtLeastOneElement()
    ->useAttributeAsKey('name')
    ->prototype('integer')
        ->min(0)
    ->end()
->end()
Run Code Online (Sandbox Code Playgroud)

问题是

my_bundle:
  profiles:
    123
    456
Run Code Online (Sandbox Code Playgroud)

当我不希望它是有效的。

我找不到一种简单的内置方法来验证原型数组的键。

可能的解决方案

一:似乎我可以把它变成一个数组数组

my_bundle:
  profiles:
    -
      name: abc
      value: 123
    -
      name: def
      value: 456
Run Code Online (Sandbox Code Playgroud)

->arrayNode('profiles')
    ->isRequired()
    ->useAttributeAsKey('name')
    ->requiresAtLeastOneElement()
    ->prototype('array')
        ->children()
            ->scalarNode('name')
                ->isRequired()
                ->cannotBeEmpty()
                ->validate()
                    ->ifTrue(
                        function($s) {
                            return !is_string($s);
                        }
                    )
                    ->thenInvalid('Profile key must be a non-empty string')
                ->end()
            ->end()
            ->integerNode('value')
                ->isRequired()
                ->min(0)
            ->end()
        ->end()
    ->end()
Run Code Online (Sandbox Code Playgroud)

但我不想这样做,因为我们的客户已经在他们的配置中使用了原始格式,我们可以比配置文件本身更容易更新配置。

二:似乎我可以在不更改配置的情况下通过添加一些规范化将旧配置格式转换为新格式执行 #1

->arrayNode('profiles')
    ->beforeNormalization()
        ->ifTrue(
            function($array) {
                $hasSimpleKeyValuePair = false;
                foreach ($array as $key => $value) {
                    if (!is_array($value)) {
                        $hasSimpleKeyValuePair = true;
                        break;
                    }
                }

                return $hasSimpleKeyValuePair;
            }
        )
        ->then(
            function($array) {
                $newArray = [];
                foreach ($array as $key => $value) {
                    if (!is_array($value)) {
                        $newArray[] = ['name' => $key, 'value' => $value];
                    }
                }
                return $newArray;
            }
        )
    ->end()
    ->isRequired()
    ->requiresAtLeastOneElement()
    ->prototype('array')
        ->children()
            ->scalarNode('name')
                ->isRequired()
                ->cannotBeEmpty()
                ->validate()
                    ->ifTrue(
                        function($s) {
                            return !is_string($s);
                        }
                    )
                    ->thenInvalid('Profile key must be a non-empty string')
                ->end()
            ->end()
            ->integerNode('value')
                ->isRequired()
                ->min(0)
            ->end()
        ->end()
    ->end()
->end()
Run Code Online (Sandbox Code Playgroud)

但这似乎是一个巨大的矫枉过正。

(这两个选项也意味着我需要更改配置文件数据的接收器以了解他们需要使用指向该数字的数组键索引而不是使用数字,$profile[$key]['value']而不仅仅是$profile[$key],我猜我可以这样做)

三:好像我可以在整个块上添加自定义验证

->arrayNode('profiles')
    ->isRequired()
    ->requiresAtLeastOneElement()
    ->useAttributeAsKey('name')
    ->prototype('integer')
        ->min(0)
    ->end()
    ->validate()
        ->ifTrue(function ($array) {
            $notValid = false;
            foreach ($array as $key => $value) {
                if (!is_string($key) || empty($key)) {
                    $notValid = true;
                    break;
                }
            }

            return $notValid;
        })
        ->thenInvalid('Profile keys must be a non-empty string.')
    ->end()
->end()
Run Code Online (Sandbox Code Playgroud)

这就是我现在要使用的方法,但对于我认为是常见用例的某些东西来说,这似乎仍然是不必要的自定义/hacky。

我只是想检查一下我有没有错过一些神奇的内置方式来使用 Symfony 配置来做到这一点?