php - Symfony2 配置 : omit arrayNode instead of providing empty array

标签 php symfony

我的 Symfony2 项目中有以下配置:

<?php

namespace Acme\CommonBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
 * This is the class that validates and merges configuration from your app/config files
 *
 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
 */
class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritDoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('acme_common');

        $rootNode
            ->children()
                ->arrayNode('controller')
                    ->children()
                        ->arrayNode('controllers') // overwrite defaults declared above for specific controllers
                            ->prototype('array')
                                ->children()
                                    ->scalarNode('title')->end()
                                    ->append($this->addActionsNode())
                                    ->append($this->addViewsNode())
                                ->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end();

        return $treeBuilder;
    }

    /**
     * Add the actions node, e.g.:
     * actions:
     *     edit:
     *         name: Edit
     *         path: edit
     *         icon: icon-edit
     *         width: 45
     *     archive:
     *         name: Archive
     *         path: archive
     *         icon: icon-thrash
     *         width: 75
     *
     * These are the actions that will be shown in the index view
     *
     * @return ArrayNodeDefinition|NodeDefinition
     */
    protected function addActionsNode()
    {
        $node = $this->getNode('actions');

        $node
            ->useAttributeAsKey('key')
            ->prototype('array')
                ->children()
                    ->scalarNode('name')->end()
                    ->scalarNode('path')->end()
                    ->scalarNode('icon')->end()
                    ->integerNode('width')->end()
                ->end()
            ->end();

        return $node;
    }

    /**
     * Configure the templates used, e.g.:
     * views:
     *     index: :AcmeCommon:Crud/list.html.twig
     *     edit: :AcmeCommon:Crud/edit.html.twig
     * 
     * @return ArrayNodeDefinition|NodeDefinition
     */
    protected function addViewsNode()
    {
        $node = $this->getNode('views');

        $node
            ->children()
                ->scalarNode('index')->end()
                ->scalarNode('edit')->end()
            ->end();

        return $node;
    }

    /**
     * Helper function to build a node
     *
     * @param string  $name
     *
     * @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition|NodeDefinition
     */
    protected function getNode($name)
    {
        $builder = new TreeBuilder();
        $node = $builder->root($name);

        return $node;
    }
}

我的想法是,我可以为我正在编写的一堆 CRUD Controller 配置一些东西。操作和 View 节点是可选的。仅当您想覆盖某些默认值时才应使用它们。配置可能如下所示:

acme_common:
    controller:
        controllers:
            acme_foo_controller:
                title: Foo
                actions:
                    edit:
                        name: Edit
                        path: edit
                        icon: icon-edit
                        width: 45
                views:
                    index: :AcmeCommon:Foo/list.html.twig
                    edit: :AcmeCommon:Foo/edit.html.twig
            acme_bar_controller:
                title: Bar
                views:
                    index: :AcmeCommon:Bar/list.html.twig
            acme_baz_controller:
                title: Baz

这会产生一个如下所示的数组:

Array
(
    [controller] => Array
        (
            [controllers] => Array
                (
                    [acme_foo_controller] => Array
                        (
                            [title] => Foo
                            [actions] => Array
                                (
                                    [edit] => Array
                                        (
                                            [name] => Edit
                                            [path] => edit
                                            [icon] => icon-edit
                                            [width] => 45
                                        )
                                )
                            [views] => Array
                                (
                                    [index] => :AcmeCommon:Foo/list.html.twig
                                    [edit] => :AcmeCommon:Foo/edit.html.twig
                                )
                        )
                    [acme_bar_controller] => Array
                        (
                            [title] => Bar
                            [views] => Array
                                (
                                    [index] => :AcmeCommon:Bar/list.html.twig
                                )
                            [actions] => Array
                                (
                                )
                        )
                    [acme_baz_controller] => Array
                        (
                            [title] => Baz
                            [actions] => Array
                                (
                                )
                        )
                )
        )
)

正如您所看到的,当未声明 actions 时,它仍然作为一个空数组添加。我希望这样 actions 被简单地省略并且结果数组看起来像:

Array
(
    [controller] => Array
        (
            [controllers] => Array
                (
                    [acme_foo_controller] => Array
                        (
                            [title] => Foo
                            [actions] => Array
                                (
                                    [edit] => Array
                                        (
                                            [name] => Edit
                                            [path] => edit
                                            [icon] => icon-edit
                                            [width] => 45
                                        )
                                )
                            [views] => Array
                                (
                                    [index] => :AcmeCommon:Foo/list.html.twig
                                    [edit] => :AcmeCommon:Foo/edit.html.twig
                                )
                        )
                    [acme_bar_controller] => Array
                        (
                            [title] => Bar
                            [views] => Array
                                (
                                    [index] => :AcmeCommon:Bar/list.html.twig
                                )
                        )
                    [acme_baz_controller] => Array
                        (
                            [title] => Baz
                        )
                )
        )
)

这可能吗?

最佳答案

我通过使用 xdebug 单步执行代码来解决这个问题。 PrototypeNode 似乎总是有一个默认值,即一个空数组。进一步的探测显示这个空数组是在最终确定阶段应用的。但是,这个阶段的最后一步是应用终结闭包,它恰好也是规范化闭包。要删除不需要的空数组,您需要将 normalize()->always() 添加到父节点,并为其提供一个返回修改后的数组的闭包。

你修改的例子:

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

    $rootNode
        ->children()
            ->arrayNode('controller')
                ->children()
                    ->arrayNode('controllers') // overwrite defaults declared above for specific controllers
                        ->prototype('array')
                            ->validate()
                                ->always(function($v){
                                    if ( empty($v['action']) )
                                        unset($v['action']);
                                    return $v;
                                })
                            ->end()
                            ->children()
                                ->scalarNode('title')->end()
                                ->append($this->addActionsNode())
                                ->append($this->addViewsNode())
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ->end();

    return $treeBuilder;
}

关于php - Symfony2 配置 : omit arrayNode instead of providing empty array,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23340649/

相关文章:

php - phpcs 的 PSR-2 编码标准和 Symfony2 编码标准之间的主要区别是什么?

symfony - 时间显示格式?

symfony - 如何让onAuthenticationSuccess函数被调用

symfony - symfony 3.4 形式的新实体中的空值没有默认值

php - 如何在 Propel 中启用 LOAD DATA LOCAL INFILE?

php - 基于数据库值创建 HTML 表

PHPUnit Laravel 无法在 ut 中更新

symfony - PHPUnit LogicException : The request was not redirected. Symfony

java - Google App Engine - 在 java 中执行 php 的方法?

php - 从动态 MySql 表中的第 n 行开始获取 m 行