Set value in form field in twig template (EasyAdmin) - symfony

I use EasyAdmin in Symfony 4 and I want to set some fields in twig template to disabled true or false depends on user role.
For example
{{ dump(form.role.vars.disabled) }}
shows true
I want to set it to false
{% block entity_form %}
{% set form.role.vars.disabled = false %}
{{ form(form) }}
{% endblock entity_form %}
But I got the error
Unexpected token "punctuation" of value "." ("end of statement block"
expected).
Also I tried to merge it as array but got error.
How to do it correctly?

You need to use the merge filter to update values of an array or a hash. You have a deeply nested hash so you need to use the merge filter many times:
{% set form = form|merge({
role: form.role|merge({
vars: form.role.vars|merge({
disabled: false
})
})
}) %}
See TwigFiddle. (I used {{ var ? 'true' : 'false' }} instead of {{ dump(var) }} because TwigFiddle doesn't support the dump function.)
Update:
The above code doesn't work in your case because the merge filter converts the FormView object to an array. You would need to create a Twig extension to change the object's property. Take a look at this similar question: Set value of single object in multidimensional array in twig template
Or a better approach might be to do this kind of thing in the controller (or wherever you are configuring the form) like #yceruto suggested.

Related

Symfony 4 how to set a global parameter for use in twig

I am after being able to set a parameter from my controller that can be accessed in twig to determine if the user is on an admin area or not to display an extra menu.
So perhaps I have an admin controller and inside the contractor I set a bool parameter to true as admin:
$this->isAdminArea = true;
Then in the template I need an if:
{% if isAdminArea %}
{% endif %}
And other controllers will either set $this->isAdminArea =false or somehow have it set default to false.
How can I achieve this?
You can define a global twig variable in config as stated in Symfony documentation:
# config/packages/twig.yaml
twig:
# ...
globals:
isAdminArea: false
And then override it in your controller whenever it needs to be true.
In your specific case it's also possible to just set the default value in place, using either the default or defined Twig filters
{% if isAdminArea|default(false) %}
{% endif %}
{% if isAdminArea is defined %}
{% endif %}
In the later case it doesn't matter to what value you'll set this into your admin controller, even false will do the job, which may be counter-intuitive.
Using a filter has the advantage of keeping all related bits into the same place, so unless you have the same check in multiple templates it should be considered more readable/maintainable solution.

Check if YAML field is empty in twig

I have two .yml files, in each one of them is a translation of my website. I reference the fields of the .yml files using twig. In one translation I need a field that in the other I don't. So in one translation I have to reference an empty field. But on the website there is not shown nothing but the "path" of the field. So I want to check if the field is not empty, how is it done?
YAML:
title
1: string
2: ~
HTML/Twig:
<h4> {{ 'title.1'|trans }}<sup>7</sup>
{% if 'title.2' is not empty %}
{{ 'title.2'|trans }}
{% endif %}
</h4>
Website: String title.2
With if 'title.2' is not empty you test a concrete string to be empty, which will never be false. Even if '' != 'title.2'|trans might not work as it could fall back to a default language.
If you explicitly don't want to show a certain text based on the user's locale then test for that. It also makes your code easier to read and to maintain:
{% if 'en' != app.request.locale %}
{{ 'title.2'|trans }}
{% endif %}
Even shorter:
{{ 'en' != app.request.locale ? 'title.2'|trans }}
I would also suggest using short words or phrases to identify translations instead of numbers.

Use placeholders in translation using tags

In Symfony / Twig, I could use tags by using percentages in my translated block. For example:
Hello {{nickname}}
would become
{% trans %}Hello %nickname%{% endtrans %}
This works as expected. The array with placeholders that I pass to Twig, are automatically mapped to %placeHolder%. No extra work involved. So this works with my PHP array from the controller being:
Array('nickname' => 'rolandow')
When I want to use the nickname inside the translation block, all I have to do is surround it with percentages %. Unfortunately, this doesn't seem to work when I pass it to trans.
Now I would like to translate a whole block of text, using tags. I can't figure out how I can use the tags in my translation. So, my twig would look something like this:
{{ say.hello|trans }}
And my translation snippet
<trans-unit id="1">
<source>say.hello</source>
<target>Hello %nickName%, how are you doing today? lots-of-text-here</target>
</trans-unit>
I got it working by using this in my template, but it feels like doing things twice. I now need to put the array of placeholder into the trans function again. For example:
{{ say.hello|trans('%nickName%' : nickName) }}
If I want to use other tags that are given to twig in my controller, I need to pass them to the translator as well. Can't I just pass the complete array somehow?
{{ say.hello|trans('%nickname%': 'rolandow') }}
There are several questions here so let's cover them.
1) Twig's behaviour is not like a Doctrine query, where each parameter must be bounded. You can pass an array that contains unused parameters to trans, so if you don't want to specify {'key': 'value', 'key2': 'value2'...} to the filter, just pass the entire array (example: | trans(array)). That's #Luke point.
2) You can translate block of texts using several ways, the most simple is {% set %}. The {% set %} tag can be used two ways :
{% set var = expression %} or {% set var1, var2 = expression1, expression2 %} is the most known and used way: you just put some value inside one or several variables.
{% set var %} block of text {% endset %} allow you to set an entire block of text inside that variable. This is useful if you want to put that block into a filter (such as, escape, or in your case, trans).
So to translate a block of text, you'll do something like:
{% set variable %}
block to translate %placeholder%
{% endset %}
{{ variable | trans(array) }}
Anyway, I don't see any interest of translating a whole block in one time : we use | trans generally after a property (such as say.hello), and I can't imagine your xlf/yml translation file with such a design. If you want to use the translator just to fulfill placeholders, just use Twig as it is written for that job :-)
3) About replacing placeholder by %placeholder% in your parameters array's keys : the point of Twig is: put what you want as placeholder. In such a way, if your translated sentence contains several %, you can use $something$, #something# or even something as placeholder.
If your array keys does not contain those %, you need to add them, you don't have any choice. If you really want to do it on a Twig file, you can create a macro that will do the job for you, and put it in a file you import in your base layout.
Something like :
{% macro trans_pct(property, params) %}
{% set newParams = [] }
{% if params %}
{% for key, value in params %}
{% set newParams['%' ~ key ~ '%'] = value %}
{% endfor %}
{% endif %}
{{ property | trans(newParams) }}
{% endmacro %}
And then use it with {{ _self.trans_pct('hello.say', array) | trim }}.
Notes :
_self is the template where is stored the macro (see the documentation for more details).
trim is used because I wrote the macro with indentation and line breaks (that's cleaner to read). Those spaces are, by default, printed.

twig convert a string to the object that it represent

imaging that i have a object and which can be called in a twig template like this:
{{ object1.object2.object3.property3A }}
well, it will show me the content if we use php to write is :
$object1->getObject2()->getObject3()->getProperty3A();
My question is if i have a string ,
$refString="object1.object2.object3.property3A";
and then it is passed to twig, how could i get the property3A? For my experience, we can do this in php like this:
$refString="object1->getObject2()->getObject3()->getProperty3A()";
echo $$refString;
But i do not know how to make it work in twig.
I didn't tested this, but i think it schould do the trick.
{#
recursively reading attributes from an object
! object1 must be available !
theValue is the value of property3A
#}
{% for key in "object1.object2.object3.property3A"|split('.') %}
{% if not loop.first %}{# skip the 'object1' part #}
{% set theValue = attribute(theValue|default(object1), key) %}
{% endif %}
{% endfor %}
I don't think there is a "shortcut" to do this in twig. If you can't find a simple way to do this, you can write you own extension, that would convert a STRING_TYPE to a VAR_TYPE.
Twig internals might put you on the right track. This is an example of what is feasable with twig extension and might inspire you.
I ran into a similar situation. This answer will only work if the object you need is available to the template and you know the name of it with a string.
In this case, you can access the object using Twig's Global Variable _context:
{% set object1 = _context['object1'] %}
And then access the methods and variables of the object as normal:
{{ object1.object2.object3.property3A }}

Get number of choices from entity field in Twig template

I have a Symfony 2 form for a Doctrine entity with an entity choice field for a ManyToMany relationship. It is possible for the choice field to not have any choices - how can I test for this in Twig?
Example: The form is for a "Deal" entity, which can optionally be assigned to a "Location" entity. I render the "Location" entity field like this:
{{ form_label(edit_form.locations) }}
{{ form_errors(edit_form.locations) }}
{{ form_widget(edit_form.locations) }}
The field is set to render with checkboxes. However, it is possible that no "Location" records will exist. In this scenario the user will see the label "Locations: " but no check boxes. I would like to have a conditional statement that displays a message, something along the lines of this:
{{ form_label(edit_form.locations) }}
{{ form_errors(edit_form.locations) }}
{{ form_widget(edit_form.locations) }}
{% if edit_form.locations.choices|length == 0 %}
You haven't created any Locations yet!
{% endif %}
Anybody know of a way to achieve this?
Just to help others...
In newer versions it would be:
{% if edit_form.locations.vars.choices|length == 0%}
I was actually looking to do this, so I'll share:
If you had an embedded form and you wanted to do the same type of check except pulling the data from the prototype (in case the form is not generated with any children forms)
{% if form.childForms.vars.prototype.field.vars.choices|length == 0 %}
"childForms" being the embedded form type and "field" being the select field you want the choices out of...
Found it:
{% if edit_form.locations.get('choices')|length == 0 %}

Resources