Login to bookmark this video
Buy Access to Course
17.

Accessing Data on a ManyToMany

|

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Simple goal really: print all the droids assigned to a Starship. If you got comfortable with the OneToMany relation from Starship to its parts, then you're going to love this!

Open the template for the Starship show page: templates/starship/show.html.twig. I'll steal the h4 and p tag for arrived at, paste them below, and change the h4 to Droids. Clear out arrived at ... and break that line up:

90 lines | templates/starship/show.html.twig
// ... lines 1 - 4
{% block body %}
// ... lines 6 - 19
<div class="md:flex justify-center space-x-3 mt-5 px-4 lg:px-8">
// ... lines 21 - 25
<div class="space-y-5">
<div class="mt-8 max-w-xl mx-auto">
<div class="px-8 pt-8">
// ... lines 29 - 58
<h4 class="text-xs text-slate-300 font-semibold mt-2 uppercase">
Droids
</h4>
<p class="text-[22px] font-semibold">
// ... lines 63 - 67
</p>
// ... lines 69 - 84
</div>
</div>
</div>
</div>
{% endblock %}

We have a ship variable, which is a Starship object. And remember, it has a droids property and a getDroids() method. So, for droid in ship.droids. This calls the getDroids() method, and that returns a collection of Droid objects. So we can say {{ droid.name }}.

The loop.last

I want commas, but not an extra comma at the end. Say: {% if not loop.last %}, {% endif %}. There are fancier ways to do this, but keep it simple for now.

If there aren't any droids, use an else tag and say No droids on board (clean up your own mess). Rude!

90 lines | templates/starship/show.html.twig
// ... lines 1 - 4
{% block body %}
// ... lines 6 - 19
<div class="md:flex justify-center space-x-3 mt-5 px-4 lg:px-8">
// ... lines 21 - 25
<div class="space-y-5">
<div class="mt-8 max-w-xl mx-auto">
<div class="px-8 pt-8">
// ... lines 29 - 58
<h4 class="text-xs text-slate-300 font-semibold mt-2 uppercase">
Droids
</h4>
<p class="text-[22px] font-semibold">
{% for droid in ship.droids %}
{{ droid.name }}{% if not loop.last %}, {% endif %}
{% else %}
No droids on board (clean up your own mess)
{% endfor %}
</p>
// ... lines 69 - 84
</div>
</div>
</div>
</div>
{% endblock %}

Droids on the Homepage

On the homepage, we want to show off the droids here too. Open up the template: templates/main/homepage.html.twig. Right after parts, add another div with Droids: {{ ship.droidNames ?: 'none' }}:

77 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 - 17
<div class="space-y-5">
{% for ship in ships %}
<div class="bg-[#16202A] rounded-2xl pl-5 py-5 pr-11 flex flex-col min-[1174px]:flex-row min-[1174px]:justify-between">
<div class="flex justify-center min-[1174px]:justify-start">
<img class="h-[83px] w-[84px]" src="{{ asset(ship.statusImageFilename) }}" alt="Status: {{ ship.statusString }}">
<div class="ml-5">
// ... lines 24 - 36
<div>
Parts: {{ ship.parts|length }}</div>
<div>
Droids: {{ ship.droidNames ?: 'none' }}
</div>
</div>
</div>
// ... lines 44 - 54
</div>
{% endfor %}
</div>
// ... lines 58 - 73
</div>
</main>
{% endblock %}

The Smart Method

We could use our loop.last comma thing again, but we've needed the droid names in two spots, so let's add a smart method for this in the Starship class. This could go anywhere, but I'll stick it at the bottom with the other droid methods. Create a public function getDroidNames(): string. To return a comma-separated string of droid names, check this out: return implode(', ', $this->droids->map(fn(Droid $droid) => $droid->getName())->toArray()):

229 lines | src/Entity/Starship.php
// ... lines 1 - 15
class Starship
{
// ... lines 18 - 223
public function getDroidNames(): string
{
return implode(', ', $this->droids->map(fn(Droid $droid) => $droid->getName())->toArray());
}
}

Wow, that was a mouthful! Let's break it down:

First, $this->droids is our collection of Droid objects. Second, map() applies a function to each Droid in the collection. Third, fn(Droid $droid) => $droid->getName() is a hipster way to say:

Give me the name of each droid

Fourth, toArray() converts the collection to an array so it can be used with implode(). Finally, implode(', ', ...) takes that array of names and turns it into a comma-separated string.

Now that we've got a getDroidNames() method, we can say {{ ship.droidNames ?: 'none' }}.

We're done! Refresh... and enjoy the droid names on the homepage.

Next: let's use Foundry to set the ManyToMany relationship in the fixtures. Another place Foundry shines!