recipes.rst 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. Recipes
  2. =======
  3. Making a Layout conditional
  4. ---------------------------
  5. Working with Ajax means that the same content is sometimes displayed as is,
  6. and sometimes decorated with a layout. As Twig layout template names can be
  7. any valid expression, you can pass a variable that evaluates to ``true`` when
  8. the request is made via Ajax and choose the layout accordingly:
  9. .. code-block:: jinja
  10. {% extends request.ajax ? "base_ajax.html" : "base.html" %}
  11. {% block content %}
  12. This is the content to be displayed.
  13. {% endblock %}
  14. Making an Include dynamic
  15. -------------------------
  16. When including a template, its name does not need to be a string. For
  17. instance, the name can depend on the value of a variable:
  18. .. code-block:: jinja
  19. {% include var ~ '_foo.html' %}
  20. If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be
  21. rendered.
  22. As a matter of fact, the template name can be any valid expression, such as
  23. the following:
  24. .. code-block:: jinja
  25. {% include var|default('index') ~ '_foo.html' %}
  26. Overriding a Template that also extends itself
  27. ----------------------------------------------
  28. A template can be customized in two different ways:
  29. * *Inheritance*: A template *extends* a parent template and overrides some
  30. blocks;
  31. * *Replacement*: If you use the filesystem loader, Twig loads the first
  32. template it finds in a list of configured directories; a template found in a
  33. directory *replaces* another one from a directory further in the list.
  34. But how do you combine both: *replace* a template that also extends itself
  35. (aka a template in a directory further in the list)?
  36. Let's say that your templates are loaded from both ``.../templates/mysite``
  37. and ``.../templates/default`` in this order. The ``page.twig`` template,
  38. stored in ``.../templates/default`` reads as follows:
  39. .. code-block:: jinja
  40. {# page.twig #}
  41. {% extends "layout.twig" %}
  42. {% block content %}
  43. {% endblock %}
  44. You can replace this template by putting a file with the same name in
  45. ``.../templates/mysite``. And if you want to extend the original template, you
  46. might be tempted to write the following:
  47. .. code-block:: jinja
  48. {# page.twig in .../templates/mysite #}
  49. {% extends "page.twig" %} {# from .../templates/default #}
  50. Of course, this will not work as Twig will always load the template from
  51. ``.../templates/mysite``.
  52. It turns out it is possible to get this to work, by adding a directory right
  53. at the end of your template directories, which is the parent of all of the
  54. other directories: ``.../templates`` in our case. This has the effect of
  55. making every template file within our system uniquely addressable. Most of the
  56. time you will use the "normal" paths, but in the special case of wanting to
  57. extend a template with an overriding version of itself we can reference its
  58. parent's full, unambiguous template path in the extends tag:
  59. .. code-block:: jinja
  60. {# page.twig in .../templates/mysite #}
  61. {% extends "default/page.twig" %} {# from .../templates #}
  62. .. note::
  63. This recipe was inspired by the following Django wiki page:
  64. http://code.djangoproject.com/wiki/ExtendingTemplates
  65. Customizing the Syntax
  66. ----------------------
  67. Twig allows some syntax customization for the block delimiters. It's not
  68. recommended to use this feature as templates will be tied with your custom
  69. syntax. But for specific projects, it can make sense to change the defaults.
  70. To change the block delimiters, you need to create your own lexer object::
  71. $twig = new Twig_Environment();
  72. $lexer = new Twig_Lexer($twig, array(
  73. 'tag_comment' => array('{#', '#}'),
  74. 'tag_block' => array('{%', '%}'),
  75. 'tag_variable' => array('{{', '}}'),
  76. ));
  77. $twig->setLexer($lexer);
  78. Here are some configuration example that simulates some other template engines
  79. syntax::
  80. // Ruby erb syntax
  81. $lexer = new Twig_Lexer($twig, array(
  82. 'tag_comment' => array('<%#', '%>'),
  83. 'tag_block' => array('<%', '%>'),
  84. 'tag_variable' => array('<%=', '%>'),
  85. ));
  86. // SGML Comment Syntax
  87. $lexer = new Twig_Lexer($twig, array(
  88. 'tag_comment' => array('<!--#', '-->'),
  89. 'tag_block' => array('<!--', '-->'),
  90. 'tag_variable' => array('${', '}'),
  91. ));
  92. // Smarty like
  93. $lexer = new Twig_Lexer($twig, array(
  94. 'tag_comment' => array('{*', '*}'),
  95. 'tag_block' => array('{', '}'),
  96. 'tag_variable' => array('{$', '}'),
  97. ));
  98. Using dynamic Object Properties
  99. -------------------------------
  100. When Twig encounters a variable like ``article.title``, it tries to find a
  101. ``title`` public property in the ``article`` object.
  102. It also works if the property does not exist but is rather defined dynamically
  103. thanks to the magic ``__get()`` method; you just need to also implement the
  104. ``__isset()`` magic method like shown in the following snippet of code::
  105. class Article
  106. {
  107. public function __get($name)
  108. {
  109. if ('title' == $name)
  110. {
  111. return 'The title';
  112. }
  113. // throw some kind of error
  114. }
  115. public function __isset($name)
  116. {
  117. if ('title' == $name)
  118. {
  119. return true;
  120. }
  121. return false;
  122. }
  123. }
  124. Accessing the parent Context in Nested Loops
  125. --------------------------------------------
  126. Sometimes, when using nested loops, you need to access the parent context. The
  127. parent context is always accessible via the ``loop.parent`` variable. For
  128. instance, if you have the following template data::
  129. $data = array(
  130. 'topics' => array(
  131. 'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'),
  132. 'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'),
  133. ),
  134. );
  135. And the following template to display all messages in all topics:
  136. .. code-block:: jinja
  137. {% for topic, messages in topics %}
  138. * {{ loop.index }}: {{ topic }}
  139. {% for message in messages %}
  140. - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
  141. {% endfor %}
  142. {% endfor %}
  143. The output will be similar to:
  144. .. code-block:: text
  145. * 1: topic1
  146. - 1.1: The message 1 of topic 1
  147. - 1.2: The message 2 of topic 1
  148. * 2: topic2
  149. - 2.1: The message 1 of topic 2
  150. - 2.2: The message 2 of topic 2
  151. In the inner loop, the ``loop.parent`` variable is used to access the outer
  152. context. So, the index of the current ``topic`` defined in the outer for loop
  153. is accessible via the ``loop.parent.loop.index`` variable.
  154. Defining undefined Functions and Filters on the Fly
  155. ---------------------------------------------------
  156. When a function (or a filter) is not defined, Twig defaults to throw a
  157. ``Twig_Error_Syntax`` exception. However, it can also call a `callback`_ (any
  158. valid PHP callable) which should return a function (or a filter).
  159. For filters, register callbacks with ``registerUndefinedFilterCallback()``.
  160. For functions, use ``registerUndefinedFunctionCallback()``::
  161. // auto-register all native PHP functions as Twig functions
  162. // don't try this at home as it's not secure at all!
  163. $twig->registerUndefinedFunctionCallback(function ($name) {
  164. if (function_exists($name)) {
  165. return new Twig_Function_Function($name);
  166. }
  167. return false;
  168. });
  169. If the callable is not able to return a valid function (or filter), it must
  170. return ``false``.
  171. If you register more than one callback, Twig will call them in turn until one
  172. does not return ``false``.
  173. .. tip::
  174. As the resolution of functions and filters is done during compilation,
  175. there is no overhead when registering these callbacks.
  176. Validating the Template Syntax
  177. ------------------------------
  178. When template code is providing by a third-party (through a web interface for
  179. instance), it might be interesting to validate the template syntax before
  180. saving it. If the template code is stored in a `$template` variable, here is
  181. how you can do it::
  182. try {
  183. $twig->parse($twig->tokenize($template));
  184. // the $template is valid
  185. } catch (Twig_Error_Syntax $e) {
  186. // $template contains one or more syntax errors
  187. }
  188. Refreshing modified Templates when APC is enabled and apc.stat = 0
  189. ------------------------------------------------------------------
  190. When using APC with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing
  191. the template cache won't update the APC cache. To get around this, one can
  192. extend ``Twig_Environment`` and force the update of the APC cache when Twig
  193. rewrites the cache::
  194. class Twig_Environment_APC extends Twig_Environment
  195. {
  196. protected function writeCacheFile($file, $content)
  197. {
  198. parent::writeCacheFile($file, $content);
  199. // Compile cached file into bytecode cache
  200. apc_compile_file($file);
  201. }
  202. }
  203. Reusing a stateful Node Visitor
  204. -------------------------------
  205. When attaching a visitor to a ``Twig_Environment`` instance, Twig uses it to
  206. visit *all* templates it compiles. If you need to keep some state information
  207. around, you probably want to reset it when visiting a new template.
  208. This can be easily achieved with the following code::
  209. protected $someTemplateState = array();
  210. public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
  211. {
  212. if ($node instanceof Twig_Node_Module) {
  213. // reset the state as we are entering a new template
  214. $this->someTemplateState = array();
  215. }
  216. // ...
  217. return $node;
  218. }
  219. .. _callback: http://www.php.net/manual/en/function.is-callable.php