Reputation: 1504
How can you add an item to an array within an include
tag when the array is defined in the main file?
Please note this question is distinctly different from other array questions in Twig because I'm trying to modify the value from within an included template in the main file.
For example:
main.twig
{% set includes = ["test1"] %}
{% include "test.twig" %}
{{includes|json_encode}} {# "How do I make this `includes` contain the "test2" array item?" #}
test.twig
{% set includes = includes|merge(["test2"]) %}
{{includes|json_encode}}
Currently the output from main.twig
gives me only the initial test1
item, and not the combination of the two which test.twig
is trying to modify.
I may have to utilize an added function. The goal here is basically to allow include
d templates the ability to add other CSS, JS, etc, in HTML files without having to modify the parent templates.
Here is a Twig Fiddle for you to play with which shows the output.
Please note for other people looking for an answer to this question, what I was attempting to accomplish is possible utilizing a Twig extension with a function that adds items to the array, and a second function to access it. Please view my answer below, that is not marked as correct, to see a way to potentially do this. My question, as stated, is impossible, as shown by DarkBee.
Upvotes: 0
Views: 2525
Reputation: 1504
I found a way to accomplish what I was looking for, however it isn't what was asked in the question, so it won't be marked as the answer, but I'm posting a quick answer here in case anyone else is trying to figure out how to do what I wanted to do.
Specifically, I was trying to include JS and CSS files from within nested Twig templates, and the way I had originally thought to do that was with the merge
tag, but, as shown by DarkBee in the marked answer, this is not possible.
As an alternative I was able to create an extension that allows me to do what I'm looking for globally, with as many different array names as I'd like:
class arrayDefinitionExtension extends \Twig_Extension
{
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('addToArray', [$this, "addToArray"]),
new \Twig_SimpleFunction('getArray', [$this, "getArray"]),
);
}
private $arrays = [];
public function addToArray($name, $val) {
$this->arrays[$name][] = $text;
return "";
}
public function getArray($name) {
if(isset($this->arrays[$name])) {
return $this->arrays[$name];
} else {
return [];
}
}
}
And for usage:
base.php
$loader = new \Twig_Loader_Filesystem('/path/to/templates');
$twig = new \Twig_Environment($loader);
$twig->addExtension(new \arrayDefinitionExtension());
base.twig
{% block content %}
{% endblock %}
{% set includes = getArray("includes") %}
{% for include in includes %}
<script src="{{include}}" type="application/javascript"></script>
{% endfor %}
main.twig
{% extends "base.twig" %}
{% block content %}
{% include "test.twig" %}
{% endblock %}
test.twig
{% do addToArray("includes", "path/to/some/file.js") %}
Note that this is sequential, so if you had the
content
block placed after the print of the<script>
tags then there won't be any output.
Upvotes: 0
Reputation: 15639
This can't be done in Twig
, as seen in the compiled source of the templates each included templats get his own private scope as the context array is based by value and not by reference
$this->loadTemplate("bar.twig", "main.twig", 2)->display($context);
which then calls doDisplay
in the included template
protected function doDisplay(array $context, array $blocks = array())
note
You could alter this behaviour by creating your own Twig_Environment
and Twig_Template
but a quick look shows me that there are quite some functions you'd need to override.
main.twig
{% set foo = 'bar' %}
{% include 'bar.twig' %}
Foo in foo: {{ foo }}
bar.twig
{% set foo = 'foo' %}
Foo in bar: {{ foo }}
Source main.twig
/* main.twig */
class __TwigTemplate_4ba23e628289532331bb5889dca1a4ec57774924d21a760ca6fe6f575a3978b5 extends Twig_Template
{
public function __construct(Twig_Environment $env)
{
parent::__construct($env);
$this->parent = false;
$this->blocks = array(
);
}
protected function doDisplay(array $context, array $blocks = array())
{
// line 1
$context["foo"] = "bar";
// line 2
$this->loadTemplate("bar.twig", "main.twig", 2)->display($context);
// line 3
echo "
Foo in foo: ";
// line 4
echo twig_escape_filter($this->env, ($context["foo"] ?? null), "html", null, true);
}
public function getTemplateName()
{
return "main.twig";
}
public function isTraitable()
{
return false;
}
public function getDebugInfo()
{
return array ( 26 => 4, 23 => 3, 21 => 2, 19 => 1,);
}
public function getSourceContext()
{
return new Twig_Source("", "main.twig", "/fuz/twigfiddle.com/files/environment/k85WDdymIFgSoXLc/twig/main.twig");
}
}
Source bar.twig
<?php
/* bar.twig */
class __TwigTemplate_16789decfbb837d4631acf2e648380c0658722c50a0b53184b3f3c5f68f9b0ae extends Twig_Template
{
public function __construct(Twig_Environment $env)
{
parent::__construct($env);
$this->parent = false;
$this->blocks = array(
);
}
protected function doDisplay(array $context, array $blocks = array())
{
// line 1
$context["foo"] = "foo";
// line 2
echo "Foo in bar: ";
echo twig_escape_filter($this->env, ($context["foo"] ?? null), "html", null, true);
}
public function getTemplateName()
{
return "bar.twig";
}
public function isTraitable()
{
return false;
}
public function getDebugInfo()
{
return array ( 21 => 2, 19 => 1,);
}
public function getSourceContext()
{
return new Twig_Source("", "bar.twig", "/fuz/twigfiddle.com/files/environment/k85WDdymIFgSoXLc/twig/bar.twig");
}
}
Upvotes: 2