3 Level Deep Timber menu (Wordpress) - wordpress

I'm not familiar with timber at all, but am helping a friend finish up a project that was built with it. So any help would go a long way please!
I have only the first two tiers showing up. Is there a way to call on the child of a child?
I'm using the code here, and added to it another tier under child https://timber.github.io/docs/guides/menus/
{% if menu %}
<div class="header-menu-items">
<div class="header-menu-item mod-title">
<a href="{{ site.url }}" class="" rel="home">
<div class="header-item-logo">
<div class="sitelogo">{{ site.name }}</div>
</div>
</a>
</div>
{% for item in menu.get_items() %}
<div class="header-menu-item {{ item.current ? 'is-active' : '' }}">
<div class="header-menu-item-link">
<a target="{{ item.target }}" href="{{ item.link }}">{{ item.title }}</a>
</div>
<div class="header-menu-item-triangle"></div>
<div class="header-menu-item-mega {{ item.section_colour ? " mod-#{item.section_colour}" : '' }}">
{% if item.master_object.thumbnail %}
<div class="mega-image mod-image" style="background-image: url( {{item.master_object.thumbnail.src('thumbnail') }} )">
{% else %}
<div class="mega-image">
{% endif %}
{{ item.title }}
</div>
<div class="mega-items">
{% for child in item.children %}
<div class="mega-item">
<a target="{{ child.target }}" href="{{ child.link }}">
<span class="mega-item-title">{{ child.title }}<br /></span>
<span class="mega-item-excerpt">Mega menu description lorem ipsum dolores</span>
</a>
</div>
{% for child in child.children %}
Just testing to see if it'll even show up first before applying style<br />
{{ child.title }}<br />
{% endfor %}
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}

You can access menus several layers deep by nesting for loops inside of one another. Here is a code snippet that I have tested and works.
{% for item in menu__main.items %} {# This is the top level #}
<p>{{ item }}</p>
{% if item.children %}
{% for child in item.children %} {# 2nd Level #}
<p><em>{{ child }}</em></p>
{% if child.children %}
{% for third in child.children %} {# 3rd Level #}
<p><strong>{{ third }}</strong></p>
{% endfor %} {# for third in child.children #}
{% endif %} {# if child.children #}
{% endfor %} {# for child in item.children #}
{% endif %} {# if item.children #}
{% endfor %} {# for item in menu__main.items #}
I have added comments to the end of the lines to hopefully make this more clear. So at the top, you are looping through item in menu__main.items
Then to get the children inside of these, you loop through item.children since item is the variable that represents each nav item at the top/main level. You loop through item.children to get to the next level or the children inside of the main/top level.
Then to get inside of the third level, you loop through child.children since child is the variable that represents the 2nd level. We want to loop through the children of this 2nd level. so we do third in child.children. third is the variable that represents the items 3 levels down.
I hope you can see the pattern here and you can continue this even further if you have items at even deeper levels, although at some point it will probably get ridiculous. Like if you have items nested 5 or 6 levels deep.
Let me know if that makes sense and if not I will be more than happy to try and explain it another way if you still have questions.
Cheers

Related

How to access peekAll() in base.html.twig to check for messages/flashes

In symfony 4.1
In the controler:
$this->addFlash(
'notice',
'Your changes were saved!'
);
return $this->render(...);
Twig base template:
{% for message in app.flashes('notice') %}
<div class="flash-notice">
{{ message }}
</div>
{% endfor %}
That works fine, but I want to wrap ALL messages in a div. The below does not work:
{% if app.flashes is not empty %}
<div id="messageBox" class="overlay">
<div class="message" onclick="slide(document.querySelector('#messageBox')); this.classList.add('fadeAway');">
{% for label, messages in app.flashes %}
<section class="{{ label }}">
<p><b>{{ label }}</b></p>
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</section>
{% endfor %}
</div>
</div>
{% endif %}
The problem is that the line {% if app.flashes is not empty %} erases the messages, so the following for loops and empty result.
How do I use peekAll() for the if logic?
The correct way to check with peekAll() is {% if app.session.flashbag.peekAll() is not empty %} This will not clear the flashBag as per documentation.
I do not know if this is the best way to do this, but the following is my solution.
In the base.html.twig:
{% set flashes = app.flashes %}
{% if flashes is not empty %}
<div id="messageBox" class="overlay">
<div class="message" onclick="slide(document.querySelector('#messageBox')); this.classList.add('fadeAway');">
{% for label, messages in flashes %}
<section class="{{ label }}">
<p><b>{{ label }}</b></p>
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</section>
{% endfor %}
</div>
</div>
{% endif %}
Basically, set the returned value of app.flashes to flashes and handle that.

Twig, how to print a nl2br output in one line

I used the nl2br filter like this:
{{ knp_pagination_render(requests)|raw|nl2br}}
It prints the html object separated by a few line breaks, and I need to print all the objects in one single line. How can I reach that?
Any help would be great.
{{ string | replace({"\\n":""}) }}
See fiddle.
I suggest you to override the default pagination template, so you can customize how to generate the underling HTML code.
As described in the doc You can do simply:
{{ knp_pagination_render(request, 'MyBundle:Pagination:pagination.html.twig') }}
You can copy the current pagination implementation from here and customize it as you need.
This is the default implementation:
{# default Sliding pagination control implementation #}
{% if pageCount > 1 %}
<div class="pagination">
{% if first is defined and current != first %}
<span class="first">
<<
</span>
{% endif %}
{% if previous is defined %}
<span class="previous">
<
</span>
{% endif %}
{% for page in pagesInRange %}
{% if page != current %}
<span class="page">
{{ page }}
</span>
{% else %}
<span class="current">{{ page }}</span>
{% endif %}
{% endfor %}
{% if next is defined %}
<span class="next">
>
</span>
{% endif %}
{% if last is defined and current != last %}
<span class="last">
>>
</span>
{% endif %}
</div>
{% endif %}

Recursive parsing of hierarchical Twig layout with parents, children, grandchildren

In my SF2 project I have an entity (Category) which I am representing in a hierarchical format with a parent at the top, followed by children, grandchildren etc.
The Category entity has a getChildren method, which works and returns Category entity objects.
I'm trying to work out a way to make this layout more dynamic, rather than having to explicitly set children and grandchildren variables within the template.
Is there a better way to do this?
<ul class="sortable">
{% for cat in cats %}
{% set children = cat.getChildren %}
<li id="menuItem_{{ cat.id }}">
<div data-id="{{ cat.id }}">
<span>{{ cat.name }}</span>
</div>
{% for child in children %}
{% set grandchildren = child.getChildren %}
<ul>
<li id="menuItem_{{ child.id }}">
<div data-id="{{ child.id }}">
{{ child.name }}
</div>
{% for grandchild in grandchildren %}
<ul>
<li id="menuItem_{{ grandchild.id }}">
<div data-id="{{ grandchild.id }}">
{{ grandchild.name }}
</div>
</li>
</ul>
{% endfor %}
</li>
</ul>
{% endfor %}
</li>
{% endfor %}
</ul>
so what you are trying to achieve is recursive parsing of a tree in twig right?
If so, have a look at macros
.
{% import _self as macros %}
{% macro showChild(object) %}
{% import _self as macros %}
<ul>
{% for child in object.children %}
{{ macros.showChild(child) }}
{% endfor %}
<li id="menuItem_{{ object.id }}">
<div data-id="{{ object.id }}">
{{ object.name }}
</div>
</li>
</ul>
{% endmacro %}
<ul class="sortable">
{% for cat in cats %}
{{ macros.showChild(cat) }}
{% endfor %}
</ul>
that's all :)
let me know if you need help
EDIT 1:
If you want to use the macro in another file, remove the "import _self" line and just import it with an alias in another file:
index.html.twig:
{% import 'macro_file_name.html.twig' as macros %}
then you can use the same notation to call it

Twig: Loop Index and Second Value for each Iteration

i got the following loop
{% set services = { "ceoCentralServices": ceoCentralServices, "cfoCentralServices": cfoCentralServices, "cooCentralServices": cooCentralServices} %}
{% for events, serviceEvents in services %}
{% if serviceEvents %}
<div class="wrapItFine" style="background:purple;">
{% for event in serviceEvents %}
<div class="dialog" data-index="loop2{{ loop.index0 }}">
<li class="contentli">{{ event.value }}</li>
</div>
<div style="display:none;"
id="anmelden_boxloop2{{ counter }}{{ loop.index0 }}"
class="{{ event.value }}{{ loop.index0 }}" >
{% include 'ansprechpartnerSingle.twig' %}
</div>
{% endfor %}
</div>
{% endif %}
{% endfor %}
this returns 3 boxes with data-index="loop2+index.
the problem is, that i need in each loop a different value like loop2+index, loop3+index, loop4+index
i tried a set Counter and incrementing in the for loop which returned every time the same value.
feel free to downvote, like everytime :-)
in your second loop you can simply use
{{ loop.parent.loop.index0 }}
to get the index of the parent loop, which you can use instead of your counter
data-index="loop{{ loop.parent.loop.index0 }}{{ loop.index0 }}"

Symfony2, Twig, change twig after include

I have such hierarchy of twig files
my main (for controller) twig
{% extends "MainSiteBundle::layout.html.twig" %}
{% block footer_moderator_buttons %}
some buttons
{% endblock %}
{% block content_body %}
<p>hello moderator</p>
{{ include ('MainBlogBundle:_parts:postList.html.twig', {'postList': aPostDraft}) }}
{% endblock %}
postList.html.twig
<div class="post-list">
{% for postSingle in postList %}
{{ include ('MainBlogBundle:_parts:postSingle.html.twig', {'postSingle': postSingle}) }}
{% endfor %}
</div>
postSingle.html.twig
<div class="post">
<div class="post-header">
<a class="title" href="3">{{ postSingle.title }}</a>
</div>
<div class="post-meta">
<div>Date: {{ postSingle.date|date('D M Y') }}</div>
<div>Category: {{ postSingle.getCategory.getTitle }}</div>
<div>Author: {{ postSingle.getUser.username }}
</div>
</div>
<div class="post-body">
<div class="content">
<img width="450" height="200" src="#">
<div class="text">{{ postSingle.content }}</div>
</div>
</div>
<div class="post-footer">
{% block footer_moderator_buttons %}f{% endblock %}
<div>Views: 152</div>
<div>Comments: 1231</div>
<div>
<a class="link" href="#">More... </a>
</div>
</div>
</div>
As you can see last (postSingle.html.twig) has block "footer_moderator_buttons", so how can i change it from main twig (first one) ? Current is not working, what I need change \ do ?
In Twig 1.8 there is embed tag (http://twig.sensiolabs.org/doc/tags/embed.html).
You would have to remove the postList.html.twig file or work around it.
{% embed "MainBlogBundle:_parts:postSingle.html.twig" with {'postSingle': postSingle} %}
{% block footer_moderator_buttons %}
custom buttons here
{% endblock %}
{% endembed %}
So, you question is actually "I want to understand how\what twig can". Well, the answer to that question is: "It can't overwrite blocks from "main" to the smaller ones."
If you want to use twig, you have to stop thinking in php include() way where you make new files and you "PUT" components in them over and over again, components such as header, footer, menu etc.
In twig, you define main twig file with blocks which can be imagined as empty spaces which can, but do not have to, be overwritten. Surely, it still means you can have postList.html.twig as include in some file that extends MainSiteBundle::layout.html.twig. The same goes for postSingle.html.twig.
I think you catch the logic of twig except don't try to overwrite blocks from the wrong side - in this case, from MainSiteBundle::layout.html.twig to it's smaller portions.
how about this:
{% extends "MainSiteBundle::layout.html.twig" %}
....
{% block footer_moderator_buttons %}
{{ parent() }}
{% endblock %}
woops didnt put parent..
the {{ parent() }} will inherit {% block footer_moderator_buttons %}{% endblock %} from extended twig.

Resources