Matthias Andreas Benkard | 12a5735 | 2021-12-28 18:02:04 +0100 | [diff] [blame^] | 1 | ``extends`` |
| 2 | =========== |
| 3 | |
| 4 | The ``extends`` tag can be used to extend a template from another one. |
| 5 | |
| 6 | .. note:: |
| 7 | |
| 8 | Like PHP, Twig does not support multiple inheritance. So you can only have |
| 9 | one extends tag called per rendering. However, Twig supports horizontal |
| 10 | :doc:`reuse<use>`. |
| 11 | |
| 12 | Let's define a base template, ``base.html``, which defines a simple HTML |
| 13 | skeleton document: |
| 14 | |
| 15 | .. code-block:: html+twig |
| 16 | |
| 17 | <!DOCTYPE html> |
| 18 | <html> |
| 19 | <head> |
| 20 | {% block head %} |
| 21 | <link rel="stylesheet" href="style.css"/> |
| 22 | <title>{% block title %}{% endblock %} - My Webpage</title> |
| 23 | {% endblock %} |
| 24 | </head> |
| 25 | <body> |
| 26 | <div id="content">{% block content %}{% endblock %}</div> |
| 27 | <div id="footer"> |
| 28 | {% block footer %} |
| 29 | © Copyright 2011 by <a href="http://domain.invalid/">you</a>. |
| 30 | {% endblock %} |
| 31 | </div> |
| 32 | </body> |
| 33 | </html> |
| 34 | |
| 35 | In this example, the :doc:`block<block>` tags define four blocks that child |
| 36 | templates can fill in. |
| 37 | |
| 38 | All the ``block`` tag does is to tell the template engine that a child |
| 39 | template may override those portions of the template. |
| 40 | |
| 41 | Child Template |
| 42 | -------------- |
| 43 | |
| 44 | A child template might look like this: |
| 45 | |
| 46 | .. code-block:: html+twig |
| 47 | |
| 48 | {% extends "base.html" %} |
| 49 | |
| 50 | {% block title %}Index{% endblock %} |
| 51 | {% block head %} |
| 52 | {{ parent() }} |
| 53 | <style type="text/css"> |
| 54 | .important { color: #336699; } |
| 55 | </style> |
| 56 | {% endblock %} |
| 57 | {% block content %} |
| 58 | <h1>Index</h1> |
| 59 | <p class="important"> |
| 60 | Welcome on my awesome homepage. |
| 61 | </p> |
| 62 | {% endblock %} |
| 63 | |
| 64 | The ``extends`` tag is the key here. It tells the template engine that this |
| 65 | template "extends" another template. When the template system evaluates this |
| 66 | template, first it locates the parent. The extends tag should be the first tag |
| 67 | in the template. |
| 68 | |
| 69 | Note that since the child template doesn't define the ``footer`` block, the |
| 70 | value from the parent template is used instead. |
| 71 | |
| 72 | You can't define multiple ``block`` tags with the same name in the same |
| 73 | template. This limitation exists because a block tag works in "both" |
| 74 | directions. That is, a block tag doesn't just provide a hole to fill - it also |
| 75 | defines the content that fills the hole in the *parent*. If there were two |
| 76 | similarly-named ``block`` tags in a template, that template's parent wouldn't |
| 77 | know which one of the blocks' content to use. |
| 78 | |
| 79 | If you want to print a block multiple times you can however use the |
| 80 | ``block`` function: |
| 81 | |
| 82 | .. code-block:: html+twig |
| 83 | |
| 84 | <title>{% block title %}{% endblock %}</title> |
| 85 | <h1>{{ block('title') }}</h1> |
| 86 | {% block body %}{% endblock %} |
| 87 | |
| 88 | Parent Blocks |
| 89 | ------------- |
| 90 | |
| 91 | It's possible to render the contents of the parent block by using the |
| 92 | :doc:`parent<../functions/parent>` function. This gives back the results of |
| 93 | the parent block: |
| 94 | |
| 95 | .. code-block:: html+twig |
| 96 | |
| 97 | {% block sidebar %} |
| 98 | <h3>Table Of Contents</h3> |
| 99 | ... |
| 100 | {{ parent() }} |
| 101 | {% endblock %} |
| 102 | |
| 103 | Named Block End-Tags |
| 104 | -------------------- |
| 105 | |
| 106 | Twig allows you to put the name of the block after the end tag for better |
| 107 | readability (the name after the ``endblock`` word must match the block name): |
| 108 | |
| 109 | .. code-block:: twig |
| 110 | |
| 111 | {% block sidebar %} |
| 112 | {% block inner_sidebar %} |
| 113 | ... |
| 114 | {% endblock inner_sidebar %} |
| 115 | {% endblock sidebar %} |
| 116 | |
| 117 | Block Nesting and Scope |
| 118 | ----------------------- |
| 119 | |
| 120 | Blocks can be nested for more complex layouts. Per default, blocks have access |
| 121 | to variables from outer scopes: |
| 122 | |
| 123 | .. code-block:: html+twig |
| 124 | |
| 125 | {% for item in seq %} |
| 126 | <li>{% block loop_item %}{{ item }}{% endblock %}</li> |
| 127 | {% endfor %} |
| 128 | |
| 129 | Block Shortcuts |
| 130 | --------------- |
| 131 | |
| 132 | For blocks with little content, it's possible to use a shortcut syntax. The |
| 133 | following constructs do the same thing: |
| 134 | |
| 135 | .. code-block:: twig |
| 136 | |
| 137 | {% block title %} |
| 138 | {{ page_title|title }} |
| 139 | {% endblock %} |
| 140 | |
| 141 | .. code-block:: twig |
| 142 | |
| 143 | {% block title page_title|title %} |
| 144 | |
| 145 | Dynamic Inheritance |
| 146 | ------------------- |
| 147 | |
| 148 | Twig supports dynamic inheritance by using a variable as the base template: |
| 149 | |
| 150 | .. code-block:: twig |
| 151 | |
| 152 | {% extends some_var %} |
| 153 | |
| 154 | If the variable evaluates to a ``\Twig\Template`` or a ``\Twig\TemplateWrapper`` |
| 155 | instance, Twig will use it as the parent template:: |
| 156 | |
| 157 | // {% extends layout %} |
| 158 | |
| 159 | $layout = $twig->load('some_layout_template.twig'); |
| 160 | |
| 161 | $twig->display('template.twig', ['layout' => $layout]); |
| 162 | |
| 163 | You can also provide a list of templates that are checked for existence. The |
| 164 | first template that exists will be used as a parent: |
| 165 | |
| 166 | .. code-block:: twig |
| 167 | |
| 168 | {% extends ['layout.html', 'base_layout.html'] %} |
| 169 | |
| 170 | Conditional Inheritance |
| 171 | ----------------------- |
| 172 | |
| 173 | As the template name for the parent can be any valid Twig expression, it's |
| 174 | possible to make the inheritance mechanism conditional: |
| 175 | |
| 176 | .. code-block:: twig |
| 177 | |
| 178 | {% extends standalone ? "minimum.html" : "base.html" %} |
| 179 | |
| 180 | In this example, the template will extend the "minimum.html" layout template |
| 181 | if the ``standalone`` variable evaluates to ``true``, and "base.html" |
| 182 | otherwise. |
| 183 | |
| 184 | How do blocks work? |
| 185 | ------------------- |
| 186 | |
| 187 | A block provides a way to change how a certain part of a template is rendered |
| 188 | but it does not interfere in any way with the logic around it. |
| 189 | |
| 190 | Let's take the following example to illustrate how a block works and more |
| 191 | importantly, how it does not work: |
| 192 | |
| 193 | .. code-block:: html+twig |
| 194 | |
| 195 | {# base.twig #} |
| 196 | {% for post in posts %} |
| 197 | {% block post %} |
| 198 | <h1>{{ post.title }}</h1> |
| 199 | <p>{{ post.body }}</p> |
| 200 | {% endblock %} |
| 201 | {% endfor %} |
| 202 | |
| 203 | If you render this template, the result would be exactly the same with or |
| 204 | without the ``block`` tag. The ``block`` inside the ``for`` loop is just a way |
| 205 | to make it overridable by a child template: |
| 206 | |
| 207 | .. code-block:: html+twig |
| 208 | |
| 209 | {# child.twig #} |
| 210 | {% extends "base.twig" %} |
| 211 | |
| 212 | {% block post %} |
| 213 | <article> |
| 214 | <header>{{ post.title }}</header> |
| 215 | <section>{{ post.text }}</section> |
| 216 | </article> |
| 217 | {% endblock %} |
| 218 | |
| 219 | Now, when rendering the child template, the loop is going to use the block |
| 220 | defined in the child template instead of the one defined in the base one; the |
| 221 | executed template is then equivalent to the following one: |
| 222 | |
| 223 | .. code-block:: html+twig |
| 224 | |
| 225 | {% for post in posts %} |
| 226 | <article> |
| 227 | <header>{{ post.title }}</header> |
| 228 | <section>{{ post.text }}</section> |
| 229 | </article> |
| 230 | {% endfor %} |
| 231 | |
| 232 | Let's take another example: a block included within an ``if`` statement: |
| 233 | |
| 234 | .. code-block:: html+twig |
| 235 | |
| 236 | {% if posts is empty %} |
| 237 | {% block head %} |
| 238 | {{ parent() }} |
| 239 | |
| 240 | <meta name="robots" content="noindex, follow"> |
| 241 | {% endblock head %} |
| 242 | {% endif %} |
| 243 | |
| 244 | Contrary to what you might think, this template does not define a block |
| 245 | conditionally; it just makes overridable by a child template the output of |
| 246 | what will be rendered when the condition is ``true``. |
| 247 | |
| 248 | If you want the output to be displayed conditionally, use the following |
| 249 | instead: |
| 250 | |
| 251 | .. code-block:: html+twig |
| 252 | |
| 253 | {% block head %} |
| 254 | {{ parent() }} |
| 255 | |
| 256 | {% if posts is empty %} |
| 257 | <meta name="robots" content="noindex, follow"> |
| 258 | {% endif %} |
| 259 | {% endblock head %} |
| 260 | |
| 261 | .. seealso:: |
| 262 | |
| 263 | :doc:`block<../functions/block>`, :doc:`block<../tags/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>` |