Pass variable in twig to included template - symfony

I have set the rendering of an navigation menu en an separate twig template. Reason is the the menu is generated on two different places in the application.
Now I want to give a parameter to the navigation menu so I know from with place it is generated are these some small differences.
I tried the following code but the variable is not know in the navigation menu template:
{% set menuType = 'user' %}
{% include 'MyBundle:nav.html.twig' with menuType %}
Also tried:
{% include 'MyBundle:nav.html.twig' with {'menuType': 'user'} %}
In both case twig generated the error that {{ menuType }} does not exists?

Surprisingly, while I thought it would be possible to do like you to pass a simple variable, it appears only arrays are accepted as passed values. (While the two examples of the include doc are arrays, this isn't specifically precised.)
In your case, you have to write it this way:
{% set menuType = 'user' %}
{% include 'MyBundle:nav.html.twig' with {menuType:menuType} only %}
Note: I've added the only keyword to disable the access to the context.
Without it you don't need to pass the variable to the included templates as they will have access to them. (It's a good practice to disable it.)
Here is a Twigfiddle with some tests and dumps: https://twigfiddle.com/gtnqvv
{% set menuType = 'user' %}
{% set vars = {'foo': 'bar'} %}
{% set z = 'bob' %}
{# vars dumps ommitted here, see the fiddle. #}
{#% include '1.html.twig' with menuType only %#}
{% include '1.html.twig' with {menuType:menuType} only %}
{% include '2.html.twig' with vars only %}
{% include '3.html.twig' with {z:z} only %}
{#% include '3.html.twig' with z only %#}
The first and last commented lines doesn't work, as you know, here is the error:
Uncaught TypeError: Argument 1 passed to Twig_Template::display() must
be of the type array, string given
The second works as you want, you just have to make it an array. (Strange anyway)
The third line is a test from the Twig doc, and the fourth is a test with another variable name, just to be sure.

As long as a variable is available, or included with the parent template, it is available to any included or child template.
Ex:
Controller:
return $this->render('CmsBundle:EmailBulk:edit.html.twig', array(
'entity' => $entity,
'form' => $editForm->createView(),
'tokens' => $tokens
));
Then, edit.html.twig:
{% block body -%}
<div class="panel panel-default animated fadeIn delay-400">
<div class="panel-heading">
blah blah blah
</div>
<div class="panel-body">
{{ include('CmsBundle:EmailBulk:form.html.twig') }}
</div>
</div>
{% endblock %}
The 'form' variable from the controller is available to the included template form.html.twig

I created a sample twigfiddle for you here:
https://twigfiddle.com/fpzv26
You need something like this:
{% set vars = { 'menuType' : 'user'} %}
{% include 'MyBundle:nav.html.twig' with vars %}

I use this in my code and it works for me. The var foo is used in baz.html.twig directly:
{% set foo = 'foo' %}
{{ include ('MyBundle:bar:baz.html.twig') }}
In twig docs it says:
Included templates have access to the variables of the active context. [...]
The context is passed by default to the template but you can also pass additional variables

Related

How to "shuffle" includes from twig

I have a base template that includes multiple sub-templates and the code runs something like this:
<ul class="row portfolio list-unstyled mt-3 lightbox" id="grid">
<!-- summary section -->
{{ render(controller('App\\Controller\\ReadController::summary')) }}
{{ render(controller('App\\Controller\\BookController::random', {num: 3})) }}
{{ render(controller('App\\Controller\\WikiController::random')) }}
</ul><!-- / portfolio row -->
As can be seen, these "items" appear in a fixed order:summary goes first, then 3 random and then one random.
What I intend to do is to "shuffle" these items (in the above code snippet, there will be 5 items) so that the order is different in each refresh to give the end user some variation.
Is this possible to do in Twig?
UPDATE
I used #hcoat method and it is working. Will try the shuffle filter later.
As suggested to you in the comments above you can use the Array Extension or send it through a controller. I think having a controller that sends in the random list is the way to go.
However, sometimes it is useful to randomize a list with standard twig and in such cases you can do something like the following:
// Path and pram Array, pass empty hash if no params
{% set arr = [
["App\\Controller\\ReadController::summary", {}],
["App\\Controller\\BookController::random", {'num': 3}],
["App\\Controller\\WikiController::random", {}]
] %}
// create a list and merge arr array with random key
{% set list = {} %}
{% for item in arr %}
// There is a bug in some twig verions
// so concat a letter to ensure random key works
{% set list = list|merge({ (random()~'a'):(item) }) %}
{% endfor %}
// sort the list by the random key and render the output
{% for key in list|keys|sort %}
{{ render(controller(list[key][0], list[key][1])) }}
{% endfor %}
Now the sub-templates will be rendered in a random order.

Use Twig Custom Set Variables From an Include

I'm trying to include a twig file with a bunch of custom set variables and then use the variables in the multiple other template files. Similar to how including a PHP file works.
I don't seem to have access to the variables set inside the include in my index file.
Is there any way to do this?
Sample Code *Edited
Included File:
{# variables.html #}
{% set width = "100" %}
{% set height = "250" %}
Template File:
{# index.html #}
{% include 'variables.html' %}
{{ width }}
{{ height }}
Expected Outcome:
100 250
Actual Outcome:
// Nothing gets output
I was just trying to do the same thing you were and came up with the following:
Created snippets.twig to maintain all these mini variables. In your case, you might call it variables.twig. In this file, I used a macro without any arguments. I was creating formatted entry date markup that I can use across all my templates and it looked like this:
{% macro entry_date() %}
<time datetime="{{post.post_date|date('m-d-Y')}}">{{post.post_date|date('F j, Y')}}</time>
{% endmacro %}
note that the parenthesis after the name declaration were imperative
In my main layout file, layout.twig, I referenced this macro via an import statement so it would be accessible in all child templates:
{% import "snippets.twig" as snippets %}
<!doctype html>
...
In my template files, I now have snippets accessible and can query it like any other variable:
{{ snippets.entry_date }}
UPDATE
This doesn't seem to correctly run code. If you're just storing static content, you should be good. You can also pass args to the macro so I imagine you could make some magic happen there but I haven't tried it.
As far as I know it is only possible with {% extends %} tag. Instead of including template with variables you should extend it.
Example:
variables.tpl:
{% set some_variable='123' %}
... more variables ...
{% block content %}
{% endblock %}
template.tpl
{% extends 'variables.tpl' %}
{% block content %}
{{ some_variable }}
... more code which uses variables assigned in variables.tpl ...
{% endblock %}
You can use a template extension : https://symfony.com/doc/current/templating/twig_extension.html
post|entry_date

Dynamic twig variable names

Given an array of variables sent to a twig template, such as:
$form = $this->createForm( new ServiceItemType()
, $entity
, array( 'attr'=>
array(
'em' => $this->EM()
,'group' => true
) ) );
I want to capture the variables for easy access in twig. However:
{% for key, value in form.vars.attr %}
{% set key = value %}
{% endfor %}
remaps the key variable in the for loop.
twig objects to:
{% for key, value in form.vars.attr %}
{% set {{key}} = value %}
{% endfor %}
And stack as I am aware never seems to address set. Would anyone who knows, please indicate how to accomplish this variable assignment?
I know this syntax works
{% render "..." with {(key): value} %}
Did you try the following syntax? As of March, Friday 22nd this syntax didn't work so you need to use a work around.
{% set (key) = value %}
An alternative to that would be to include a template and pass and form.vars.attr.
{% include "YourAwesomeBundle:Controller:template.html.twig" with form.vars.attr %}
You can also merge form.vars.attr with another array using the merge function.
{% set vars = {} %}
{% set vars = vars|merge(form.vars.attr) %}
{% include "YourAwesomeBundle:Controller:template.html.twig" with vars %}
Within the included template you will be able to use the variable em and group.

Extending twig for generate html code

I have to generate something like star rating and I have to generate some html for styling ect.
<div class="star on"><i>*</i></div>
<div class="star on"><i>*</i></div>
<div class="star on"><i>*</i></div>
<div class="star"><i></i></div>
<div class="star"><i></i></div>
I want to render using a twig function passing active stars parameters.
{{ stars(4) }}
Is correct use twig functions for generate html code?
Or maybe should I use {% include ... %}
No need in overengineering for such simple task.
If you generate your array in Controller, then it could look like this:
$stars = array(
true,
true,
true,
false,
false,
);
Then you could render your HTML in Twig:
{% for star in stars %}
<div class="star{{ star ? ' on' }}"<i>{{ star ? '*' }}</i></div>
{% endfor %}
In case if you would like to operate with Twig only, I recommend you to use macro:
{% macro stars(stars, total) %}
{% for item in 1..total %}
{{ item }}<br>
{% if item <= stars %}
<div class="star on"><i>*</i></div>
{% else %}
<div class="star"><i></i></div>
{% endif %}
{% endfor %}
{% endmacro %}
If you've defined your macro in the same template, you should call it via _self, if in another file - just like a function, but not forget to import your file into needed twig. See chapter about macros (linked above).
Following call will produce HTML structure that you described in your question:
{{ _self.stars(3,5) }}
See the Extending Twig section of its docs. According to the table in the first section on that page, using functions for content generation is natural. I create a lot of Twig functions and I suggest you create one to solve your problem.
BTW, your function can render a separate template with HTML code — do not generate the HTML code right in your Twig function's PHP code. To render a separate template from your Twig function, inject the service_container service into it, get the templating service and call the render() method on it:
return $this->container->get('templating')->render($pathToYourCustomTemplate);
Usually, it's best to inject the needed services individually, but if you inject the templating service instead of service_container, you'll get a cyclic dependencies problem. That's why injecting the whole container into Twig extensions is a reasonable exception.

symfony2 - twig - how to render a twig template from inside a twig template

I have a xxx.html.twig file which shows a page, but when I want to refresh the page with different data and just update it with new data, I have a select and a submit button for it.
The thing is that I don't know how do I call an action in the controller which I pass parameters to from my twig and call for new data and then I render the same twig template again with new parameters.
How do I do so?
Here are a few different ways:
{{ render(app.request.baseUrl ~ '/helper/test', {"hostid2": hostid } ) }}
or
{% include 'MyCoreBundle:Helper:test.html.twig' with {"hostid2": hostid } only %}
or
{% render controller("MyCoreBundle:Helper:test", {'hostid2': hostid}) %}
Symfony 2.1:
{% render 'YourBundle:YourController:yourAction' with {'var': value} %}
Symfony 2.6+:
{{ render(controller('YourBundle:YourController:yourAction', {'var': value})) }}
And, of course, read the documentation.
I think some parts are depricated here.
To make the include work in latest Symfony 3.1.10, I solved it like this:
{% extends 'base.html.twig' %}
{% block body %}
{{ include('AppBundle:Default:inner_content.html.twig') }}
{% endblock %}
Note: include() with parentheses.
Then all the variables are included from the parent template. If you like to restrict some variables in the child template, you use with ... only (look over)

Resources