Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using twig variable to dynamically call an imported macro sub-function

Tags:

twig

I am attempting if use a variable to call a specific macro name.

I have a macros file that is being imported

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

Now in that file there are all the form element macros: text, textarea, select, radio etc.

I have an array variable that gets passed in that has an elements in it:

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

{{ elements }}

what im trying to do is generate those elements from the macros. they work just 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 have tried the following all resulting in 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 throws 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 schema to use would be very helpful.

like image 276
Chase Avatar asked Dec 04 '12 21:12

Chase


1 Answers

Dynamic macros may not be supported in Twig.

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

Example:
Let's say you have a bunch of content modules or content blocks (or however you wanna call them) for your site. And you have Twig macros responsible of 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 extra template for each, like so:

{# 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 just include the template instead of calling the macro.

{# template.twig #}

{# this is the macro's 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 produced) output will be <div>module two</div>.

like image 73
Arvid Avatar answered Oct 19 '22 23:10

Arvid