在树枝上执行封闭

问题描述:

我正试图执行一个位于树枝模板上的数组内的闭包。下面你可以找到一个简单的代码片段,其中我想:在树枝上执行封闭


//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 %} 

当嫩枝渲染模板,抛出一个异常抱怨数组字符串转换

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 » 

我会感谢您的帮助。

谢谢!

您不能直接在您的Twig模板中执行闭包。但是,如果您需要在模板中调用一些PHP,则应该使用create a Twig Extension并将您的逻辑包含在内。

+0

谢谢!但是这个解决方案对我来说不起作用,因为逻辑是可变的 – Carles

+0

你的意思是“逻辑是可变的”? – Terenoth

+0

它可能是许多不同的条件函数,具有不同的条件。我认为将不同条件的分枝扩展放在一起效率不高,因为它会导致模板呈现性能下降。 – Carles

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


一个简单的树枝的扩展,实现这个目的应该是这样的Symfony的2.8+。 (为symfony1.2 4,看new documentation

// 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:

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

没有延伸的树枝,一个办法来解决这个问题是使用一个类作为封闭的包装,并使用Twig的attribute函数,因为它可用于调用对象的方法。

// 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]) }} 

然而,这是一个有点多余的,internally,该Twig的10个函数也解包给定的参数。通过使用上面的代码,对于每次调用,参数都会连续解压缩,打包和解压缩。