This course is still being released! Check back later for more chapters.

Get Notified About this Course!

We will send you messages regarding this course only
and nothing else, we promise.
You can unsubscribe anytime by emailing us at:
privacy@symfonycasts.com
Login to bookmark this video
Buy Access to Course
06.

Fetching a Relation's Data

|

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

Navigate to the homepage and click on any of the starships with an 'In Progress' status.

Hey! We're already listing the parts... sort of... these are all hard-coded!

Now, how do we get the parts that are related to this ship?

Open the controller for this page: src/Controller/StarshipController.php

To query for parts, we would typically autowire the StarshipPartRepository. Start the same way here with a StarshipPartRepository $partRepository argument:

28 lines | src/Controller/StarshipController.php
// ... lines 1 - 5
use App\Repository\StarshipPartRepository;
// ... lines 7 - 12
class StarshipController extends AbstractController
{
#[Route('/starships/{slug}', name: 'app_starship_show')]
public function show(
// ... lines 17 - 18
StarshipPartRepository $partRepository,
): Response {
// ... lines 21 - 25
}
}

Next, set $parts, to $partRepository->findBy():

This is pretty standard stuff: if you want to query where some property equals a value, use findBy() and pass the property name and the value. When it comes to relationships, it's the same darn thing!

$parts = $partRepository->findBy(['starship' => $ship]).

And no, we're not doing Starship ID or anything of the sort. Keep IDs out of this! Instead, pass the Starship object itself. You could pass the id if you're feeling lazy, but in the spirit of Doctrine, relationships, and thinking about objects, passing the entire Starship object is the way to go.

Let's debug and see what we've got: dd($parts):

28 lines | src/Controller/StarshipController.php
// ... lines 1 - 12
class StarshipController extends AbstractController
{
// ... line 15
public function show(
// ... lines 17 - 18
StarshipPartRepository $partRepository,
): Response {
$parts = $partRepository->findBy(['starship' => $ship]);
dd($parts);
// ... lines 23 - 25
}
}

Refresh, and voila! An array of 10 StarshipPart objects, all related to this Starship. Pretty awesome, right? If you think so, hold onto your pants.

Replace $parts with $ship->getParts():

28 lines | src/Controller/StarshipController.php
// ... lines 1 - 12
class StarshipController extends AbstractController
{
// ... line 15
public function show(
// ... lines 17 - 19
): Response {
// ... line 21
dd($ship->getParts());
// ... lines 23 - 25
}
}

Refresh! Instead of an array of StarshipPart objects, we get a PersistentCollection object that looks... empty. Remember the ArrayCollection that make:entity added to our Starship constructor? PersistentCollection and ArrayCollection are part of the same collection family. They're objects but they act like arrays. Cool... but why does this collection look empty? It's because Doctrine is smart: it doesn't query for the parts until we need them. Loop over $ship->getParts() and dump $part:

32 lines | src/Controller/StarshipController.php
// ... lines 1 - 12
class StarshipController extends AbstractController
{
// ... line 15
public function show(
// ... lines 17 - 19
): Response {
// ... lines 21 - 22
foreach ($ship->getParts() as $part) {
dump($part);
}
// ... lines 26 - 29
}
}

Suddenly that empty-looking collection is full of the 10 StarshipPart objects. Magic!

Lazy Relation Queries

There are two queries at play here. The first one is for the Starship, and the second one is for all its StarshipParts. The first comes from Symfony querying for the Starship based on the slug. The second is more interesting: happens the moment we foreach over the parts. At that exact instance Doctrine says:

I just remembered: I don't actually have the StarshipParts data for this Starship. Let me go and get that.

Isn't that just amazing? Makes me want to throw a party for Doctrine.

Tidying Up and Looping Over Parts

Get rid of the parts variable entirely... and remove StarshipPartRepository: that was way too much work. Instead, set a parts variable to $ship->getParts():

26 lines | src/Controller/StarshipController.php
// ... lines 1 - 12
class StarshipController extends AbstractController
{
#[Route('/starships/{slug}', name: 'app_starship_show')]
public function show(
#[MapEntity(mapping: ['slug' => 'slug'])]
Starship $ship,
): Response {
return $this->render('starship/show.html.twig', [
'ship' => $ship,
'parts' => $ship->getParts(),
]);
}
}

Now that we've got our shiny new parts variable, Loop over that in the template. Open up templates/starship/show.html.twig and replace the hard-coded part with our loop: for part in parts, part.name, part.price, part.notes, endfor:

79 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 - 61
<ul class="text-sm font-medium text-slate-400 tracking-wide">
{% for part in parts %}
<li class="border-b border-slate-600 py-2">
<span class="block text-white font-semibold">
{{ part.name }} (✦ {{ part.price }})
</span>
<span class="text-xs text-slate-500 italic">
{{ part.notes }}
</span>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
{% endblock %}

Still too much Work?

And we've done it! We've managed to display all 10 related parts, without doing any serious work thanks to $ship->getParts().

But you know what? Even this is too much work. Get rid of the parts variable entirely:

25 lines | src/Controller/StarshipController.php
// ... lines 1 - 12
class StarshipController extends AbstractController
{
// ... line 15
public function show(
// ... lines 17 - 18
): Response {
return $this->render('starship/show.html.twig', [
'ship' => $ship,
]);
}
}

for part in ship.parts:

79 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 - 61
<ul class="text-sm font-medium text-slate-400 tracking-wide">
{% for part in ship.parts %}
// ... lines 64 - 71
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
{% endblock %}

And... still not broken! For kicks and giggles, let's also display the number of parts for this ship. ship.parts|length:

79 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">
Parts ({{ ship.parts|length }})
</h4>
// ... lines 62 - 73
</div>
</div>
</div>
</div>
{% endblock %}

We still have two queries, but Doctrine, again is smart. It knows we've already queried for all the StarshipParts, so when we count them, we don't actually need to make another count query.

Next up: we'll talk about an often-misunderstood topic in Doctrine relations: the owning vs inverse side of each relationship.