如何使用动态变量设置变量名称?

时间:2018-01-30 13:17:56

标签: twig

我正在尝试使用动态名称设置变量。我正在使用的代码是:

{% for i in 0..2 %}
    {% set foo~i    = 'array'.'~i~'.'getfoo' %}
    {% set bar~i    = 'array'.'~i~'.'getbar' %}
{% endfor %}

我想要的变量是:
foo0
BAR0
foo1
BAR1
foo2的
bar2

但我收到此错误Unexpected token "operator" of value "~" ("end of statement block" expected)

此外,我不希望这些变量成为数组。

1 个答案:

答案 0 :(得分:1)

就像@DarkBee提到的那样,你不能在vanilla Twig中这样做。但是你可以创建一个非常简单的扩展 - 注意$context需要通过引用传递:

class MyTwigExtension extends Twig_Extension {
    public function getFunctions() {
        return [
            new Twig_Function('set', [$this, 'set'], ['needs_context' => true]),
        ];
    }

    public function set(&$context, $name, $value) {
        $context[$name] = $value;
    }
}
$twig->addExtension(new MyTwigExtension());

然后在Twig你可以这样做:

{{ dump() }}
{% do set('foo' ~ 1, 'bar') %}
{{ dump() }}

以上将打印:

array(0) {
}

array(1) {
  ["foo1"]=>
  string(3) "bar"
}

但请注意,for循环有自己的上下文。所以,如果你这样做:

{% set foo = 'bar' %}

Before loop:
{{ dump() }}

{% for i in 0..2 %}
    {%- do set('foo' ~ i, 'iteration ' ~ i) %}
    {%- if loop.last %}
        {{- 'Inside loop (last iteration):\n' }}
        {{- loop.last ? dump() }}
    {% endif %}
{% endfor %}

After loop:
{{ dump() }}

你得到了这个 - 注意代表循环之外的“父”上下文的_parent数组:

Before loop:
array(1) {
  ["foo"]=>
  string(3) "bar"
}


Inside loop (last iteration):
array(9) {
  ["foo"]=>
  string(3) "bar"
  ["_parent"]=>
  array(1) {
    ["foo"]=>
    string(3) "bar"
  }
  ["_seq"]=>
  array(3) {
    [0]=>
    int(0)
    [1]=>
    int(1)
    [2]=>
    int(2)
  }
  ["loop"]=>
  array(8) {
    ["parent"]=>
    array(1) {
      ["foo"]=>
      string(3) "bar"
    }
    ["index0"]=>
    int(2)
    ["index"]=>
    int(3)
    ["first"]=>
    bool(false)
    ["revindex0"]=>
    int(0)
    ["revindex"]=>
    int(1)
    ["length"]=>
    int(3)
    ["last"]=>
    bool(true)
  }
  ["i"]=>
  int(2)
  ["_key"]=>
  int(2)
  ["foo0"]=>
  string(11) "iteration 0"
  ["foo1"]=>
  string(11) "iteration 1"
  ["foo2"]=>
  string(11) "iteration 2"
}


After loop:
array(1) {
  ["foo"]=>
  string(3) "bar"
}

您可以通过三种方式克服此限制。首先是在for循环之前初始化变量(注意foo0保留为null,因为循环从1开始,foo3赢了'因为它尚未初始化,所以在全局范围内:

{% set foo0 = null %}
{% set foo1 = null %}
{% set foo2 = null %}

{% for i in 1..3 %}
    {% do set('foo' ~ i, 'iteration ' ~ i) %}
{% endfor %}

{{ dump() }}

以上将打印:

array(3) {
  ["foo0"]=>
  NULL
  ["foo1"]=>
  string(11) "iteration 1"
  ["foo2"]=>
  string(11) "iteration 2"
}

第二种方法是修改扩展程序的set方法,以检查$context是否包含密钥_parent

public function set(&$context, $name, $value) {
    $context[$name] = $value;

    if (array_key_exists('_parent', $context)) {
        $this->set($context['_parent'], $name, $value);
    }
}

然后即使是嵌套的for循环也不是问题:

{% for i in 1..2 %}
    {% for j in 3..4 %}
        {% do set('foo' ~ i ~ j, i ~ ' and ' ~ j) %}
    {% endfor %}
{% endfor %}

{{ dump() }}

以上将打印:

array(4) {
  ["foo13"]=>
  string(7) "1 and 3"
  ["foo14"]=>
  string(7) "1 and 4"
  ["foo23"]=>
  string(7) "2 and 3"
  ["foo24"]=>
  string(7) "2 and 4"
}

第三种方法是保持扩展程序的set方法不变并创建一个新方法,例如set_global

class MyTwigExtension extends Twig_Extension {
    public function getFunctions() {
        return [
            new Twig_Function('set',        [$this, 'set'],        ['needs_context' => true]),
            new Twig_Function('set_global', [$this, 'set_global'], ['needs_context' => true]),
        ];
    }

    public function set(&$context, $name, $value) {
        $context[$name] = $value;
    }

    public function set_global(&$context, $name, $value) {
        $context[$name] = $value;

        if (array_key_exists('_parent', $context)) {
            return $this->set_global($context['_parent'], $name, $value);
        }
    }
}
$twig->addExtension(new MyTwigExtension());

然后你可以使用set在当前上下文中设置变量(例如在for循环的上下文中)或set_global来设置“全局”变量(在上下文中)文件)。您可以在for循环中使用这两种方法为已初始化的变量设置新值。