5

我正在尝试执行一个位于 Twig 模板上的数组内的闭包。您可以在下面找到我正在尝试的简化片段:


//Symfony controller
...
$funcs = array(
    "conditional" => function($obj){
        return $obj->getFoo() === $obj::TRUE_FOO
    }
);
$this->render('template_name', array('funcs' => $funcs));

{# Twig template #}
{# obj var is set #}
...
{% if funcs.conditional(obj)%}
<p>Got it</p>
{% endif %}

当 Twig 渲染模板时,抛出一个异常,抱怨数组到字符串的转换

An exception has been thrown during the rendering of a template ("Notice: Array to string conversion") in "template_name.html.twig".
500 Internal Server Error - Twig_Error_Runtime
1 linked Exception: ContextErrorException »

我会感谢你的帮助。

谢谢!

4

3 回答 3

2

如果您使用的是闭包,则可以使用闭包的调用方法

http://php.net/manual/en/closure.call.php

你最终会得到这样的东西

{{ funcs.conditional.call(obj, obj) }}

由于第一个参数必须是“this”将引用的对象,因此我传递的对象与第一个参数相同。

无需 Twig 扩展,也无需额外的 PHP 代码;)

于 2018-11-15T16:14:19.983 回答
1

您不能直接在 Twig 模板中执行闭包。但是,如果您需要在模板中调用一些 PHP,您应该使用创建 Twig 扩展并在其中包含您的逻辑。

于 2016-01-22T12:43:36.117 回答
1

Twig 不允许直接这样做。您可以向 Twig 添加一个简单的函数来处理闭包的执行,或者将您的闭包包装在一个类中以便能够使用 Twig 的属性函数(因为直接调用attribute(_context, 'myclosure', args)将触发致命错误,因为 Twig 将直接返回闭包并忽略因为给定的参数_context是一个数组)。


对于 Symfony 2.8+,实现此目的的简单 Twig 扩展看起来像这样。(对于 Symfony 4,请参阅新文档

// src/AppBundle/Twig/Extension/CoreExtensions.php
namespace AppBundle\Twig\Extension;

class CoreExtensions extends \Twig_Extension
{
    public function getFunctions()
    {
        return [
            new \Twig_SimpleFunction('execute', [$this, 'executeClosure'])
        ];
    }

    public function executeClosure(\Closure $closure, $arguments)
    {
        return $closure(...$arguments);
    }

    public function getName()
    {
        return 'core_extensions_twig_extension';
    }
}

然后,在您的模板中,您只需调用执行:

{{ execute(closure, [argument1, argument2]) }}

在不扩展 Twig 的情况下,解决此问题的一种方法是使用一个充当闭包包装器的类,并使用attributeTwig 的函数,因为它可用于调用对象的方法。

// src/AppBundle/Twig/ClosureWrapper.php
namespace AppBundle\Twig;

/**
 * Wrapper to get around the issue of not being able to use closures in Twig
 * Since it is possible to call a method of a given object in Twig via "attribute",
 * the only purpose of this class is to store the closure and give a method to execute it
 */
class ClosureWrapper
{
    private $closure;

    public function __construct($closure)
    {
        $this->closure = $closure;
    }

    public function execute()
    {
        return ($this->closure)(...func_get_args());
    }
}

然后,你只需要在渲染时给你的模板一个 ClosureWrapper 实例,而不是闭包本身:

use AppBundle\Twig\ClosureWrapper;

class MyController extends Controller
{
    public function myAction()
    {
        $localValue = 2;
        $closure = new ClosureWrapper(function($param1, $param2) use ($localValue) {
            return $localValue + $param1 + $param2;
        });

        return $this->render('mytemplate.html.twig', ['closure' => $closure]);
    }

    ...

最终,在您的模板中,您需要使用attribute来执行您在控制器中定义的闭包:

// Displays 12
{{ attribute(closure, 'execute', [4, 6]) }}

然而,这有点多余,因为在内部attributeTwig 的函数也会解包给定的参数。通过使用上面的代码,对于每次调用,参数都被依次解包、打包和再次解包。

于 2017-12-24T02:04:33.177 回答