Symfony Twig Path function parameters, key defined by string - php

In Twig with Symfony, to generate a path in a template, it wants to use
{{ path('mypathname', {parameterName: parameterValue}) }}
In my project I have an inherited template, and one block loops through a chunk of data and spits out a list of URL's. What I want to do, is from my new template, is allow me to {% set %} the keyname of the first parameter
so basically like this
template.html.twig
<ul>
{% for thing in things %}
<li>{{ thing.name }}</li>
{% endfor %}
</ul>
otherthings.html.twig
{% extends 'template.html.twig' %}
{% set path_name = "thingDetail" %}
{% set path_param = "id" %}
and see the output like
<ul>
<li>The 20th Thing</li>
</ul>
It may sound ridiculous, but across this app, the naming and pathing styles doesn't always match up with the different kinds of content that goes into that blocks loop, so some type of override needs to happen and the last thing I want to do right now, is overwrite the block for that for each type, when the last thing that needs to be done is URL generation
Thank you!

If you enclose the hash key in parenthesis, Twig will treat it as an expression:
{%- set field = 'foo' -%}
{%- set arr = { (field): 'bar' } -%}
will set arr to
{ foo: 'bar' }
(This is mentioned in the Twig documentation, but it's easy to see how it could be overlooked.)

Related

How to prevent Twig from adding form-control class to the fields it generates?

I have this code:
{% for key, customField in customFields %}
{{ form_widget(formVirtualTerminal['cf_' ~ key], { 'attr': {'placeholder': customField['FieldDisplayName'], 'name': customField['FieldName'], 'class' : 'row no-gutters mb-3'} }) }}
{% endfor %}
which traverses an array and outputs some fields for it. However, for reasons unknown to me, a form-control class is generated into all these fields and that ruins the design of the page.
In this question (delete form-control in form-row symfony/twig) it's mentioned that this is part of a default Bootstrap theme and they discuss how this theme can be deactivated on application level.
However, I prefer not to do such radical changes if possible. I would like to tell Twig not to add classes.
The following is a hack which solves the problem by removing that class:
$("#custom-text-field-container .form-control").removeClass("form-control");
However, I would like to avoid writing Javascript to remove classes from fields which should never had that class in the first place.
Is there a way to tell twig not to write form-control class into these text fields?
You can't prevent it without making changes to the theme
You can't "tell twig not to add classes".
The form_widget does not accept any parameter to remove a class or attribute that will be later applied by the theme.
Basically, it has no control over what the template does, it only passes the template whatever parameters you want, and it's up to the form template how to use that information.
E.g. for HTML classes, the usual approach is simply to merge whatever classes you pass with whatever classes the template has set by default.
Workaround while keeping most of the form theme you are using
A simple recourse would be to create to your theme based on the form theme you are using (which seems to be 'bootstrap_4_layout.html.twig')
You'd need to rewrite only the parts that hinder you, leaving everything else as it is.
E.g. a "complete" new theme could be as short as:
{# templates/form/your_theme.html.twig #}
{% use 'bootstrap_4_layout.html.twig' %}
{% block form_widget_simple -%}
{% if type is not defined or type != 'hidden' %}
{%- set attr = attr|merge({class: (attr.class|default('') ~ (type|default('') == 'file' ? ' custom-file-input' : ''))|trim}) -%}
{% endif %}
{%- if type is defined and (type == 'range' or type == 'color') %}
{# Attribute "required" is not supported #}
{%- set required = false -%}
{% endif %}
{{- parent() -}}
{%- endblock form_widget_simple %}
This is almost the same form_widget_simple block as the original, I simply removed the form-control class that is merged with the classes you can pass through.
You can use this form only in the templates you want this custom theme by doing this in the appropriate template:
{% form_theme form 'form/your_theme.html.twig' %}
Or if you want to use it by default in all forms in your application:
# config/packages/twig.yaml
twig:
form_themes: ['form/your_theme.html.twig']
# ...

Twig iterate/read and get value - Octobercms

Hi guys its rather a very basic question, had chance to look several questions on stackoverflow but all in vain.
so i have this twig variable called "WordoftheDayfromDB ", to which i am passing from some data after querying DB in my controller via laravel pluck method. The controller exsits in plugin of octobercms. the content of the variable is shown below
{% set WordoftheDayfromDB = __SELF__.words %}
{{WordoftheDayfromDB}} # this output below object
["{\"id\":4,\"word_tr\":\"parazit\",\"slug_tr\":\"parazit\",\"word_gr\":\"\\u03c0\\u03b1\\u03c1\\u03ac\\u03c3\\u03b9\\u03c4\\u03bf\",\"slug_gr\":\"parasito\",\"pubordraft\":1,\"created_at\":\"2017-06-07 13:04:57\",\"updated_at\":\"2017-06-07 13:04:57\",\"deleted_at\":null,\"word_image\":\"\\\/cropped-images\\\/image2.jpg\",\"typeswb_id\":0}"]
can someone tell me a way to extract keys and values from the about twig variable.
what i already tried is following:
<pre> {{WordoftheDayfromDB.id}}</pre>
or
{% for item in WordoftheDayfromDB %}
{{item.word_tr}}
{% endfor %}
also some combination using {% if WordoftheDayfromDB is iterable %}.
I will appreciate your answer very much!
thank you for reading my question.
You can use the for loop so that the keys and values are both accessible like this:
{% for key, value in WordoftheDayfromDB %}
<li>{{ key }}: {{ value }}</li>
{% endfor %}
So the answer is rather complex then even i anticipated! i had to do a lot of digging with frustration to really get at the bottom of this matter.
First thing first, i was doing a cron job where i saved the data from a model in text type field. That is why if you see above result i.e
{% set WordoftheDayfromDB = __SELF__.words %}
{{WordoftheDayfromDB}} # this output below object
["{\"id\":4,\"word_tr\":\"parazit\",\"slug_tr\":\"parazit\",\"word_gr\":\"\\u03c0\\u03b1\\u03c1\\u03ac\\u03c3\\u03b9\\u03c4\\u03bf\",\"slug_gr\":\"parasito\",\"pubordraft\":1,\"created_at\":\"2017-06-07 13:04:57\",\"updated_at\":\"2017-06-07 13:04:57\",\"deleted_at\":null,\"word_image\":\"\\\/cropped-images\\\/image2.jpg\",\"typeswb_id\":0}"]
it outputs a JSON String, too bad can't iterate or do something with it.
To solve this,
Create json_decode filter in twig.
Apply the filter to value part of array.
Access Individual values of array with variable[keyname] method.
I created a twig filter json_decode
for creating filter see this Link
while in October, the creation to new twig extension is rather easy which is just give registerMarkupTags method in Plugin.php with filter array poiting to name and function name. See this link for extending twig in octobercms here
Now, the part we were waiting for, how to get the values and show them in twig template. Here it is going to be, by using above same example. This is what i did
{% set wordoftheday = __SELF__.words %}
{% for key, value in wordoftheday %}
{% set decoded = value|json_decode %}
# to get the indvisual values
{{ decoded['id'] }}
{{ decoded['created_at'] }}
{% endfor %}

How to automatically check if variables are not empty in Symfony 3?

(Sorry for this bad english)
I would like to know if it's possible to automatically check if variables from MySQL request are not empty in Symfony 3. I know that I can put {% if foo is defined %} in Twig or something like this but I didn't find if there's a way to do it automatically. I can test the request in the controller too. But with those solutions I have to do it for every request.
All my website uses "if not empty then show it" that's why I'm trying to find it.
Edit : I know how to check every fields of every request in Twig or in php (Controller) one by one but there is a lot of duplication code, which is "boring". Thats why I am asking you if something automatic exists to check my data. (parameter in Symfony, ...)
Thank you <3
If I understand, your problem :
You want to iterate on each property in an entity. But you can't, then you are searching for a solution to not write :
{% if entity.property1 %}
{{ entity.property1 }}
{% endif %}
{% if entity.property2 %}
{{ entity.property2 }}
{% endif %}
{% if entity.property3 %}
{{ entity.property3 }}
{% endif %}
You have two solutions to make your properties traversable :
Get your entity with a Doctrine query using ->getArrayResult() instead of ->getResult()
Use ReflectionClass to get the properties as an array :
http://php.net/manual/fr/class.reflectionclass.php
So you can iterate on each property, and do something like that :
{# Where you get your entity as an array #}
{% for property in entity %}
{% if not property is null %}
{{ property }}
{% endif %}
{% endfor %}
{# where fields comes from the ReflectionClass #}
{% for field in fields %}
{% if not attribute(entity,field) is null %}
{{ attribute(entity,field)}}
{% endif %}
{% endfor %}
Use the Twig strict_variables config parameter and set it to false (should be the default value):
strict_variables boolean
If set to false, Twig will silently ignore invalid variables
(variables and or attributes/methods that do not exist) and replace
them with a null value. When set to true, Twig throws an exception
instead (default to false).

Difference between customizing form theme and referencing block in Symfony?

what is the difference between referencing a widget block and customizing it : the docs say :
So far, to override a particular form block, the best method is to
copy the default block from form_div_layout.html.twig, paste it into a
different template, and then customize it. In many cases, you can
avoid doing this by referencing the base block when customizing it
But to me it looks the same :
{# app/Resources/views/Form/fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}
{% block integer_widget %}
<div class="integer_widget">
{{ parent() }}
</div>
{% endblock %}
{# app/Resources/views/form/fields.html.twig #}
{% block integer_widget %}
<div class="integer_widget">
{% set type = type|default('number') %}
{{ block('form_widget_simple') }}
</div>
{% endblock %}
What is the difference?
The first example in the documentation that you are referencing shows how to override the entire widget that displays your form element.
The second example in the documentation that you are referencing shows how you can employ code re-use so that you are not rewriting form templating sections that you are not modifying. So, instead of having to declare
{% set type = type|default('number') %}
{{ block('form_widget_simple') }}
all over again in your overriding widget, you can instead reference the base block that already has this. If you are referencing base blocks from an external template, you can call the parent block via {{ parent() }}, and if you are referencing blocks from inside the same template as the form, you can call the base block via {{ block('base_integer_widget') }}
If you look at it from a PHP/Symfony point of view with inheritance that can help explain it as well. Say you have one PHP class that extends another and you want to override a function named doSomething() - you might rewrite the entire function as you need it. But, say that doSomething() has a block of common code that you always want to run, then you might perform your actions and call parent::doSomething() at the end of it. Or, if you're accessing a different Symfony service you might call $this->get('some.service')->doSomething() instead.
That's the same concept here, you can either override the entire widget or you can override parts of it - perhaps putting a surrounding <div></div> but calling {{ parent() }} from within that since you're changing nothing else about the widget.
I do have one example where I overrode standard button behavior in Symfony and used both cases. I have a separate template file in `app/Resources/views/Form/navigationButton.html.twig' with the following code:
{% use 'form_div_layout.html.twig' %}
{% block button_widget -%}
{% set attr = attr|merge({class: (attr.class|default(''))|trim}) %}
{{- parent() -}}
{%- endblock %}
{% block button_row -%}
{{- form_widget(form) -}}
{%- endblock button_row %}
I am overriding the button widget by allowing additional classes to be passed as attributes and then calling the parent widget to produce it as normal. I then override the button row widget to not put surrounding <div></div> tags since I didn't want that in my template (see the originals here and here).
Then to use in one of my templates I simply do:
{% form_theme form ':Form:navigationButton.html.twig' %}

twig: pass variables from view to controller

Setup:
Twig 1.13.1
PHP 5.4.3
Problem:
I have 10,000 articles in my DB. I need a way to only allow X amount of stories to display on the page. I know I can limit the number in the controller before i call the template but that number will be different depending on the template that is used. I will have one controller to handles all the articles. I need a way to pass the number from the template to the controller to limit the array. I don't want to pull down all 10,000 articles then use twig "slice" filter/func.
I know in django you can use the below. That will only load the top 3 stories.
{% get_latest_stories 3 sports as story_list %}
{% for story in story_list %}
{{ story.title }}
{% endfor %}
Here is my current files.
Controller
<?php
$stories = news_stories::getStories("sports",5); //getStories(section,limit);
?>
<?=$twig->render("storyList.html", array('stories' => $stories))?>
View/Template
{% for story in story_list %}
{{ story.title }}
{% endfor %}
Summary
I would like a way to pass a number from the template to the controller so that i can limit the about of rows returned from the DB
Logically speaking, it would be impossible for the view to pass something to controller since the view is being processed at the end of the stack, after everything else.
You can however, pass a function into the view. You would want to create some sort of getViewStories function that you can access from your twig template. Since you have this already in your controller:
<?php
$stories = news_stories::getStories("sports",5); //getStories(section,limit);
?>
<?=$twig->render("storyList.html", array('stories' => $stories))?>
All you would need to do is change it around a bit, like this:
<?php
$function = new Twig_SimpleFunction('getViewStories', function (section, limit) {
return news_stories::getStories(section,limit);
});
$twig->addFunction($function);
?>
<?=$twig->render("storyList.html")?>
Now, from inside your template, you can call this function, like so:
{% set story_list = getViewStories('sports',5) %}
{% for story in story_list %}
{{ story.title }}
{% endfor %}
And change the getViewStories parameters around in each template.
And while you can use the slice filter, I would recommend against it in your case, as it makes for unnecessarily long database calls. This is the most optimized method (that I'm aware of).
You want to use the slice filter I think this should work for you
http://twig.sensiolabs.org/doc/filters/slice.html
{% for story in story_list|slice(1,5) %}
{{ story.title }}
{% endfor %}
should only return the elements 1 - > 5 of the loop then break loop. You can also do it like this
{% for story in story_list|[start:5] %}
{{ story.title }}
{% endfor %}
Disclaimer: I've never actually used twig though this was just a quick browse through its docs
You can embedded controllers ( or render other urls ) from inside a twig template. This means you could have a main layout template for your site, and keep your storyList.html template very plain - just to iterate over the stories and any markup they might need.
In your main layout you would render the action for the news stories:
<div id="stories">
{% render url('...') with { section: 'sports', limit: 5}, {'standalone': 'js'} %}
</div>
This way requires hindclude.js included on your page. Check these docs. If you are also using symfony ( you mention MVC but not the framework ) - even better. Scroll up a bit and look at Embedded controllers.
Otherwise, I believe this way essentially uses ajax.

Categories