0

I usually load and prepare data before calling a certain include. That data may be loaded in a certain way for different cases, so I cannot add that data preparation code into the include itself. I have to do it before calling it. Like:

{# Example #}
{% set whatever = entry.whatever.one() %}
{% if whatever %}
    {% include '/_includes/whatever' with {
        'whatever': whatever
    } %}
{% endif %}

Of course there will be more complex situations, this is just an example.

If I move the {% set whatever = entry.whatever.one() %} line into its own include and then do:

{# Example #}
{% include '/_includes/prepare-whatever' %}
{% if whatever %}
    {% include '/_includes/whatever' with {
        'whatever': whatever
    } %}
{% endif %}

The scope stays inside the include, so whatever is not defined when I need it.

How can I achieve the same functionality, to stay DRY?

Thanks!

2 Answers 2

3

As Steve mentioned, there are a few different approaches, but for me I'd reach for embed in this situation. https://twig.symfony.com/doc/3.x/tags/embed.html

By combining with the parent() function, you can effectively prepend or append your per-instance data-prepping chunks of code to the output like this:

{# calling template: #}

{% embed '_includes/whatever' %}
    {% block content %}
        {# prepare some data for this instance here #}
        {% set something = 'aaa' %}

        {# include the shared portion of the `content` block via parent() #}
        {{ parent() }}
    {% endblock %}
{% endembed %}
{# _includes/whatever: #}

{% block content %}
    <h2>Hi there</h2>
    {{ something ?? null }}
{% endblock %}
2
  • Hey look! An option I didn't know existed! Thanks James. Commented Sep 14, 2022 at 17:04
  • Embed is absolutely invaluable, especially with Tailwind or any build that requires a high degree of componentisation. Commented Sep 14, 2022 at 19:26
2

You can't pass a variable from an included template back to the template that called it.

But you still have at least three options (that I can think of) to achieve the kind of reusability that you're looking for.

Option 1

Re-organize your code so that 'prepare-whatever' takes place in your main template, and you then include the contents of your other file which can decide whether or not it even needs to render anything.

index.twig

{% set whatever = entry.whatever.one() ?? null %}
{% include '_includes/whatever' with {
    'whatever': whatever
} %}

_includes/whatever.twig

{% if whatever %}
    Whatever: {{whatever}}
{% endif %}

Option 2

Use the extends functionality to keep important pieces of common code inside a base template, that your index.twig extends from.

index.twig

{% extends '_includes/prepare-things' %}

{% block content %}
    {% include '_includes/whatever' with {
        'whatever': whatever
    } %}
{% endblock %} 

_includes/whatever.twig

{% if whatever %}
    Whatever: {{whatever}}
{% endif %}

_includes/prepare-things

{% set whatever  = entry.whatever.one()  ?? null %}
{% set something = entry.something.one() ?? null %}

{% block content %}{% endblock %}

Option 3

Similar to option 2, except instead of extending a template from you main twig file, you can extend a template from within an included template.

index.twig

{% include '_includes/whatever' %}

_includes/whatever.twig

{% extends 'includes/prepare-whatever' %}

{% block whateverContent %}
    {% if whatever %}
        Whatever: {{whatever}}
    {% endif %}
{% endblock %}

_includes/prepare-whatever.twig

{% set whatever = entry.whatever.one() ?? null %}

{% block whateverContent %}{% endblock %}
1
  • Thanks for this excellent list of alternatives. It's very useful. In my case James answer is the approach I will take, so I'm accepting his answer. But this one should be co-accepted! Commented Sep 14, 2022 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.