8

I'm essentially looking for a way to have a template that runs through a bunch of sets that I might use throughout my templates.

The problem is, if I include in a template with an extends, it dies up with:

A template that extends another one cannot have a body.

{% include '/_resources/variables.twig' %}
{% extends '/_layouts/default.twig' %}

Some research indicates that maybe this isn't easily done with Twig parsing alone: https://stackoverflow.com/questions/20410194/use-twig-custom-set-variables-from-an-include https://groups.google.com/forum/#!topic/twig-users/QKdpWAxLdjE

So, what would be the best way to achieve this? My initial thought would be a plugin that you gave the path of a template, which preloaded things into variables.

I realize some variables (strings) could be done via macros. But, for example, I have a set usStates = {...} that is a big hash of state codes and names.

14

There are actually two issues with this approach. The first, and the direct reason you’re getting that Twig error, is that templates that extend another template can only output things within {% block %} tags. Any HTML that gets output outside of those tags (either directly or via {{ output tags }}) will result in that Twig error.

The {% include %} tag is in the same boat. Its only purpose (as far as the parent template is concerned) is to output the results of a child template. So like {{ output tags }}, you can’t place it outside of {% block %} tags. If you really want to include a template within these nested templates, you will need to move it to within your {% block %}.

Even if you did that, though, you’d then run into the second issue: Any variables that are set/modified within an included template will not bubble back up to the parent template. For example, if you had these two templates:

  • parent template:

    {% include "_inc" %}
    {{ foo }}
    
  • child template (_inc.html):

    {% set foo = 'bar' %}
    

That foo variable would actually be discarded as soon as Twig was finished rendering _inc.html; nothing that happens in there will have any effect on the parent template. (As I said, the only purpose of the {% include %} tag as far as the parent template is concerned is to output the rendering result of the included template.)

However, it’s still probably possible to achieve what you want when you consider the order in which these templates are going to be processed:

  1. Everything besides the {% block %} tags in the requested template (the one with the {% extends %} tag)
  2. The layout template (_layouts/default.twig), including its {% block %} tags
  3. The requested template’s {% block %} tags

Knowing that, you can probably just set your variables you’re trying to set from within _layouts/default.twig, and they will be available within the requested template’s {% block %} tags.

  • requested template:

    {% extends '_layouts/default.twig' %}
    
    {% block content  %}
        {{ foo }}
    {% endblock %}
    
  • layout template (_layouts/default.twig):

    {# Set the variables you were trying to set in _resources/variables.twig #}
    {% set foo = 'bar' %}
    
    {% block content %}
    {% endblock %}
    
  • You could wrap the whole page in a single block (for example named "HTML") in _layouts/default.twig and extend the variables template using {% extends '_resources/variables.twig' %}. This allows you to separate variables and layout HTML. – carlcs Dec 18 '14 at 9:22
  • Brandon, that solution doesn't work for me when trying to set variables from an entry. I have a need to set meta data from the current entry on ALL pages, so I've tried this layout: gist.github.com/russback/230310fe4c8f849958bb. However the data doesn't get rendered. Am I missing something? In my partials/_global_header.html template I am attempting the output those variables. @carics, I don't follow your suggestion - do you have an example? – Russ Back Dec 24 '14 at 13:21
  • @RussBack That’s not quite the same question. Can you please post a new one? – Brandon Kelly Dec 24 '14 at 15:25
  • Brandon, thanks. Done at craftcms.stackexchange.com/questions/5400/…. – Russ Back Dec 24 '14 at 21:09
3

Another option would be to create a small plugin with a variables file dedicated to storing this data.

class MyPluginVariable {

    public function exampleVariable($optional = null)
    {
        return "Return anything you want.";
    }

}

Then in your template, you can just use:

{{ craft.myPlugin.exampleVariable }}
2

It looks like another good solution is to use a template hook. I just became aware of these, but they look very useful for this type of thing.

1

Use macros in your variables.html:

{% macro myMacro(param1, param2) %}
  {{param1}} / {{param2}}
{% endmacro %}`

Put this in your layout.html:

{% import 'folder/to/variables' as helpers %}

and then in your partials:

{{helpers.myMacro(param1, param2)}}

1

Using macro command you can replace the set command, avoiding the disadvantage of long code every time you need to call a variable. I had the same need, and my solution is:

Parent template "main.twig.html":

{% import "constants.twig" as constants %}
var priceA = {{ constants.priceA }}

Child template "constants.twig":

{% macro priceA () %}   15.10   {% endmacro %}
{% macro priceB () %}   26.20   {% endmacro %}
{% macro priceC () %}   30.40   {% endmacro %}

This is nice and simple to read AND short to write whenever you need to call a variable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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