15.

Twig Partials & for Loops

|

Share this awesome video!

|

We just gave our site a design makeover... which means we updated our templates to include HTML elements with a bunch of Tailwind classes. The result? A site that's easy on the eyes.

For some parts of the templates, things are still dynamic: we have Twig code to print out the captain and class. But in other parts, everything is hard-coded. And... this is pretty typical: a frontend developer might code up the site in HTML & Tailwind... but leave it for you to make it dynamic and bring it to life.

Organizing into a Template Partial

At the top of homepage.html.twig, this long <aside> element is the sidebar. It's fine that this code lives in homepage.html.twig... but it does take up a lot of space! And what if we want to reuse this sidebar on another page?

One great feature of Twig is the ability to take "chunks" of HTML and isolate them into their own templates so you can reuse them. These are called template partials... since they hold code for just part of the page.

Copy this code, and in the main/ directory - though this could go anywhere - add a new file called _shipStatusAside.html.twig. Paste inside.

<aside
class="pb-8 lg:pb-0 lg:w-[411px] shrink-0 lg:block lg:min-h-screen text-white transition-all overflow-hidden px-8 border-b lg:border-b-0 lg:border-r border-white/20"
>
<div class="flex justify-between mt-11 mb-7">
<h2 class="text-[32px] font-semibold">My Ship Status</h2>
<button>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 448 512"><!--!Font Awesome Pro 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2024 Fonticons, Inc.--><path fill="#fff" d="M384 96c0-17.7 14.3-32 32-32s32 14.3 32 32V416c0 17.7-14.3 32-32 32s-32-14.3-32-32V96zM9.4 278.6c-12.5-12.5-12.5-32.8 0-45.3l128-128c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L109.3 224 288 224c17.7 0 32 14.3 32 32s-14.3 32-32 32l-178.7 0 73.4 73.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-128-128z"/></svg>
</button>
</div>
<div>
<div class="flex flex-col space-y-1.5">
<div class="rounded-2xl py-1 px-3 flex justify-center w-32 items-center" style="background: rgba(255, 184, 0, .1);">
<div class="rounded-full h-2 w-2 bg-amber-400 blur-[1px] mr-2"></div>
<p class="uppercase text-xs">in progress</p>
</div>
<h3 class="tracking-tight text-[22px] font-semibold">
<a class="hover:underline" href="{{ path('app_starship_show', {
id: myShip.id
}) }}">{{ myShip.name }}</a>
</h3>
</div>
<div class="flex mt-4">
<div class="border-r border-white/20 pr-8">
<p class="text-slate-400 text-xs">Captain</p>
<p class="text-xl">{{ myShip.captain }}</p>
</div>
<div class="pl-8">
<p class="text-slate-400 text-xs">Class</p>
<p class="text-xl">{{ myShip.class }}</p>
</div>
</div>
</div>
</aside>

Back in homepage.html.twig, delete that, then include it with {{ - so the say something syntax - include() and the name of the template: main/_shipStatusAside.html.twig.

54 lines | templates/main/homepage.html.twig
// ... lines 1 - 4
{% block body %}
<main class="flex flex-col lg:flex-row">
{{ include('main/_shipStatusAside.html.twig') }}
// ... lines 8 - 51
</main>
{% endblock %}

Try it out! And... no change! The include() statement is simple:

Render this template and give it the same variables that I have

If you're wondering why I prefixed the template with an underscore... no reason! It's just a convention that helps me know that this template holds only a part of the page.

Looping over the Ships in Twig

In the homepage template, we can focus on the ship list below, which is this area. Right now, there's just one ship... and it's hard-coded. Our intention is to list every ship that we're currently repairing. And we do already have a ships variable that we're using at the bottom: it's an array of Starship objects.

So for the first time in Twig, we need to loop over an array! To do that, I'll remove this comment, and say {% - so the do something tag - then for ship in ships. ships is the array variable we already have and ship is the new variable name in the loop that represents a single Starship object. At the bottom, add {% endfor %}.

54 lines | templates/main/homepage.html.twig
// ... lines 1 - 4
{% block body %}
<main class="flex flex-col lg:flex-row">
// ... lines 7 - 8
<div class="px-12 pt-10 w-full">
// ... lines 10 - 13
<div class="space-y-5">
{% for ship in ships %}
// ... lines 16 - 43
{% endfor %}
</div>
// ... lines 46 - 50
</div>
</main>
{% endblock %}

And already... when we try it, we get three hard-coded ships! That's an improvement!

Next: it's time for a plot twist that'll lead us to creating a PHP enum.