Set variable in parent scope in Twig

2019-03-15 21:21发布

问题:

In Smarty you can do

{$var = 'bla' scope=parent}

Is it possible in Twig?

Don't suggest to use blocks. I know. I need variable.

回答1:

base.twig

<title>{{ title|default('example.com') }} - My cool site</title>

child.twig

{% set title = 'ChildTitle' %}


回答2:

If you don't want to use the default() filter (i.e., when you want to use the variable multiple times throughout your parent and child templates), you can actually define a block that contains your entire page in the parent template, and then nest your other blocks inside of that:

{# base.twig #}

{# Default page properties.  You can override these in the `page` block of your child templates. #}
{% set page = page | default({}) | merge({
    "title"       : "My Default Title",
    "description" : "Default description"
}) %}

{% block page %}
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta name="description" content="{{ page.description }}"> 
            <title>{{ page.title }}</title>

            ...

        </head>
        <body>
            {% block content %}{% endblock %}
        </body>
    </html>
{% endblock %}

You can then override the page variable in the page block in your child template, by setting the value and then calling parent():

{# child.twig #}

{% extends "base.twig" %}

{% block page %}
    {# By putting this in a special block, we ensure that it will be set AFTER the default values are set in the parent template, 
    but BEFORE the page itself is rendered. #}

    {% set page = page | merge({
        "title"       : "Child Page",
        "description" : "Welcome to the child page!"
    }) %}    

    {{ parent() }}
{% endblock %}

{% block content %}
    ...
{% endblock %}

Note that in the parent template, we define the page variable outside of the page block, while in the child template we define it inside the page block.

So, Twig will do the following:

  1. When it renders child.twig, it will start from the top of base.twig, setting the default values for the page variable.
  2. When it comes to the page block, it will see that child.twig overrides that block. So, it will run the page block in child.twig instead.
  3. Inside the page block in child.twig, it will set the new values for the page variable. It will then call parent(), which tells it to go back to base.twig and render the parent page block.
  4. It will then continue to render the page, substituting any additional blocks as defined in child.twig (in my example, it will render the content block).

See a working example here. Do be aware that this can become more complicated when you start adding multiple layers of inheritance (e.g., grandchild templates).



回答3:

@n3xus gave a nice answer, it actually helped me also (thanks), but you may also want to take a look at this page from the documentation: Twig docs

One particularly nice feature is the ability to set a chunk of text/html:

{% set title %}
    <i class="icon-user"></i>
    {{ user.username | capitalize }}
    <small>{{ user.email | lower }}</small>
{% endset %}

Makes it easy to generate very specific bits of content from the child templates.



回答4:

If you simply want a variable to be 'overridable' from a subtemplate you can set your variable in the parent like so:

{% set title = (title|default('My Page')) %}

So you could set up a situation like this...

base.twig

<html>
  <head>
    <title>{{title}}</title>
  </head>
  <body>
    {% block content %}
    {% end block %}
  </body>
</html>

parent.twig

{% extends 'base.twig' %}

{% set title = (title|default('My Page')) %}

child.twig

{% extends 'parent.twig' %} 

{% set title='My Subpage' %}   

{% block content %}
  This is the Sub-page.
{% end block %}

The end result will be:

<html>
  <head>
    <title>My Subpage</title>
  </head>
  <body>
    This is the Sub-page.
  </body>
</html>

I think that works nicely for most situations. Rather than forcing the parent var to be overridden by the child, the parent 'lets' the variable be overridden by children. You could also define different behaviour in the parent such as concatenation rather than overriding.



回答5:

You can use the following in your child pages:

{% set title = 'your desired title' %}


回答6:

A great way to do something like this i just discovered:

parent.twig

{% set subvar = block('subvar') %}

{# somewhere later #}

{{ subvar }}

child.twig

{% block subvar %}
    anything you want
{% endblock %}

if your child template does not define the block subvar, the variable in parent.twig will be empty.



回答7:

One other approach is to see twig configuration globally in your application. For example in Silex:

$app['twig']->addGlobal('someuser', $user);

You can then access that variable in all templates:

Hello, {{someuser.name}}


回答8:

Use set tag with embed

Example:

parent.twig:

{%  block child %}

{% endblock %}

I say: {{ var }}

child.twig:

 {% embed "parent.twig" %}
     {% block child %}
 I child, but i not to fail set    
         {% endblock %}
    {%  set var='bla' %}

 {% endembed %}