Using the twig variable to dynamically invoke an imported macro subfunction

I am trying to use a variable to call a specific macro name.

I have a macro file that is imported

{% import 'form-elements.html.twig' as forms %} 

Now in this file there are all macros of form elements: text, text field, selection, radio, etc.

I have an array variable that is passed in that contains elements in it:

 $elements = array( array( 'type'=>'text, 'value'=>'some value', 'atts'=>null, ), array( 'type'=>'text, 'value'=>'some other value', 'atts'=>null, ), ); {{ elements }} 

what I'm trying to do is generate these elements from macros. they work fine when called by name:

 {{ forms.text(element.0.name,element.0.value,element.0.atts) }} 

However, what I want to do is something like this:

 {% for element in elements %} {{ forms[element.type](element.name,element.value,element.atts) }} {% endfor %} 

I tried the following, which led to the same error:

 {{ forms["'"..element.type.."'"](element.name,element.value,element.atts) }} {{ forms.(element.type)(element.name,element.value,element.atts) }} {{ forms.{element.type}(element.name,element.value,element.atts) }} 

This unfortunately causes the following error:

  Fatal error: Uncaught exception 'LogicException' with message 'Attribute "value" does not exist for Node "Twig_Node_Expression_GetAttr".' in Twig\Environment.php on line 541 

Any help or advice on a solution or a better scheme to use would be very helpful.

+8
twig
source share
2 answers

I just thought that other people might need an answer to this question as provided by fabpot:

This is really something that is not supported: calling a macro with a dynamic name (I added the correct exception to be more clear on this issue).

If you really want to do this, you can do it with the following code:

{{attribute (forms, element.type, [element.name, element.value, element.atts])}}

-fabpot

https://github.com/twigphp/Twig/issues/922#issuecomment-11133299

+15
source share

Dynamic macros may not be supported in Twig.

But there is a simple workaround as you can dynamically include other templates.

Example:
Suppose you have several content modules or blocks of content (or whatever you want to call them) for your site. And you have Twig macros responsible for rendering each of these modules.

 {# modules.twig #} {% macro module1(config) %} <div>module one</div> {% endmacro %} {% macro module2(config) %} <div>module two</div> {% endmacro %} {% macro module3(config) %} <div>module three</div> {% endmacro %} 

Now, what you need to dynamically call these macros is to add an additional template for each, for example like this:

 {# module1.twig #} {% import "modules.twig" as modules %} {{ modules.module1(config) }} 
 {# module2.twig #} {% import "modules.twig" as modules %} {{ modules.module2(config) }} 
 {# module3.twig #} {% import "modules.twig" as modules %} {{ modules.module3(config) }} 

Finally, in your actual page template, you simply include the template instead of calling a macro.

 {# template.twig #} {# this is the macro name to be called #} {% set macro = 'module2' %} {# this is just a config object to be passed to the macro #} {% set config = {} %} {% include macro ~ '.twig' with { config: config } only %} 

Et voilรก, (dynamically generated) output will be <div>module two</div> .

0
source share

All Articles