123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- Recipes
- =======
-
- Making a Layout conditional
- ---------------------------
-
- Working with Ajax means that the same content is sometimes displayed as is,
- and sometimes decorated with a layout. As Twig layout template names can be
- any valid expression, you can pass a variable that evaluates to ``true`` when
- the request is made via Ajax and choose the layout accordingly:
-
- .. code-block:: jinja
-
- {% extends request.ajax ? "base_ajax.html" : "base.html" %}
-
- {% block content %}
- This is the content to be displayed.
- {% endblock %}
-
- Making an Include dynamic
- -------------------------
-
- When including a template, its name does not need to be a string. For
- instance, the name can depend on the value of a variable:
-
- .. code-block:: jinja
-
- {% include var ~ '_foo.html' %}
-
- If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be
- rendered.
-
- As a matter of fact, the template name can be any valid expression, such as
- the following:
-
- .. code-block:: jinja
-
- {% include var|default('index') ~ '_foo.html' %}
-
- Overriding a Template that also extends itself
- ----------------------------------------------
-
- A template can be customized in two different ways:
-
- * *Inheritance*: A template *extends* a parent template and overrides some
- blocks;
-
- * *Replacement*: If you use the filesystem loader, Twig loads the first
- template it finds in a list of configured directories; a template found in a
- directory *replaces* another one from a directory further in the list.
-
- But how do you combine both: *replace* a template that also extends itself
- (aka a template in a directory further in the list)?
-
- Let's say that your templates are loaded from both ``.../templates/mysite``
- and ``.../templates/default`` in this order. The ``page.twig`` template,
- stored in ``.../templates/default`` reads as follows:
-
- .. code-block:: jinja
-
- {# page.twig #}
- {% extends "layout.twig" %}
-
- {% block content %}
- {% endblock %}
-
- You can replace this template by putting a file with the same name in
- ``.../templates/mysite``. And if you want to extend the original template, you
- might be tempted to write the following:
-
- .. code-block:: jinja
-
- {# page.twig in .../templates/mysite #}
- {% extends "page.twig" %} {# from .../templates/default #}
-
- Of course, this will not work as Twig will always load the template from
- ``.../templates/mysite``.
-
- It turns out it is possible to get this to work, by adding a directory right
- at the end of your template directories, which is the parent of all of the
- other directories: ``.../templates`` in our case. This has the effect of
- making every template file within our system uniquely addressable. Most of the
- time you will use the "normal" paths, but in the special case of wanting to
- extend a template with an overriding version of itself we can reference its
- parent's full, unambiguous template path in the extends tag:
-
- .. code-block:: jinja
-
- {# page.twig in .../templates/mysite #}
- {% extends "default/page.twig" %} {# from .../templates #}
-
- .. note::
-
- This recipe was inspired by the following Django wiki page:
- http://code.djangoproject.com/wiki/ExtendingTemplates
-
- Customizing the Syntax
- ----------------------
-
- Twig allows some syntax customization for the block delimiters. It's not
- recommended to use this feature as templates will be tied with your custom
- syntax. But for specific projects, it can make sense to change the defaults.
-
- To change the block delimiters, you need to create your own lexer object::
-
- $twig = new Twig_Environment();
-
- $lexer = new Twig_Lexer($twig, array(
- 'tag_comment' => array('{#', '#}'),
- 'tag_block' => array('{%', '%}'),
- 'tag_variable' => array('{{', '}}'),
- ));
- $twig->setLexer($lexer);
-
- Here are some configuration example that simulates some other template engines
- syntax::
-
- // Ruby erb syntax
- $lexer = new Twig_Lexer($twig, array(
- 'tag_comment' => array('<%#', '%>'),
- 'tag_block' => array('<%', '%>'),
- 'tag_variable' => array('<%=', '%>'),
- ));
-
- // SGML Comment Syntax
- $lexer = new Twig_Lexer($twig, array(
- 'tag_comment' => array('<!--#', '-->'),
- 'tag_block' => array('<!--', '-->'),
- 'tag_variable' => array('${', '}'),
- ));
-
- // Smarty like
- $lexer = new Twig_Lexer($twig, array(
- 'tag_comment' => array('{*', '*}'),
- 'tag_block' => array('{', '}'),
- 'tag_variable' => array('{$', '}'),
- ));
-
- Using dynamic Object Properties
- -------------------------------
-
- When Twig encounters a variable like ``article.title``, it tries to find a
- ``title`` public property in the ``article`` object.
-
- It also works if the property does not exist but is rather defined dynamically
- thanks to the magic ``__get()`` method; you just need to also implement the
- ``__isset()`` magic method like shown in the following snippet of code::
-
- class Article
- {
- public function __get($name)
- {
- if ('title' == $name)
- {
- return 'The title';
- }
-
- // throw some kind of error
- }
-
- public function __isset($name)
- {
- if ('title' == $name)
- {
- return true;
- }
-
- return false;
- }
- }
-
- Accessing the parent Context in Nested Loops
- --------------------------------------------
-
- Sometimes, when using nested loops, you need to access the parent context. The
- parent context is always accessible via the ``loop.parent`` variable. For
- instance, if you have the following template data::
-
- $data = array(
- 'topics' => array(
- 'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'),
- 'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'),
- ),
- );
-
- And the following template to display all messages in all topics:
-
- .. code-block:: jinja
-
- {% for topic, messages in topics %}
- * {{ loop.index }}: {{ topic }}
- {% for message in messages %}
- - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
- {% endfor %}
- {% endfor %}
-
- The output will be similar to:
-
- .. code-block:: text
-
- * 1: topic1
- - 1.1: The message 1 of topic 1
- - 1.2: The message 2 of topic 1
- * 2: topic2
- - 2.1: The message 1 of topic 2
- - 2.2: The message 2 of topic 2
-
- In the inner loop, the ``loop.parent`` variable is used to access the outer
- context. So, the index of the current ``topic`` defined in the outer for loop
- is accessible via the ``loop.parent.loop.index`` variable.
-
- Defining undefined Functions and Filters on the Fly
- ---------------------------------------------------
-
- When a function (or a filter) is not defined, Twig defaults to throw a
- ``Twig_Error_Syntax`` exception. However, it can also call a `callback`_ (any
- valid PHP callable) which should return a function (or a filter).
-
- For filters, register callbacks with ``registerUndefinedFilterCallback()``.
- For functions, use ``registerUndefinedFunctionCallback()``::
-
- // auto-register all native PHP functions as Twig functions
- // don't try this at home as it's not secure at all!
- $twig->registerUndefinedFunctionCallback(function ($name) {
- if (function_exists($name)) {
- return new Twig_Function_Function($name);
- }
-
- return false;
- });
-
- If the callable is not able to return a valid function (or filter), it must
- return ``false``.
-
- If you register more than one callback, Twig will call them in turn until one
- does not return ``false``.
-
- .. tip::
-
- As the resolution of functions and filters is done during compilation,
- there is no overhead when registering these callbacks.
-
- Validating the Template Syntax
- ------------------------------
-
- When template code is providing by a third-party (through a web interface for
- instance), it might be interesting to validate the template syntax before
- saving it. If the template code is stored in a `$template` variable, here is
- how you can do it::
-
- try {
- $twig->parse($twig->tokenize($template));
-
- // the $template is valid
- } catch (Twig_Error_Syntax $e) {
- // $template contains one or more syntax errors
- }
-
- Refreshing modified Templates when APC is enabled and apc.stat = 0
- ------------------------------------------------------------------
-
- When using APC with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing
- the template cache won't update the APC cache. To get around this, one can
- extend ``Twig_Environment`` and force the update of the APC cache when Twig
- rewrites the cache::
-
- class Twig_Environment_APC extends Twig_Environment
- {
- protected function writeCacheFile($file, $content)
- {
- parent::writeCacheFile($file, $content);
-
- // Compile cached file into bytecode cache
- apc_compile_file($file);
- }
- }
-
- Reusing a stateful Node Visitor
- -------------------------------
-
- When attaching a visitor to a ``Twig_Environment`` instance, Twig uses it to
- visit *all* templates it compiles. If you need to keep some state information
- around, you probably want to reset it when visiting a new template.
-
- This can be easily achieved with the following code::
-
- protected $someTemplateState = array();
-
- public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
- {
- if ($node instanceof Twig_Node_Module) {
- // reset the state as we are entering a new template
- $this->someTemplateState = array();
- }
-
- // ...
-
- return $node;
- }
-
- .. _callback: http://www.php.net/manual/en/function.is-callable.php
|