Twig - print array without empty values - php

I have array constructed like this
add_to_context('custom', [
[
'title' => 'My title',
'link' => 'My link'
],
[
'title' => 'My title 1',
'link' => 'My link 1'
]
]);
and in view I have simple loop
{% for item in custom %}
<li>
<h1>{{ item.title }}
<img src="{{ item.link|e }}" target="_blank">
</li>
{% endfor %}
And everything works fine. But I want to print elements which have both keys with value. For example, if I have
[
'title' => '',
'link' => 'mylink'
]
I don't want to print this. If link will be empty I don't want it too. If both are empty - the same. I want to print it only if both keys have values. So, how can I do this?

You could do something like this, maybe.
Twig even has a little built in functionality for this:
<ul>
{% for item in custom if item.title and item.link %}
<li>{{ item.title }}</li>
{% endfor %}
</ul>
I haven't tested it, but I assume the and in the if statement should work.

You can just add a simple test in your template :
{% for item in custom %}
{% if item.title|length %}
<li>
<h1>{{ item.title}}
<img src="{{ item.link|e }}" target="_blank">
</li>
{% endif %}
{% endfor %}
Generally speaking , "0"|trim expression will evaluates to false.

Related

How to put liquid code inside a php string

I want to put Liquid code in a metafield with value type string. This is the Liquid code:
{% unless shop.metafields.cmld == blank %}
{%- assign cmld = shop.metafields.cmld -%}
<div class="slider">
{%- for field in cmld -%}
<div>
<img src="{{ field | last }}" />
</div>
{% endfor %}
</div>
{% endunless %}
The Liquid code works fine when I try it on shopify.
The problem here is this error on the 1st row:
{%'(T_CONSTANT_ENCAPSED_STRING), expecting ')' in .....
Any suggestions? It looks like this:
$add_metafield= array(
"metafield" => array(
"namespace"=> $metafield_namespace,
"key"=> "something",
"value" => "{% unless shop.metafields.".$metafield_namespace." == blank %}{%- assign ".$metafield_namespace." = shop.metafields.".$metafield_namespace." -%}<div class="slider">{%- for field in ".$metafield_namespace." -%}<div><img src="{{ field | last }}" /></div>{% endfor %}</div>{% endunless %}",
"value_type" => "string"
)
);
Change from double to single quotes in <div class="slider"> and <img src="{{ field | last }}" />
changing it to -> <div class='slider'> and <img src='{{ field | last }}' />

Sonata Admin conflict between 'template' and 'editable'

I am currently working on a small personal project to learn how to manipulate Symfony and Sonata, and I find myself confronted with a small problem. I have constrained one of my variables to a "template" in "configureListFields" but I can not submit it to "editable". I can do the one without the other but not both at the same time if not the "editable" bug as I show you a bit further down.
List :
$listMapper->add('status', 'string', array(
'template' => 'WebBundle:Default:list_client.html.twig',
'label'=> 'Status'))
Form :
$formMapper->add('Status', 'choice', array(
'choices' => array(
'Client' => 'Client',
'Ex-Client' => 'Ex-Client',
'Prospect' => 'Prospect')))
Template :
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
<div>
<p class="ClientStatus {% if object.Status == 'Ex-Client' %}
label label-danger
{% elseif object.Status == 'Client' %}
label label-success
{% else %}
label label-info
{% endif %}" >
{{ object.Status }}
</p>
</div>
{% endblock %}
View with that config :
Second config :
$listMapper->add('status', 'choice', array(
'choices'=>array(
"Client"=>"Client",
"New Client"=>"New Client",
"Ex-Client"=>"Ex Client"
),
'label'=> 'Status',
'editable'=>true))
View :
Third Config :
->add('status', 'choice', array(
'choices'=>array(
"Client"=>"Client",
"New Client"=>"New Client",
"Ex-Client"=>"Ex Client"
),
'template' => 'WebBundle:Default:list_client.html.twig',
'label'=> 'Status',
'editable'=>true))
view :
So there seems to be a conflict between "template" and "editable" opinions on how to handle this problem ?
Thanks a lot.
check out the list_choice template that is provided with the Sonata sources.
# SonataAdminBundle/Resources/views/CRUD/list_choice.html.twig
{% set is_editable =
field_description.options.editable is defined and
field_description.options.editable and
admin.hasAccess('edit', object)
%}
{% set x_editable_type = field_description.type|sonata_xeditable_type %}
{% if is_editable and x_editable_type %}
{% block field_span_attributes %}
{% spaceless %}
{{ parent() }}
data-source="{{ field_description|sonata_xeditable_choices|json_encode }}"
{% endspaceless %}
{% endblock %}
{% endif %}
They check if the field is editable and add data-source attribute to the field_span_attributes block of the parent base_list_field template part:
<span {% block field_span_attributes %}class="x-editable"
data-type="{{ xEditableType }}"
data-value="{{ data_value }}"
data-title="{{ field_description.label|trans({}, field_description.translationDomain) }}"
data-pk="{{ admin.id(object) }}"
data-url="{{ url }}" {% endblock %}>
{{ block('field') }}
</span>
So try adding the data-source in your custom template as well.
This answer worked for me:
If you check the HTML code, on your element choice, of your site you can detect if any tag is missing, for example the code below is the code that generate the page without the 'template' option configured:
<span class="x-editable label-info editable editable-click editable-open" data-type="select"
data-value="Rechazado"
data-title="Estado"
data-pk="4"
data-url="/eventos/web/app_dev.php/admin/core/set-object-field-value?context=list&field=estado&objectId=4&code=admin.evento"
data-source="[{"value":"Aprobado","text":"1"},{"value":"Pendiente","text":"Pendiente"},{"value":"Rechazado","text":"Rechazado"}]" data-original-title="" title="" aria-describedby="popover786605">
</span>
And now check the code generated when the 'template' option is on set
<span class="x-editable label label-danger editable editable-click"
data-type="select"
data-value="Rechazado"
data-title="Estado"
data-pk="4"
data-url="/eventos/web/app_dev.php/admin/core/set-object-field-value?context=list&field=estado&objectId=4&code=admin.evento">
</span>
The data-source field is missing and that is the reason why its crash.
The solution is simple (isn't the best but it is something) on your template add the data-source with the data of your choice.

Creating a dynamic tree in symfony 2

I am beginner in symfony 2. I have a project and I want to create a dynamic tree using css, this tree will display my categories levels.
I already tried to do it by myself, I was able to display the first 3 levels of my categories using 3 loops, but other levels are not displaying.
What I want is to know if I can use recursion or an other way to display all categories as a tree.
this the action method i used :
public function afficherArborescenceAction(){
$em = $this->getDoctrine()->getManager();
$categories = $em->getRepository('PortfolioBundle:Categorie')->findByCategorieParent();
$sousCategories = $em->getRepository('PortfolioBundle:Categorie')->findSousCategories();
return $this->render('AdministrateurBundle:NouvelleCategorie:showCategories.html.twig',array('categories'=>$categories,'sousCategories'=>$sousCategories));
}
This my html code :
<div style="border:1px #999999 solid;border-radius: 5px;width:350px;margin: 100px auto;padding:10px;">
<p>Les catégories:</p>
{% for categorie in categories %}
<ul class="tree">
<li>{{ categorie.libelle }}
{% for sousCategorie in sousCategories %}
<ul>
{% if sousCategorie.categorieParent == categorie.id %}
<li>
{{ sousCategorie.libelle }}
{% for sous in sousCategories %}
{% if sous.categorieParent == sousCategorie.id %}
<ul>
<li>
{{ sous.libelle }}
</li>
</ul>
{% endif %}
{% endfor %}
</li>
{% endif %}
</ul>
{% endfor %}
</li>
</ul>
{% endfor %}
This is the current tree i get, using the code above :
By using StofDoctrineExtensionBundle it is very simple to do what you want. For example this would generate nested <ul> of your category tree:
$htmlTree = $repository->childrenHierarchy(
null,
false,
[
'decorate' => true,
'nodeDecorator' => function ($node)
{
return "$node[name]";
},
//'rootOpen' => '<ul>', leave it as is
//'rootClose' => '</ul>',
'childOpen' => function ($node)
{
return "<li data-node-id=\"$node[id]\">";
},
'childClose' => '</li>',
],
true
);
$io->writeln($htmlTree);
Here is fully working example, complete process is covered, from configuring to the more advanced queries: Hierarchical Data in Relational Databases with Symfony 4 and Doctrine.
Check there, with the component Tree:
https://github.com/stof/StofDoctrineExtensionsBundle/blob/master/Resources/doc/index.rst
It can do what you need exactly

How to auto check checkboxes using Twig template?

I want to auto-check check boxes in the HTML. I've managed to get this to work, but it's kinda messy in the template:
<ul>
{% for tag in tags %}
{% set selected = false %}
{% for article_tag in article.tags %}
{% if article_tag.id == tag.id %}
{% set selected = true %}
{% endif %}
{% endfor %}
<li><input type="checkbox" name="tags[]" value="{{ tag.id }}" {% if selected %}checked{% endif %}> {{ tag.name }}</li>
{% endfor %}
</ul>
So the data I'm loading in is like this (in JSON format):
[
'tags' => [
{'id'=> 1, 'name'=>'Travel'},
{'id'=> 2, 'name'=>'Cooking'},
],
'article' => {
'tags' => [
{'id'=> 1, 'name'=>'Travel'},
],
}
]
Also, I'm not using Symfony (I'm using Slim's Twig library) so not sure if Symfony has some stuff in it's framework for doing stuff with Twig. If so, it won't work for me :(
The problem is the article is an array, so or you need to cycle on it for every tags array that contain or you simply access on it of the first element as follow:
{% for article_tag in article[0].tags %}
Instead of:
{% for article_tag in article.tags %}
See the result on this working twigfiddle
Hope this help

Symfony form collection rendering

I'm using symfony 2.3
I have form with field of type "collection"
Visualization code in twig:
{% for field in form.fields %}
{{ form_row(field.name) }}
{% endfor %}
Everything work, expect when form.fields is empty.
Then nothing is visualized in twig loop, witch is fine.
But at the end of the form there is "label" for the element "form.fields". Only label.
Workaround:
{% for field in form.fields %}
{{ form_row(field.name) }}
{% endfor %}
<div class="hidden">
{{ form_row(form.fields) }}
If there are elements, they will be rendered in the loop.
{{ form_row }} will be empty, because all elemets are iterated in the loop above.
But if form.fields is empty then there is "hidden" (in the div) label.
What I'm missing !? Why this is happening !?
Hidden div content:
<div class="form-group"><label class="col-sm-2 control-label required">name</label><div class="col-sm-10"><div id="my-id" data-prototype=""></div></div></div>
Builder config:
$builder->add(
'fieldDataMappers',
'collection',
array(
'type' => new FieldDataType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
)
);
As you have correctly guessed, the Symfony TwigBridge keep track of what is rendered and what is not. This is useful, since there is a function called form_rest(form), which is especially useful for printing hidden form field, and to prevent the "great jupiter! I forgot to print that field!" moments. :) You often find form_rest at the end of the form, just before the submit button.
Also consider that the collection IS a composite form type, which contains a variable list of child form. When the for loop is not triggered, since the form type is empty, the call to {{ form_row(form.fields) }} print out the collection form type. By default, this will print (you've guessed it) the collection label and an empty div. On the other hand, when the collection is not empty, Symfony will consider the collection as rendered, since all children are already rendered (see FormView::isRendered)
You can take a look into Symfony standard theme form_div_layout.html.twig, especially the blocks form_row (which show label printing) and form_widget_compound (the div and the for loop).
So, if you just need to hide the label (quick and dirty, some div are still there), just use:
$builder->add(
'fieldDataMappers',
'collection',
array(
'type' => new FieldDataType(),
'label' => false,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
)
);
Or better, simply output the whole collection widget, without row:
{{ form_widget(form.fieldDataMappers) }}
Or even better, you print the whole collection with:
{{ form_row(form.fieldDataMappers) }}
...and then add a Twig theme to customize the collection output with something like (note the name syntax, and the missing form_label call):
{% block collection_row -%}
<div>
{{- form_errors(form) -}}
{{- form_widget(form) -}}
</div>
<div class="hidden">Something here?</div>
{%- endblock collection_row %}
Hope this help!
{# src/Acme/TaskBundle/Resources/views/Task/new.html.twig #}
{# ... #}
{{ form_start(form) }}
{# render the task's only field: description #}
{{ form_row(form.description) }}
<h3>Tags</h3>
<ul class="tags">
{# iterate over each existing tag and render its only field: name #}
{% for tag in form.tags %}
<li>{{ form_row(tag.name) }}</li>
{% endfor %}
</ul>
{{ form_end(form) }}
{# ... #}
Symfony2 cookbook
http://symfony.com/doc/current/cookbook/form/form_collections.html
Also the field of the collection is named fieldDataMappers not field.
So i think it should be
{% for field in form.fieldDataMappers %}
{{ form_row(field.name) }}
{% endfor %}
{{ form_label(form.emails) }}
<ul id="email-fields-list"
data-prototype="{{ form_row(form.emails.vars.prototype)|e }}"
data-widget-tags="{{ '<ol></ol>'|e }}"
data-widget-counter="{{ form.emails|length }}">
{% for email in form.emails %}
<ol>
{{ form_errors(email) }}
{{ form_row(email) }}
</ol>
{% endfor %}
</ul>
<button type="button" class="add-another-collection-widget" data-list-selector="#email-fields-list">Add email</button>
{{ form_widget(form.emails) }}
I just add {{ form_widget(form.emails) }} after block thant handles adding to collection and no more label on the end of form.
Cheers
I solved this with :
{{ form_label(form.collection) }}
{% for element in form.collection %}
{{ form_widget(element) }}
{% else %}
{{ form_widget(form.collection) }}
{% endfor %}
(a bit late, I know, but still a problem with Symfony 5)

Categories