Add array elements into variable in Twig - symfony

I stored some settings in array in Twig and I need to store them into some variable so I can print it. My array contains some data attributes like this:
{% set data = {
visible: { data: "data-visible-items", value: options.visible_items },
scroll: { data: "data-itemes-scroll", value: options.items_to_scroll },
speed: { data: "data-animation-speed", value: options.animation_speed },
infinite: { data: "data-infinite", value: options.infinite },
autoplay: { data: "data-autoplay", value: options.autoplay_enable },
interval: { data: "data-autoplay-interval", value: options.autoplay_interval },
hover: { data: "data-autoplay-hover", value: options.autoplay_hover },
} %}
Simply I want to store everything from array in one variable, in this variable it's need to be stored like this (separator is space) for example:
data-visible-items="5" data-items-scroll="2" data-animation-speed="400" data-infinite="0" data-autoplay="1" data-autoplay-interval="3000" data-autoplay-hover="1"
So, if the variable is for example attributes I just want to print it like this:
<div{{ attributes}}>
// Content
</div>
I wrote for loop like this:
{% for item in data %}
{{ item.data }} {{ item.value }}
{% endfor %}
and it will print each data and value, but how to store this in the variable in the way I described above?

If you want to store into a variable you can do this:
{% for item in data %}
{% set myvar = item.data ~ ' ' ~ item.value %}
{% endfor %}

If you want to transform that array you could make use of a Twig_Filter or Twig_Function
just chain http_build_query
PHP
$twig->addFunction(new Twig_SimpleFunction('http_build_query', http_build_query', ['is_safe' => [ 'html', ],]));
Twig
<div{{ http_build_query(attributes, '', ' ') }}>
Create the string yourself with a foreach
PHP
$twig->addFilter(new Twig_SimpleFilter('build_attribute_list', function (array $array) {
$str = '';
foreach($array as $key => $val) $str .= ' '.$key.'="'.$val.'"';
return $str;
}, ['is_safe' => ['html'],]);
Twig
<div{{ attributes|build_attribute_list }}>
(edit) Needless to say you can store the output in a variable as well
{% set my_var = attributes|build_attribute_list %}
{{ my_var }}

Related

How to filter selected value using setcontent?

I am working on a Bolt template which returns different information about the listing and I have problems when trying to use setcontent filtering. I use URL queries to toggle the search, like so:
{% if app.request.query.get('onlyPaid') == 'true' %}
{% setcontent records = 'internships' where { is_paid: 1} limit 6 %}
{% endif %}
This filter works great because i have an is_paid field described in my contenttypes.yml
The question is, how do I apply the similar filter to the selected value of 'select' field?
city:
type: select
label: City
values: regions/{title}
sort: title
autocomplete: true
required: true
However this is not a perfect/straight solution, currently works by using "%like%" operator, as you can see below:
{% setcontent building_items = 'buildings' where { 'building_type': '%public%' } %}
The string 'public' is the selected value of the SELECT's field.
The definition of the field in the contenttypes.yaml is this:
building_type:
type: select
values:
residential: Residential
public: Public

Pass custom data to Form Theme in Symfony/Twig

I'm new to Symfony, currently working with 4.4, and am trying to implement a simple form theme for one specific form, i.e. the theme is in the same file as the form's html.twig file. I have my own form_row block and I'm trying to pass in custom data (an icon to use within the div) when calling it, so something like (this is highly summarised!):
{{ form_row(signUpForm.email, {
attr: { placeholder: 'e.g. bobsmith#gmail.com' },
icon: 'envelope'
}) }}
then try to render in the form as
{%- block form_row -%}
<div>
{{ form_label(form) }}
{{ form_widget(form, {attr: class: 'input'}) }}
<i class="icon {{ icon }}"></i>
</div>
I tried also passing icon via the formBuilder, along the lines of
$builder
->add('email', EmailType::class, [
'attr'=> ['icon' => 'envelope']
])
but no joy. Surely this must be possible! Any assistance would be much appreciated. Thanks
I'm not sure but you can access to your form variable with {{form}} in your theme. So you can use it.
Hope this help
Edit:
You can add property in your entity and use it in template like this :
{%- block form_row -%}
<div>
{{ form_label(form) }}
{{ form_widget(form, {attr: class: 'input'}) }}
{% set myData = form.vars.value %}
<i class="icon {% if myData.type == 'mail' %}envelope{% endif %}"></i>
I think you have a better way to do this but i do something like that and it's work.
So, I managed to find the "proper" way to do what I want: a Form Type Extension. The Symfony Casts tutorial on it is pretty good. In short, you create a class to extend your main form input class; in my case I was dealing with a text based input, so I created an App\Form\ypeExtension\TextIconExtension class, extended from FormTypeExtensionInterface, then implemented configureOptions and buildView (I removed the functions in the interface I didn't fill in):
class TextIconExtension implements FormTypeExtensionInterface
{
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['icon'] = $options['icon'];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'icon' => 'user'
]);
}
public function getExtendedType()
{
return TextType::class;
}
public function getExtendedTypes(): iterable
{
return [TextType::class];
}
}
Then, in my form template, I can simply pass a value for icon:
{{ form_row(signUpForm.email, {
attr: { placeholder: 'e.g. bobsmith#gmail.com' },
icon: 'envelope'
}) }}

render a user image and account link in the menu.html.twig

I want to display a user picture (avatar) and some more fields in the menu.html.twig template.
I know that we can display these fields in a user.html.twig template.
{{ content.user_picture }}
{{ user.getDisplayName() }}
{{ content.field_name_user[0] }}
and etc.
But I want to display these fields in the menu.html.twig template.
As I think. we can make a variable in preprocess_block () and print the desired value.
Or if there is no necessary variable in the template - do it in the preprocessor of this template!
Help please make a decision on this issue. And what code you need to write.
It is better to write a pre-process and define a variable and insert the desired elements in it.
hook_preprocess_menu__menu_name(array &$variables) {
$userDetails = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
/**
fetch the desired elements and pass to $variables eg:
**/
$variables['userPictureUrl'] = $userDetails->user_picture->entity->url();
}
You can use hook_preprocess_menu:
function YOURMODULE_preprocess_menu(&$variables)
{
$uid = \Drupal::currentUser()->id();
if($uid > 0)
{
// Load user
$user = User::load($uid);
$userName = $user->getUsername();
// If user have a picture, add it to variable
if(!$user->user_picture->isEmpty()){
$pictureUri = $user->user_picture->entity->getFileUri();
// Add style to picture
$userPicture = [
'#theme' => 'image_style',
'#style_name' => 'profile_picture',
'#uri' => $pictureUri,
];
}
// Set variables
$variables['MYMODULE'] = [
'profile_name' => $userName,
'profile_picture' => $userPicture,
'profile_id' => $userId
];
}
}
End show in your menu.html.twig file:
{{ YOURMODULE.profile_name }}
{{ YOURMODULE.profile_picture }}
{{ YOURMODULE.profile_id }}

Check if a custom Twig function exists and then call it

I test if a custom Twig function exists:
{% if methodExist('sg_datatables_render') %}
{{ sg_datatables_render(datatable) }}
{% else %}
{{ datatable_render((datatable)) }}
{% endif %}
methodExist is a simple Twig_Function:
/**
* #param $name
* #return bool
*/
public function methodExist($name){
if($this->container->get('twig')->getFunction($name)){
return true;
}else{
return false;
}
}
But I get an exception:
Unknown "sg_datatables_render" function. Did you mean "datatable_render"?
500 Internal Server Error - Twig_Error_Syntax
I tried to replicate this, and indeed, {{ sg_datatables_render(datatable) }} seems to always cause a Twig_Error_Syntax exception when sg_datatables_render has not been registered as a Twig function.
I then tried something like this. It's ugly, but I wanted to know if it works. The idea is that a non-existing function will be created to avoid the exception being thrown:
$twig->addFunction(new Twig_Function('methodExist', function(Twig_Environment $twig, $name) {
$hasFunction = $twig->getFunction($name) !== false;
if (!$hasFunction) {
// The callback function defaults to null so I have omitted it here
return $twig->addFunction(new Twig_Function($name));
}
return $hasFunction;
}, ['needs_environment' => true]));
But it didn't work. I also tried to add a simple callback function to the new function with no success.
I tried the same trick with filters, i.e.:
{% if filterExists('sg_datatables_render') %}
{{ datatable|sg_datatables_render }}
{% else %}
{{ datatable|datatable_render }}
{% endif %}
It didn't work either.
Solution 1: {{ renderDatatable(datatable) }}
Something like this does work (yay!):
$twig->addFunction(new Twig_Function('renderDatatable', function(Twig_Environment $twig, $datatable) {
$sgFunction = $twig->getFunction('sg_datatables_render');
if ($sgFunction !== false) {
return $sgFunction->getCallable()($datatable);
}
return $twig->getFunction('datatable_render')->getCallable()($datatable);
}, ['needs_environment' => true]));
And then in Twig:
{{ renderDatatable(datatable) }}
The renderDatatable function is specific to rendering datatables, i.e. it's not a general/multipurpose function like your methodExist is, but it works. You can of course try to create a more general implementation yourself.
Solution 2: {{ fn('sg_datatables_render', datatable) }}
Here's a more general approach. Create an additional Twig function to accompany methodExist:
$twig->addFunction(new Twig_Function('fn', function(Twig_Environment $twig, $name, ...$args) {
$fn = $twig->getFunction($name);
if ($fn === false) {
return null;
}
// You could add some kind of error handling here
return $fn->getCallable()(...$args);
}, ['needs_environment' => true]));
Then you could modify your original code to this:
{% if methodExist('sg_datatables_render') %}
{{ fn('sg_datatables_render', datatable) }}
{% else %}
{{ datatable_render((datatable)) }}
{% endif %}
Or even use the ternary operator:
{{ methodExist('sg_datatables_render') ? fn('sg_datatables_render', datatable) : datatable_render(datatable) }}
PS
Here's how I'd write the methodExist function:
$twig->addFunction(new Twig_Function('methodExists', function(Twig_Environment $twig, $name) {
return $twig->getFunction($name) !== false;
}, ['needs_environment' => true]));
I added s to the end of the function's name because the function checks whether a method/function exists.
I added ['needs_environment' => true] so I can use $twig instead of $this->container->get('twig'). (Kudos to yceruto for this tip.)
getFunction returns false if the function doesn't exist (see the docs), so I simplified the function body to a single-line return statement.

twig automatic convert string to day

I want to convert a string value to day by tapping in a widget, e.g. 24 → 1
{% block convert_day %}
<td>{{ form_widget(form['crush']) }}</td>
<td><!-- displaying my value in day --></td>
{% endblock %}
No It doesn't work !
To be more clear ,I want to do something like that but just display number of days : http://www.convertworld.com/en/time/Days.html
{{ hourValue / 24 }}
Or, if you want to round the value to two decimal places:
{{ hourValue / 24 | number_format(2, ".", ",") }}
Documentation:
http://twig.sensiolabs.org/doc/templates.html#math
http://twig.sensiolabs.org/doc/filters/number_format.html
edit
Alternatively, you can put the precision into the form instance itself. When you create your form, you're probably doing something like this in your controller (or if the form has its own class, you're doing something similar in the buildForm() method of that class):
$form = $this->createFormBuilder($entity)
->add('name', 'text')
->getForm();
When you add your crush field, you can then specify the number of decimal places that should be represented on the form by including the precision option:
$form = $this->createFormBuilder($entity)
->add('name', 'text')
->add('crush', 'number', array('precision' => 3) )
->getForm();
The form will then round the value before inserting it into the database.
Documentation:
http://symfony.com/doc/current/reference/forms/types/number.html#precision
that's what I did ;
<script type="text/javascript">
function execute_time_ext(clicked) {
if (document.forms && document.forms['show_convert']) {
var from = document.forms['show_convert'].unit_from.value;
var elms = document.getElementsByName('unit');
var amount = document.forms['show_convert']['value'].value;
var to;
var amount_int = ['show_convert']['value'] - 0;
for (var i=0; i<elms.length; i++) {
to = elms[i].value;
convert(show_convert['value'], from, to, false, false);
}
var cookie = 'default_decimals';
if (getCookie(cookie) != decimals) {
setCookie(cookie, decimals, null, '/');
}
} else {
if (clicked) {
alert('Converter error. Conversion not supported by browser.');
}
}
}
execute_time_ext(false);
</script>
widget :
<div onkeyup="execute_time_ext(true)" onchange="execute_time_ext(true)"> {{ form_widget(form['value']) }} {% endblock %}
<div> <input id="value_4" type="hidden" value="365.25|0|4" name="unit"></div>
</div>

Resources