Twig: filter in an if condition - symfony

I want to use an filter in an if condition in Twig. The reason for this is a Symfony2 attribute, which I can't compare directly, I have to change it beforehand. I have started with this code:
{% if app.request.attributes.get('_controller')|split('::')|first == 'some\controller\name' %}
do something
{% endif %}
Unfortunately this does not function. So I thought I would use set before the comparison:
{% set controller = app.request.attributes.get('_controller')|split('::')|first %}
{% if controller == 'some\controller\name' %}
do something
{% endif %}
{{ controller }} {# would print 'some\controller\name' #}
Guess what? "do something" is not printed, even if the variable controller now exists and has the value I compare it with. What am I doing wrong?

Ok I tested it, Twig has a strange behavior. "\" is escaped or something like this.
I extended my twig environement with the var_dump function, check this:
{{ var_dump("Sybio\Bundle\WebsiteBundle\Controller\MainController") }}
//string(48) "SybioBundleWebsiteBundleControllerMainController"
{{ var_dump(app.request.attributes.get('_controller')|split('::')|first) }}
// string(52) "Sybio\Bundle\WebsiteBundle\Controller\MainController"
{{ var_dump("Sybio\\Bundle\\WebsiteBundle\\Controller\\MainController") }}
// string(52) "Sybio\Bundle\WebsiteBundle\Controller\MainController"
That's why your test is always false.
You need to double the backslashes of your compared string...
{% if app.request.attributes.get('_controller')|split('::')|first == 'some\\controller\\name' %}
do something
{% endif %}

Related

Twig - loop default value

I am trying to print array from the cotroller into the twig temlate. I want to print "-" whenever array is NULL. My problem is that in for-loop case it writes nothing, however single row working fine. Is there some simple way how to do it correctly?
this is not working as i expected
{% for key in keywords|default('-') %}
{{ key~', '}}
{% endfor %}
this is working
{{ key |default('-')}}
You can use an {% else %} construct on a for loop to do something else if the array is null:
{% for key in keywords %}
{{ key~', '}}
{% else %}
-
{% endfor %}
See the documentation here.

Getting current route twig

I want to get the current route on twig , I used this two code but It fail always
Code 1:
{% if app.request.get('_route') == 'my_route' %}
//Do something
{% endif %}
Code 2:
{% if app.request.attributes.get == 'my_route' %}
//Do something
{% endif %}
Use the "app_dev.php" at the end of your URL to debug and check what route is being used at the bottom. For example I show "route1" here:
Then in your twig template you can use something like this:
{% if app.request.attributes.get('_route') == 'route1' %}
<h1>test</h1>
{% endif %}
I verified this works. I'm using Symfony3 though.
Maybe also try instead of "app.request.attributes.get()", try these:
app.request.pathinfo
app.request.uri
See if those work.
Also if the route is indeed null, try:
{% if app.request.attributes.get('_route') == '' %}
<h1>test</h1>
{% endif %}

Toggle html validation globally

I've made a couple of twig extensions but I'm stumped on this one.
I have the following template logic that I want to make into an extension.
I need reuse this logic into many different forms instead of copying and pasting the following code everywhere:
{% if html5validation is not defined %}
{{ form_start(some_form) }}
{% else %}
{% if html5validation %}
{{ form_start(some_form) }}
{% else %}
{{ form_start
(
company, {'attr': {'novalidate': 'novalidate'}}
)
}}
{% endif %}
{% endif %}
With the above code from the controller I can do the following to turn the html5 validator on and off:
$this->render(..., array(html5validation => false));
I want put the template logic into the twig extension below...
I just don't know if it's possible to implement what I've done above in a twig extension.
class HTML5Validation extends \Twig_Extension
{
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('html5validation', array($this, 'setValidation')),
);
}
public function setValidation($boolean)
{
//Implement the same logic as the twig template.
}
public function getName()
{
return 'html5validator';
}
}
The short answer is no - you can't do this using a twig extension, it's not what they're meant for.
Looking at your template fragment I'd say you need to customise the form_start block. To do this see Symfony Form Theming and How to customise form rendering.
EDIT: This solution does not work if your customised code requires local twig variables - only global twig variables are available for form theming. You can define your own twig globals in config.yml or in a twig extension.
For example, to override form_start globally, you find the default definition of the form_start block in form_div_layout.html.twig, copy it into your own form theme file e.g. YourBundle/Form/fields.html.twig, modify it as required and and update the twig configuration to apply your form theme file. Something like this:
{# src/YourBundle/Form/fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}
{% block form_start -%}
{% if html5validation is not defined %}
{{ parent() }}
{% else %}
{% if html5validation %}
{{ parent() }}
{% else %}
{{ parent
(
company, {'attr': {'novalidate': 'novalidate'}}
)
}}
{% endif %}
{% endif %}
{%- endblock form_start %}
Config:
# app/config/config.yml
twig:
form:
resources:
- 'YourBundle:Form:fields.html.twig'
I actually found a better way to do what I wanted.
As a plus it works globally instead of having to populate more fields into your controller!
In YourBundle/Resources/views/validation.toggle.html.twig
{% extends 'form_div_layout.html.twig' %}
{% block form_start -%}
{% if html5validation is defined and html5validation == false %}
{% set attr = attr|merge({'novalidate': 'novalidate'}) %}
{% endif %}
{{ parent() }}
{%- endblock form_start %}
Then if you want to turn off html5 validation across the whole website:
# app/config/config.yml
twig:
global:
html5validation: false
Or
Even better just use it in your dev_config.yml if you want validation on by default on production mode but the ability to toggle validation on and off for dev mode.
# app/config/dev_config.yml
twig:
global:
html5validation: false
resources:
- 'YourBundle::validation.toggle.html.twig'
Finally use it in your twig template normally:
{% form_theme your_form 'YourBundle::validation.toggle.html.twig' %}
form_start(your_form)
Reusable and non invasive, exactly like I wanted it. :)
I got the hint from:
https://github.com/symfony/symfony/issues/11409#issuecomment-49358377
In the absence of a more elegant solution, you can always put the twig fragment given in your question into a separate file and use twig include from your various forms. The included fragment has access to the variables from the surrounding context:
{# YourBundle/Resources/views/form_start.html.twig #}
{% if html5validation is not defined %}
{{ form_start(some_form) }}
{% else %}
{% if html5validation %}
{{ form_start(some_form) }}
{% else %}
{{ form_start
(
company, {'attr': {'novalidate': 'novalidate'}}
)
}}
{% endif %}
{% endif %}
Then in the twig file for the form:
{% include 'YourBundle::form_start.html.twig' %}
If you typically pass a 'form' variable into render() in your controller(s) then you can use that in your form_start fragment. Otherwise you can pass the appropriate form in as a variable:
{% include 'YourBundle::form_start.html.twig' with {'form': localForm} %}

Join property values of a list of objects in twig

Is it possible to join the values of properties of a list of objects for displaying it?
Something like:
{{ users|join(', ', username) }}
Where users are objects, having a getUsername() method.
I suppose join doesn't take an additional argument, but is there a workaround to achieve something similar? I can not use the __toString() function, as it represents something else...
or have the same result with just one forloop
{% for user in users %}
{{ user.username }}{% if not loop.last %}, {% endif %}
{% endfor %}
You can use map() filter… and fit everything in one line:
{{ users|map(u => u.username)|join(', ') }}
You could use..
{% set usernames = [] %}
{% for user in users %}
{% set usernames = usernames|merge([user.username]) %}
{% endfor %}
{{ usernames|join(', ') }}
Not the prettiest though.
You could always make a custom twig filter to do it.
A shorter version of digital-message's idea:
{% for user in users %}
{{ user.username ~ (not loop.last ? ', ') }}
{% endfor %}

Generate a path appending query string in Symfony2

Is there any facility to generate a path for a given route and arguments, appending the query string automatically? As a temporary workaround i'm using a self made macro:
{% macro path(route, args, with_query) %}
{% spaceless %}
{% set with_query = with_query|default(false) and app.request.queryString %}
{{ path(route, args) ~ (with_query ? '?' ~ app.request.queryString : '' ) }}
{% endspaceless %}
{% endmacro %}
Is there some native function in Symfony2/Twig for doing this?
A nice thing with path Twig extension is that unknow parameters passed through the args array are automatically appended at the end of the URL as GET paramaters :
{{ path('route_id', {'routeParam':'foo', 'unknownParam':'bar'}) }}
will produce
/path/to/route/foo?unknownParam=bar
As simple as :
{{ path('route_id', app.request.query.all) }}

Resources