* 1.3.0
* allowed empty templates to be used as traits
+ * added traits support for the "parent" function
* 1.2.0 (2011-09-13)
{% block title %}{% endblock %}
{% block content %}{% endblock %}
-Renaming also allows you to simulate inheritance by calling the "parent" block
-(like what you would have done with ``parent()``):
+The ``parent()`` function automatically determines the correct inheritance
+tree, so it can be used when overriding a block defined in an imported
+template:
.. code-block:: jinja
{% extends "base.html" %}
- {% use "blocks.html" with sidebar as parent_sidebar %}
+ {% use "blocks.html" %}
{% block sidebar %}
- {{ block('parent_sidebar') }}
+ {{ parent() }}
{% endblock %}
{% block title %}{% endblock %}
{% block content %}{% endblock %}
+In this example, ``parent()`` will correctly call the ``sidebar`` block from
+the ``blocks.html`` template.
+
.. note::
You can use as many ``use`` statements as you want in any given template.
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line);
}
- if (!$this->parser->getParent()) {
- throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend another one is forbidden', $line);
+ if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
+ throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line);
}
return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
}
$compiler
- ->write("\$this->blocks = array_merge(\n")
+ ->write("\$this->traits = array_merge(\n")
->indent()
;
for ($i = 0; $i < $countTraits; $i++) {
$compiler
- ->write(sprintf("\$_trait_%s_blocks,\n", $i))
+ ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i))
;
}
$compiler
+ ->outdent()
+ ->write(");\n\n")
+ ;
+
+ $compiler
+ ->write("\$this->blocks = array_merge(\n")
+ ->indent()
+ ->write("\$this->traits,\n")
->write("array(\n")
;
} else {
$this->traits[] = $trait;
}
+ public function hasTraits()
+ {
+ return count($this->traits) > 0;
+ }
+
public function addImportedFunction($alias, $name, Twig_Node_Expression $node)
{
$this->importedFunctions[0][$alias] = array('name' => $name, 'node' => $node);
protected $parents;
protected $env;
protected $blocks;
+ protected $traits;
/**
* Constructor.
{
$this->env = $env;
$this->blocks = array();
+ $this->traits = array();
}
/**
*/
public function displayParentBlock($name, array $context, array $blocks = array())
{
- if (false !== $parent = $this->getParent($context)) {
+ if (isset($this->traits[$name])) {
+ $this->traits[$name][0]->displayBlock($name, $context, $blocks);
+ } elseif (false !== $parent = $this->getParent($context)) {
$parent->displayBlock($name, $context, $blocks);
} else {
- throw new Twig_Error_Runtime('This template has no parent', -1, $this->getTemplateName());
+ throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
}
}
--- /dev/null
+--TEST--
+"parent" tag
+--TEMPLATE--
+{% block content %}
+ {{ parent() }}
+{% endblock %}
+--EXCEPTION--
+Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3
--- /dev/null
+--TEST--
+"parent" tag
+--TEMPLATE--
+{% use 'foo.twig' %}
+
+{% block content %}
+ {{ parent() }}
+{% endblock %}
+--TEMPLATE(foo.twig)--
+{% block content %}BAR{% endblock %}
+--DATA--
+return array()
+--EXPECT--
+BAR
--- /dev/null
+--TEST--
+"parent" function
+--TEMPLATE--
+{% extends "parent.twig" %}
+
+{% use "use1.twig" %}
+{% use "use2.twig" %}
+
+{% block content_parent %}
+ {{ parent() }}
+{% endblock %}
+
+{% block content_use1 %}
+ {{ parent() }}
+{% endblock %}
+
+{% block content_use2 %}
+ {{ parent() }}
+{% endblock %}
+
+{% block content %}
+ {{ block('content_use1_only') }}
+ {{ block('content_use2_only') }}
+{% endblock %}
+--TEMPLATE(parent.twig)--
+{% block content_parent 'content_parent' %}
+{% block content_use1 'content_parent' %}
+{% block content_use2 'content_parent' %}
+{% block content '' %}
+--TEMPLATE(use1.twig)--
+{% block content_use1 'content_use1' %}
+{% block content_use2 'content_use1' %}
+{% block content_use1_only 'content_use1_only' %}
+--TEMPLATE(use2.twig)--
+{% block content_use2 'content_use2' %}
+{% block content_use2_only 'content_use2_only' %}
+--DATA--
+return array()
+--EXPECT--
+ content_parent
+ content_use1
+ content_use2
+ content_use1_only
+ content_use2_only