Use parent and child blocks in a for-loop, including variables - php

Is there are way I can create an easily maintainable for loop in Twig that works with Bootstrap's layout?
I'm using bootstrap's row functionality, meaning that each row has a div of class "row". This makes things tricky in twig, because I have no way of entering these header divs.
For example, my main page will have several articles, and extends the skeleton template, as shown below. Is there any way I can use this skeleton base and use it multiple times in a for loop?
I've tried by adding the Article block, but it overrides everything inside. Do I have to create another template for this to work?
<div id="main" class="container-fluid">
{% block article %}
<div class="row">
<div class="col-md-3"></div>
<div id="title" class="col-md-6">
{% block title %}
{% endblock title %}
</div>
<div class="col-md-3"></div>
</div>
<div class="row">
<div class="col-md-1"></div>
<div id="left-nav" class="col-md-2">
{% block left_nav %}
{% endblock left_nav %}
</div>
<div class="col-md-6" id="content">
{% block content %}
{% endblock content %}
</div>
<div id="right-nav" class="col-md-2">
{% block right_nav %}
{% endblock right_nav %}
</div>
<div class="col-md-1"></div>
</div>
{% endblock article %}
</div>
How do I iterate and create multiple "Articles" in this context?
Using parent() isn't helpful because it'll just print the start and end tags, wheras I need them to be placed around the correct sections, otherwise the rows won't work.
In other words, in a for loop context, how can I change the values of variables inside child blocks within a parent block?
Apologies if this is unclear, I found it hard to put into words.
Edit:
I know the existence of for loops in twig, I am just unsure of how to implement a solution to this particular problem.
As a simple example of the skeleton template and its output:
Template
<div class="row">
<div class="title">
{% block title %}{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</div>
Output
<div class="row">
<div class="title">
This is the first post.
</div>
<div id="content">
Post 1
</div>
</div>
<div class="row">
<div class="title">
This is the second post.
</div>
<div id="content">
Post 2
</div>
</div>

I found a solution that is fairly maintainable, as it still extends the skeleton template, but is flexible enough to support for loops inside a block.
Skeleton
<div id="main" class="container-fluid">
{% block article %}
<div class="row">
<div class="col-md-3"></div>
<div id="whitespace" class="col-md-6">
{% block title %}
{% endblock title %}
</div>
<div class="col-md-3"></div>
</div>
<div class="row">
<div class="col-md-1"></div>
<div id="left-nav" class="col-md-2">
{% block left_nav %}
{% endblock left_nav %}
</div>
<div class="col-md-6" id="content">
{% block content %}
{% endblock content %}
</div>
<div id="right-nav" class="col-md-2">
{% block right_nav %}
{% endblock right_nav %}
</div>
<div class="col-md-1"></div>
</div>
{% endblock article %}
</div>
Article list
{% extends 'skeleton.php' %}
{% block article %}
{% for post in posts %}
<div class="row">
{# Start title #}
<div class="col-md-3"></div>
<div id="title" class="col-md-6">
{{post.title}}
</div>
<div class="col-md-3"></div>
{# End title #}
</div>
<div class="row">
<div class="col-md-1"></div>
{# Start left-nav #}
<div id="left-nav" class="col-md-2">
{{post.left}}
</div>
{# end left-nav #}
{# Start content #}
<div class="col-md-6" id="content">
{{post.getBody}}
</div>
{# end content #}
{# start right-nav #}
<div id="right-nav" class="col-md-2">
{{post.right}}
</div>
{# end right-nav #}
<div class="col-md-1"></div>
</div>
{% endfor %}
{% endblock article %}

{% foreach article in articles %}
<div class="row">
<div class="title">
{{ article.title }}
</div>
<div id="content">
{{ article.content }}
</div>
</div>
{% endfor %}
Is the template assuming articles is an array containing arrays with keys title and content

Related

Twig how to check which template is geting rendered

I have
<div>
<div id="page-wrapper" class="sidebar-content white-bg">
<div class="row border-bottom">
{% include '/layouts/navbar.html.twig' %}
</div>
{% block body %}{% endblock %}
</div>
</div>
Now in this /layouts/navbar.html.twig theres an anchor tag, who's href attribute heavily depends on which template is rendered below, for example if its the Posts template, the href should be /posts/new, if its the announcements template, the href should be /announcements/newand so on, is that even possible?
You could make use of the global variable _self to solve this e.g.
main.twig
{% include "foo.twig" %}
{% include "bar.twig" %}
foo.twig and bar.twig
<div>
<div id="page-wrapper" class="sidebar-content white-bg">
<div class="row border-bottom">
{% include "nav.twig" with { 'template': _self, } %}
</div>
{% block body %}{% endblock %}
</div>
</div>
nav.twig
{% set path = '' %}
{% if template == 'foo.twig' %}
{% set path = 'path/to/foo' %}
{% elseif template == 'bar.twig' %}
{% set path = 'path/to/bar' %}
{% endif %}
{% for i in 0..3 %}
{{ i }}
{% endfor %}
demo
If the path is the only thing that depends on the current template, I'd modify DarkBee's example to just simply pass the path from the parent template. That way you don't need the if/else structure:
posts.twig:
<div>
<div id="page-wrapper" class="sidebar-content white-bg">
<div class="row border-bottom">
{% include "nav.twig" with { 'path': '/posts/new' } %}
</div>
{% block body %}{% endblock %}
</div>
</div>
announcements.twig:
<div>
<div id="page-wrapper" class="sidebar-content white-bg">
<div class="row border-bottom">
{% include "nav.twig" with { 'path': '/announcements/new' } %}
</div>
{% block body %}{% endblock %}
</div>
</div>
nav.twig:
{% for i in 0..3 %}
{{ i }}
{% endfor %}

twig break out of loop and then continue the rest in next div

I am trying to loop through an array of meeting objects each meeting on same date needs to be displayed inside its own div and if the date changes then that date meeting needs to be in next div so the output i am looking for is something like this
<div class="row">
<h2 class="date-heading">September 24</h2>
<div class="col4">Meeting 1 </div>
<div class="col4">Meeting 2 </div>
<div class="col4">Meeting 3 </div>
<div class="col4">Meeting 4 </div>
<div class="col4">Meeting 5 </div>
</div>
<div class="row">
<h2 class="date-heading">September 25</h2>
<div class="col4">Meeting 1 </div>
<div class="col4">Meeting 2 </div>
<div class="col4">Meeting 3 </div>
<div class="col4">Meeting 4 </div>
<div class="col4">Meeting 5 </div>
</div>
<div class="row">
<h2 class="date-heading">September 26</h2>
<div class="col4">Meeting 1 </div>
<div class="col4">Meeting 2 </div>
<div class="col4">Meeting 3 </div>
<div class="col4">Meeting 4 </div>
<div class="col4">Meeting 5 </div>
</div>
Now inside the loop i have access to a parameter of each object called request.isNewDate, this is set to yes as soon as the current loop has a different date than the previous loop, i think i can use this to achieve what i need but i am having a problem in the logic.
This is what my code looks like where I am looping, how can i break out of this loop if the request.isNewDate is yes and then start the rest inside a new div.
Any help will be appreciated.
<div class="row">
{% for request in pagination %}
<div class="col4">
<h5 class="">{{ request.getGuest.getFirstName }}</h5>
<h6 class="text-transform-none">{{ request.getGuest.getCollege }}<br>
{{ request.getGuest.getDegree }}<br>
({{ request.getGuest.getCourse }})
</h6>
</div>
</div>
Try this snipped code:
{% for request in pagination %}
{#
If is a new date, open a new div TAG and dump the data.
If the first element of the collection do not have this info
consider to use loop.index0 as OR operation.
#}
{% if request.isNewDate %}
<div class="row">
{# print and format the date #}
<h2 class="date-heading">{{ request.date }}</h2>
{% endif %}
{# Render data of an element, consider to wrap this data in a single twig and incude #}
<div class="col4">
<h5 class="">{{ request.getGuest.getFirstName }}</h5>
<h6 class="text-transform-none">{{ request.getGuest.getCollege }}<br>
{{ request.getGuest.getDegree }}<br>
({{ request.getGuest.getCourse }})
</h6>
</div>
{# remember to close the div #}
{% if request.isNewDate %}
</div>
{% endif %}
{% endfor %}
Hope this help
Perform the logic in the controller and pass the data to Twig separately.
You don't want that kind of logic buried in a template. It will become a maintenance headache in the future and doing that sort of thing leads to cluttered, difficult to read templates.

Customize embedded form symfony 2

I have read the Symfony 2 documentation and I'm trying to make a custom embedded form and I can't understand the provided code in the documentation.
Official documentation:
http://symfony.com/doc/current/cookbook/form/form_customization.html
Specifically the code that i don't understand is this:
{% form_theme form _self %}
{% block _tasks_entry_widget %}
<tr>
<td>{{ form_widget(task.task) }}</td>
<td>{{ form_widget(task.dueDate) }}</td>
</tr>
{% endblock %}
after many tests I've noticed that '_task_entry' is the name of the embedded form (not the name of field in the main form)
Now I'm trying to get what is the 'task' variable, {{ form_widget(task.dueDate) }}
I've tried with the embedded form name again, with the name of the entity field, and with the name of the main form variable but nothing works:
{% form_theme edit_form.lineas _self %}
{% block zb_gestionbundle_lineaalbaran_widget %}
<div class="large-1 small-1 columns">
{{ form_widget(form.cantidad) }}
</div>
<div class="large-8 small-8 columns">
{{ form_widget(form.concepto) }}
</div>
<div class="large-2 small-2 columns">
{{ form_widget(form.precio) }}
</div>
{% endblock %}
{{ form_label(edit_form.lineas) }}
{{ form_errors(edit_form.lineas) }}
{{ form_widget(edit_form.lineas) }}
In summary, What I need to put in {{ form_widget(form.cantidad) }} for make the code works?
Tyvm!!
One possible solution:
After investigate a little more, I've found this code that works!
{% form_theme edit_form _self %}
{% macro prototype(linea) %}
<div class="large-1 small-1 columns">
{{ form_widget(linea.cantidad) }}
</div>
<div class="large-8 small-8 columns">
{{ form_widget(linea.concepto) }}
</div>
<div class="large-2 small-2 columns">
{{ form_widget(linea.precio) }}
</div>
{% endmacro %}
{% for linea in edit_form.lineas %}
{{_self.prototype(linea)}}
{% endfor %}
I don't know if the documentation is wrong, I leave the answer open for the doubt about the documentation.
Your solution work ! Just to complete, i had the same problem but the documentation is correct ! Just a little bit difficult to understand.(in my opinion).
To use the documentation solution :
you have to know the unique_block_prefix of your embbeded form.
To do this : add this to your code {{dump(form)}} and search the unique_block_prefix of your embedded form.
then you just have to replace the example of documentation like this :
{% form_theme form _self %}
{% block _zb_gestionbundle_lineaalbaran_entry_widget %}
<div class="large-1 small-1 columns">
{{ form_widget(form.cantidad) }}
</div>
<div class="large-8 small-8 columns">
{{ form_widget(form.concepto) }}
</div>
<div class="large-2 small-2 columns">
{{ form_widget(form.precio) }}
</div>
{% endblock %}
<!--block with your html/twig code, form, etc ..-->
{% block your_main_block %}
...
<!--your form-->
...
<!-- the embbeded part -->
{{form_row(form.lineas)}}
...
{% endblock %}
To sum up, generally the unique_block_prefix is
_(id of your embedded form)_entry_widget
And you just have to replace like the example of the doc.
I hope you understand and i miss nothing(sorry for my english...).

Twig custom text field not displaying - syntax wrong?

Is anyone here familiar with Twig? The Wordpress theme Realia is supposed to display a custom text field when the price field is left empty. But it doesn't. Here is the code that I think is responsible.
<div class="property span9">
<div class="row">
<div class="image span3">
<div class="content">
{% if wp.get_the_post_thumbnail(property.ID) %}
<img src="{{ wp.aq_resize(wp.wp_get_attachment_url(wp.get_post_thumbnail_id(property.ID), 'full'), 270, 200, 'true') }}" alt="{{ property.post_title }}">
{% else %}
<img src="{{ wp.get_template_directory_uri() }}/assets/img/property-tmp-small.png" alt="{{ property.post_title }}">
{% endif %}
</div>
<!-- /.content -->
</div>
<!-- /.image -->
<div class="body span6">
<div class="title-price row">
<div class="title span4">
<h2>{{ property.post_title }}</h2>
</div>
<!-- /.title -->
<div class="price">
{% if wp.get_post_meta(post.ID, '_property_custom_text').0 %}
{{ wp.get_post_meta(post.ID, '_property_custom_text').0 }}
{% else %}
{{ wp.aviators_price_format(property.meta._property_price.0) }}{% if property.meta._property_price_suffix.0 %}
<span class="suffix">{{ property.meta._property_price_suffix.0 }}</span>{% endif %}</div>
{% endif %}
<!-- /.price -->
</div>
<!-- /.title -->
<div class="location">{{ property.location.0.name }}</div>
<!-- /.location -->
<div class="body">
{{ wp.aviators_core_get_post_teaser(property.ID) }}
</div>
<!-- /.body -->
<div class="info clearfix">
{% if property.meta._property_area.0 %}
<div class="area">
<span class="key">{{ wp.__('Area', 'aviators') }}:</span><!-- /.key -->
<span class="value">{{ property.meta._property_area.0 }}{{ wp.aviators_settings_get_value('properties', 'units', 'area')|raw }}</span><!-- /.value -->
</div><!-- /.area -->
{% endif %}
{% if property.meta._property_bedrooms.0 %}
<div class="bedrooms" title="{{ wp.__('Bedrooms', 'aviators') }}">
<div class="content">{{ property.meta._property_bedrooms.0 }}</div>
</div><!-- /.bedrooms -->
{% endif %}
{% if property.meta._property_bathrooms.0 %}
<div class="bathrooms" title="{{ wp.__('Bathrooms', 'aviators') }}">
<div class="content">{{ property.meta._property_bathrooms.0 }}</div>
</div><!-- /.bathrooms -->
{% endif %}
</div>
<!-- /.info -->
</div>
<!-- /.body -->
</div>
<!-- /.row -->
</div><!-- /.property -->
Try:
{% if property.meta._property_price_suffix == false %}
The table at the end of the documentation at http://twig.sensiolabs.org/doc/tags/if.html is useful.

Symfony Twig override specific form row

I have the a form twig template, where I want to parse a specific fields help text with the raw filter (it contains html). The field is called postcode in a form called Clinic
According to here http://symfony.com/doc/current/cookbook/form/form_customization.html#how-to-customize-an-individual-field
Form template:
{% extends 'AgriHealthAhpBundle::admin.html.twig' %}
{% form_theme form 'AgriHealthAhpBundle:Form:fields.html.twig' %}
{% block _clinic_postcode_row %}
<div class="row">
test<div class="small-12 medium-3 columns label">{{ form_label(form) }}</div>
<div class="small-12 medium-6 columns widget">
{{ form_widget(form) }}
<div class="error">
{{ form_errors(form) }}
</div>
</div>
<div class="small-12 medium-3 columns help">
{% if help is defined %}
{{ help|raw }}
{% endif %}
</div>
</div>
{% endblock %}
{% block admin -%}
<h1>New Clinic</h1>
{{ form(form) }}
<div class="row form_actions">
<div class="small-12 medium-offset-3 medium-2 columns submit">
<button type="submit" id="agrihealth_ahpbundle_clinic_submit_visible" name="agrihealth_ahpbundle_clinic[submit]">Create</button>
</div>
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery('#agrihealth_ahpbundle_clinic_submit_visible').click(function(){
jQuery('form[name="agrihealth_ahpbundle_clinic"]').submit();
});
});
</script>
<div class="small-12 medium-2 columns cancel">
<a href="{{ path('clinic') }}">
Cancel
</a>
</div>
<div class="small-12 medium-2 end columns cancel">
<a href="{{ path('clinic') }}">
Back to List
</a>
</div>
</div>
{% endblock %}
AhpBundle/Resources/views/Form/fields.html.twig
{% block form_row %}
{% spaceless %}
<div class="row">
<div class="small-12 medium-3 columns label">{{ form_label(form) }}</div>
<div class="small-12 medium-6 columns widget">
{{ form_widget(form) }}
<div class="error">
{{ form_errors(form) }}
</div>
</div>
<div class="small-12 medium-3 columns help">
{% if help is defined %}
{{ help }}
{% endif %}
</div>
</div>
{% endspaceless %}
{% endblock form_row %}
Anyone can see what I have overlooked, I tried
{% block _clinic_postcode_row %}
and
{% block _Clinic_postcode_row %}
Solution
As per accepted answer, the form row block needs to fully idetified with shorthand bundle name. The easiest way is to view the source code of the form and identify the text used in each input field and the form name="":
Replace
{% form_theme form 'AgriHealthAhpBundle:Form:fields.html.twig' %}
with
{% form_theme form with ['AgriHealthAhpBundle:Form:fields.html.twig', _self] %}
Since you are decorating the row inside action template, while applying separate form template you need to specify multiple templates
You also need to specify a fully qualified path name to your row block such as
{% block _agrihealth_ahpbundle_clinic_postcode_row %}

Categories