17

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

parameters:
    category:
        var: test

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

twig:
    globals:
        my_var: %category.var% # throws ParameterNotFoundException
4

7 回答 7

11

在我见过的所有 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

... 等等。

编辑

我尝试将问题中的嵌套参数粘贴到我的一个项目中的 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);

完全限定参数很好,尝试获取嵌套参数导致InvalidArgumentExceptioncategory.var必须定义参数。我不认为参数可以用嵌套来定义。

于 2013-11-07T15:17:13.877 回答
9
$this->container->getParameter('category')['var']

我已经在 symfony 2.8 上测试过了,它对我有用。

于 2016-12-01T10:01:00.307 回答
6

或者您可以编写简单的 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 );
    }

}

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

}

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

$parameterBag = new \Your\Namespace\ParameterBagNested();
$container = new ContainerBuilder($parameterBag);

就是这样,您现在可以在 Symfony DI 容器中使用带有点符号的嵌套参数。

如果您在 phpstorm 中使用 Symfony 插件,它也会使用点符号自动完成您的嵌套属性。

于 2014-03-16T14:34:27.643 回答
5

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

# app/config/config.yml
twig:
    globals:
        my_var: category['var']

解释

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

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

# app/config/parameters.yml
parameters:
    category:
        var: test

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

更新

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

# app/config/config.yml
twig:
    globals:
       custom_categories: '%categories%'

通过设置此参数:

# app/config/parameters.yml
parameters:
    categories:       
      - category_A
      - category_B

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

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

<ul>
{% for category in categories %}
    <li> {{ category }} </li>
{% endfor %}
</ul>

另一个对象示例:

# app/config/parameters.yml
parameters:
    marketplace:       
        name: 'My Store'
        address: '...'

配置树枝变量:

# app/config/config.yml
twig:
    globals:
       marketplace: '%marketplace%'

在树枝中使用它:

...
<p>{{marketplace.name}}</p>
<p>{{marketplace.address}}</p>
...

希望这可以帮助!:)

于 2017-07-13T23:38:28.107 回答
4

Symfony 的表达式语言语法来救援!试一试:

twig:
    globals:
        my_var: '@=parameter("category")["var"]'
于 2021-03-12T02:15:53.477 回答
1

为了提供一种不需要从 setter 中删除值的替代方法,我重写了 getter 方法。

namespace NameSpaceFor\ParameterBags;

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


class ParameterBagParser extends ParameterBag
{

    /**
     * {@inheritDoc}
     */
    public function get($name, $parent = null)
    {
        if (null === $parent) {
            $parent = $this->parameters;
        }
        $name = strtolower($name);
        if (!array_key_exists($name, $parent)) {
            if (!$name) {
                throw new ParameterNotFoundException($name);
            }
            if (false !== strpos($name, '.')) {
                $parts = explode('.', $name);
                $key = array_shift($parts);
                if (isset($parent[$key])) {
                    return $this->get(implode('.', $parts), $parent[$key]);
                }
            }
            $alternatives = [];
            foreach ($parent as $key => $parameterValue) {
                $lev = levenshtein($name, $key);
                if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) {
                    $alternatives[] = $key;
                }
            }
            throw new ParameterNotFoundException($name, null, null, null, $alternatives);
        }

        return $parent[$name];
    }

}

它递归地遍历名称,直到检查完所有点符号。

所以它将适用于数组和标量值。

配置.yml:

parameters:
   my_param:
     - test

   my_inherited: '%my_param.0%' #test

容器感知:

$container->getParameter('my_param')[0]; //test
于 2016-05-04T21:43:01.847 回答
0

模拟嵌套参数的技巧,在 yaml 文件中访问:

parameters:
  crawler:
    urls:
      a: '%crawler.urls.a%'   # here the root of the rest of the tree/array
  crawler.urls.a:
    cat1:
      - aa
      - bb
    cat2:
      - cc

services:
  xx:
    class:  myclass
    arguments:
      $urls:   '%crawler.urls.a%'

在 Symfony 中,我现在将参数('crawler')作为完整树访问,在服务 xx 中,子树/数组是可访问的。

于 2018-06-25T18:15:57.753 回答