如何在Symfony2中访问嵌套参数值?

Got*_*bel 14 parameters configuration yaml symfony

我在我的parameters.yml文件中创建了一个参数:

parameters:
    category:
        var: test
Run Code Online (Sandbox Code Playgroud)

如何在我的访问中访问此参数config.yml?例如,我想将此参数作为全局twig变量传递给我的所有twig文件:

twig:
    globals:
        my_var: %category.var% # throws ParameterNotFoundException
Run Code Online (Sandbox Code Playgroud)

red*_*rdo 9

在我看过的所有symfony配置文件中,'parameters:'下的条目一直是完全限定的.我不完全理解为什么会这样,但它可以帮助您在parameters.yml中写入条目,如下所示:

category1.var1: xxx
category1.var2: yyy
category1.var3. zzz

category2.subcategory1.var1: 5
category2.subcategory1.var2: 10
category2.subcategory2.var1: foo
category2.subcategory2.var2: bar
Run Code Online (Sandbox Code Playgroud)

... 等等.

编辑

我尝试将问题中的嵌套参数粘贴到我的一个项目中的parameters.local.yml中,并运行一个简单的单元测试来从容器中检索它和一个完全限定的参数,例如

$testUserEmail = $container->getParameter('test.user.email');
$this->assertEquals('dahlia.flower@randomtest.com', $testUserEmail);
$testParam = $container->getParameter('category.var');
$this->assertEquals('test', $testParam);
Run Code Online (Sandbox Code Playgroud)

完全限定的参数很好,试图获得嵌套参数导致InvalidArgumentException:category.var必须定义参数.我认为参数不能用嵌套来定义.


小智 6

$this->container->getParameter('category')['var']
Run Code Online (Sandbox Code Playgroud)

我在symfony 2.8上测试了它,它对我有用.


pal*_*mic 5

或者你可以编写简单的ParameterBag,它可以动态地实现嵌套参数(没有配置值的双重性):

<?php

namespace Your\Namespace;

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;

class ParameterBagNested extends ParameterBag
{

    /**
     * wire $this->set() logic into add() too
     *
     * @param array $parameters
     */
    public function add( array $parameters )
    {
        foreach ( $parameters as $name => $value ) {
            $this->set( $name, $value );
        }
    }

    /**
     * sets all levels of nested array parameters with dot notation
     * - loggly[host: loggly.com] will be translated this way:
     *  - loggly: [host: loggly.com] - standard array parameter will be left as is
     *  - loggly.host: loggly.com - nested variables ar translated so you can access them directly too as parent.variable
     *
     * @param string $name
     * @param mixed $value
     */
    public function set( $name, $value )
    {
        if ( $this->has( $name ) ) {
            // this is required because of array values
            // we can have arrays defined there, so we need to remove them first
            // otherwise some subvalues would to remain in the system and as a result, arrays would be merged, not overwriten by set()
            $this->remove( $name );
        }
        $this->setNested( $name, $value );
    }

    /**
     * remove checks even if name is not array
     *
     * @param string $name
     */
    public function remove( $name )
    {
        $value = $this->get( $name );
        if ( is_array( $value ) ) {
            foreach ( $value as $k => $v ) {
                $this->remove( $name . '.' . $k, $v );
            }
        }
        if ( strpos( $name, '.' ) !== FALSE ) {
            $parts = explode( '.', $name );
            $nameTopLevel = reset( $parts );
            array_shift( $parts );
            $topLevelData = $this->removeKeyByAddress( $this->get( $nameTopLevel ), $parts );
            ksort( $topLevelData );
            $this->setNested( $nameTopLevel, $topLevelData );
        }
        parent::remove( $name );
    }

    /**
     * @param array $data
     * @param array $addressParts
     *
     * @return array
     */
    private function removeKeyByAddress( $data, $addressParts )
    {
        $updatedLevel = & $data;
        $i = 1;
        foreach ( $addressParts as $part ) {
            if ( $i === count( $addressParts ) ) {
                unset( $updatedLevel[$part] );
            } else {
                $updatedLevel = & $updatedLevel[$part];
                $i++;
            }
        }
        return $data;
    }

    /**
     * @see set()
     *
     * @param string $name
     * @param mixed $value
     */
    private function setNested( $name, $value )
    {
        if ( is_array( $value ) ) {
            foreach ( $value as $k => $v ) {
                $this->setNested( $name . '.' . $k, $v );
            }
        }
        parent::set( $name, $value );
    }

}
Run Code Online (Sandbox Code Playgroud)

phpunit测试:

<?php

namespace Your\Namespace;

use Symfony\Component\DependencyInjection\Tests\ParameterBag\ParameterBagTest;

/**
 * its essential to use ParameterBagNested as ParameterBag because this way we run even parent class tests upon it
 * parent class is part of Symfony DIC standard test suite and we use it here just for check if our parameter bag is still ok
 */
use SBKS\DependencyInjection\ParameterBag\ParameterBagNested as ParameterBag;

/**
 * testing basic and even added ParameterBag functionality
 */
class ParameterBagNestedTest extends ParameterBagTest
{

    public function testConstructorNested()
    {
        $bag = new ParameterBag(
            array(
                'foo' => array( 'foo1' => 'foo' ),
                'bar' => 'bar',
            )
        );
        $this->assertEquals(
             array(
                 'foo.foo1' => 'foo',
                 'foo' => array(
                     'foo1' => 'foo',
                 ),
                 'bar' => 'bar',
             ),
             $bag->all(),
             '__construct() takes an array of parameters as its first argument'
        );
    }

    public function testRemoveNested()
    {
        $bag = new ParameterBag(
            array(
                'foo' => array(
                    'foo1' => array(
                        'foo11' => 'foo',
                        'foo12' => 'foo',
                    ),
                    'foo2' => 'foo',
                ),
                'bar' => 'bar',
            )
        );

        $bag->remove( 'foo.foo1.foo11' );
        $this->assertEquals(
             array(
                 'foo' => array(
                     'foo1' => array(
                         'foo12' => 'foo',
                     ),
                     'foo2' => 'foo',
                 ),
                 'foo.foo1' => array( 'foo12' => 'foo' ),
                 'foo.foo1.foo12' => 'foo',
                 'foo.foo2' => 'foo',
                 'bar' => 'bar',
             ),
             $bag->all(),
             '->remove() removes a parameter'
        );

        $bag->remove( 'foo' );
        $this->assertEquals(
             array(
                 'bar' => 'bar',
             ),
             $bag->all(),
             '->remove() removes a parameter'
        );
    }

    public function testSetNested()
    {
        $bag = new ParameterBag(
            array(
                'foo' => array(
                    'foo1' => array(
                        'foo11' => 'foo',
                        'foo12' => 'foo',
                    ),
                    'foo2' => 'foo',
                ),
            )
        );
        $bag->set( 'foo', 'foo' );
        $this->assertEquals( array( 'foo' => 'foo' ), $bag->all(), '->set() sets the value of a new parameter' );
    }

    public function testHasNested()
    {
        $bag = new ParameterBag(
            array(
                'foo' => array(
                    'foo1' => array(
                        'foo11' => 'foo',
                        'foo12' => 'foo',
                    ),
                    'foo2' => 'foo',
                ),
            )
        );
        $this->assertTrue( $bag->has( 'foo' ), '->has() returns true if a parameter is defined' );
        $this->assertTrue( $bag->has( 'foo.foo1' ), '->has() returns true if a parameter is defined' );
        $this->assertTrue( $bag->has( 'foo.foo1.foo12' ), '->has() returns true if a parameter is defined' );
        $this->assertTrue( $bag->has( 'foo.foo2' ), '->has() returns true if a parameter is defined' );
    }

}
Run Code Online (Sandbox Code Playgroud)

然后你可以通过将Parameter bag注入到ContainerBuilder中来使用它:

$parameterBag = new \Your\Namespace\ParameterBagNested();
$container = new ContainerBuilder($parameterBag);
Run Code Online (Sandbox Code Playgroud)

多数,您现在可以在Symfony DI容器中使用带点符号的嵌套参数.

如果您在phpstorm中使用Symfony插件,它将使用点表示法自动填充您的嵌套属性.

  • 我不明白 此ParameterBag只是​​允许您在yaml配置中使用嵌套配置的功能.它向后兼容您当前的所有配置.就这样.它没有定义php配置. (2认同)

小智 5

有点晚了,但这里有一个对我有用的解决方案。

# app/config/config.yml
twig:
    globals:
        my_var: category['var']
Run Code Online (Sandbox Code Playgroud)

解释

当您将参数文件导入app/config/config.yml文件时,您可以自动访问参数文件中定义的所有变量。

请记住,当您使用该结构时:

# app/config/parameters.yml
parameters:
    category:
        var: test
Run Code Online (Sandbox Code Playgroud)

您正在定义一个category使用值调用的参数,该值本身又是一个包含var键和test值的键值对数组。

更新

在较新版本的 symfony (3.4 - 4.2) 中,您现在可以执行以下操作:

# app/config/config.yml
twig:
    globals:
       custom_categories: '%categories%'
Run Code Online (Sandbox Code Playgroud)

通过设置此参数:

# app/config/parameters.yml
parameters:
    categories:       
      - category_A
      - category_B
Run Code Online (Sandbox Code Playgroud)

这里要注意的事情: - 在 parameters.yml 类别中是一个数组

所以要在树枝模板中使用它,您应该能够执行以下操作:

<ul>
{% for category in categories %}
    <li> {{ category }} </li>
{% endfor %}
</ul>
Run Code Online (Sandbox Code Playgroud)

另一个对象示例:

# app/config/parameters.yml
parameters:
    marketplace:       
        name: 'My Store'
        address: '...'
Run Code Online (Sandbox Code Playgroud)

配置树枝变量:

# app/config/config.yml
twig:
    globals:
       marketplace: '%marketplace%'
Run Code Online (Sandbox Code Playgroud)

在树枝中使用它:

...
<p>{{marketplace.name}}</p>
<p>{{marketplace.address}}</p>
...
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!:)