Symfony 'trans' domain inside Twig template - symfony

I'd like to do this:
$this->get('translator')->trans('notice.unregistered', array(), 'index');
Inside Twig template, so I don't have to pass this as an argument. How?

You can also do using trans filter :
{{ 'translationkey'|trans({},'domain') }}

The solution is:
{% trans from "domain" %}text{% endtrans %}

You can add custom functions to change domains inside your templates.
Add your functions:
$getTextdomain = new Twig_SimpleFunction('get_textdomain', function () {
return textdomain(NULL);
});
$setTextdomain = new Twig_SimpleFunction('set_textdomain', function ($domain) {
textdomain($domain);
});
$twig->addFunction($getTextdomain);
$twig->addFunction($setTextdomain);
Then use it:
{% set originalDomain = get_textdomain() %}
{{ set_textdomain('errors') }}
{% trans "My error message" %}
{{ set_textdomain(originalDomain) }}

Related

Override twig variable in included template in Symfony 3

I'm trying to override a variable in the included template.
Can I do this in a Symfony3 & Twig?
My twig template looks like this:
{% set foo = 'bar' %}
{% include 'first.html.twig' %}
{% include 'second.html.twig' %}
// first.html.twig
{{ foo }}
{% set foo = 'second' %}
// second.html.twig
{{ foo }}
I get such a result:
bar bar
But I expect:
bar second
The following Twig code:
{% set a = 42 %}
{{ include("first.twig") }}
Will be compiled to this one:
// line 1
$context["a"] = 42;
// line 2
echo twig_include($this->env, $context, "first.twig");
And twig_include prototype is:
# lib/Twig/Extension/Core.php
function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
So variables are passed by copy, not by reference in included templates. Thus your changes in included templates won't be reflected to including templates.
Moreover, since Twig 2.0, you can't call TwigEnvironment::addGlobal once twig runtime is initialized, so you can't glitch using simple extensions.
All in all, you can understand that if you need to update variables cross templates, it means some template contains business logic and Twig isn't build for that. You need to prepare the whole context in controllers.
Alternatively you may call a PHP class method from TWIG. Example of a page-counter needed when generating a pdf.
Custom class :
class PageCounter
{
private $pageNumber = 0;
public function incrementPageCounter()
{
$this->pageNumber ++;
return $this->pageNumber;
}
}
Controller:
....
$twigVariables = [
...
'pageCounter' => new PageCounter()
];
return $this->render('template.html.twig', $twigVariables);
Twig template (object pageCounter available from any included template)
{{ pageCounter.incrementPageCounter() }} / {{totalPages}}
You just need to do the check and override the variable with another variable :))
{% if name is defined %}
{% set foo = name %}
{% else %}
{% set foo = 'bar' %}
{% endif %}
{% include 'first.html.twig' %}
{% include 'second.html.twig' %}
// first.html.twig
{% set name = 'first' %}
// second.html.twig
{% set name = 'second' %}
Why not override your variable with your include tag/function like :
{% include 'first.html.twig' with {'foo': 'second'} %}
or :
{ include('first.html.twig', {foo: 'second'}) }}

Display View in twig

I created (in Drupal 8) a view of a dataset (newsteaser_mit_bild) with some News in there.
With this view i created a block. The name is automatically generated (views_block__newsteaser_mit_bild_block_1).
The normal content is displayed with
{{ page.content }}
How can i display this View in my Twig File ?
{{ page.newsteaser_mit_bild }}
seems not to be right.
How can i use the view/block in my twig an how can i template them ?
In the main twig file you can use name block like this:
{% block my_custom_block }%
{% endblock my_custom_block %}
In the another twig file you can call the block like this:
{% extends 'link_for_file.twig' %}
{% block my_custom_block }%
{{ parent() }}
{% endblock my_custom_block }%
You can preprocess a new variable and use views_embed_view like:
function THEME-NAME_preprocess(&$variables, $hook) {
$variables['MY-VIEW-NAME'] = views_embed_view('VIEW-ID');
}
And then in the twig file:
{{ MY-VIEW-NAME }}

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} %}

Unable to override KnpMenuBundle template

With ...MyBundle\Resources\views\Menu\knp_menu.html.twig, deleting the </li> has no effect on the rendered menu. (Removing the tag is done to remove the space between inline list elements.) I have followed the advice provided in this answer, including the {% import 'knp_menu.html.twig' as knp_menu %} mentioned toward the bottom of that post. Is this because knp_menu.html.twig already extends knp_menu_base.html.twig? Or what?
layout.html.twig:
...
{{ render(controller('VolVolBundle:Default:userMenu')) }}
...
userMenuAction:
$user = $this->getUser();
$tool = $this->container->get('vol.toolbox');
$type = $tool->getUserType($user);
return $this->render(
'VolVolBundle:Default:userMenu.html.twig', array('type' => $type)
);
userMenu.html.twig
...
{% if type is not null %}
{% set menu = "VolVolBundle:Builder:"~type~"Menu" %}
{{ knp_menu_render(menu) }}
{% endif %}
The answer was found deep in here. All that's required to do a global override of the template is to modify config.yml.
config.yml:
...
knp_menu:
twig: # use "twig: false" to disable the Twig extension and the TwigRenderer
template: VolVolBundle:Menu:knp_menu.html.twig
...

Symfony2: How to display admin-account name while impersonating user-account?

I want to display something like that:
Case 1: "logged in as USER"
# UserName [ logout ]
No problems here, i just do:
# {{ app.user.username}} [ logout ]
Case 2: "logged in as ADMIN"
# AdminName [ logout ]
The same works here:
# {{ app.user.username}} [ logout ]
Case 3: "logged in as ADMIN impersonating a USER"
AdminName # UserName [ return ]
Now thats a problem:
{{ ??..what here..?? }} # {{ app.user.username}} [ return ]
This is the only solution I know... it seems a lot of code for a sipmle displaying username :/
{# iterating through user roles to find ROLE_PREVIOUS_ADMIN #}
{% for role in app.security.token.roles %}
{% if role.source is defined %}
{{ role.source.user.username }}
{% endif %}
{% endfor %}
# {{ app.user.username }} [ return ]
Is there any other way? I need a pure TWIG solution -> this is supposed to be part of my main twig template (that is extended by all other templates) -> I can't add controller code to all actions, just to display username.
With the idea you have proposed above,.. can you not just create a custom twig extension that encompasses your logic from your twig template so that you can just call myCustomTwigFunction within your twig template and it will output the original users name?
See http://symfony.com/doc/current/cookbook/templating/twig_extension.html for more info about custom twig extensions
The code you'd have in your Twig extension file would be...
$roles = $this->container->get('security.context')->getToken()->getRoles();
foreach ($roles as $role) {
if (method_exists($role, 'getSource')) {
return ($role->getSource()->getUser()->getUsername());
}
}
Where $container is a class variable of the DI Container on your twig extension class
For anyone looking for a solution for Symfony 4.3/4.4/5.0:
{% if is_granted('ROLE_PREVIOUS_ADMIN') %}
{% for role in app.token.roles %}
{% if role.role == 'ROLE_PREVIOUS_ADMIN' %}
Admin username is {{ role.source.user.username }}
{% endif %}
{% endfor %}
{% endif %}
From Symfony 5.1 onwards, use IS_IMPERSONATOR in place of ROLE_PREVIOUS_ADMIN.

Resources