Login to bookmark this video
Buy Access to Course
21.

Persisting the More Complex Many-to-Many Relationship

|

Share this awesome video!

|

Lucky you! You found an early release chapter - it will be fully polished and published shortly!

This Chapter isn't quite ready...

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

We refactored our many-to-many relationship to include a join entity called StarshipDroid, instead of relying on Doctrine to create the join table for us. Reload our fixtures, but hold on to your hats:

symfony console doctrine:fixtures:load

Error!

Undefined property: App\Entity\Starship::$droids

This error is being spat out from Starship line 205. The culprit? Our getDroids() method. Well duh, we just removed the droids property! The quick fix, duh again, just comment it out. And huzzah! The fixtures are back in action:

symfony console doctrine:fixtures:load

Creating the Join Entity

To discover the right fix, let's do a few things manually: $ship = StarshipFactory, we could use createOne(), but let's grab a random one instead. Also use the _real() trick to get the actual object, not a proxy. Then do the same for $droid = DroidFactory, again grabbing a random one and calling _real() on that.

Relating via the Join Entity

Previously, we could used $ship->addDroid($droid) to add a droid to a Starship. But not anymore! It's referencing the obsolete droids property. It's now called starshipDroids, and as you might've guessed, it's a collection of StarshipDroid entities. Ditch $ship->addDroid() and instead say $starshipDroid equals new StarshipDroid(), then $starshipDroid->setDroid(), not $ship but $droid. And set $starshipDroid->setStarship($ship). We're manually creating the entity and setting those many-to-one relationships. Finally, because we're assembling these by hand, we need to persist and flush them using $manager->persist($starshipDroid), and $manager->flush().

It's definitely more work, but it's simple enough. Give the fixtures a spin:

symfony console doctrine:fixtures:load

And peek at the database with:

symfony console doctrine:query:sql "SELECT * from starship_droid"

We're selecting from that join table and yes! One entry for the one Starship, and the one Droid. So far, so good. Refresh the homepage. Another error! [Semantical Error] line 0, col 55 near 'droids WHERE': Class App\Entity\Starship has no association named droids. Looks like we've got a query issue on our hands.

Fixing the Query Issue

Time to roll up our sleeves and dive into src/Repository/StarshipRepository. Our join is having a bit of a meltdown. We're joining on s.droids, but the droids property has left the building. We need to join on starshipDroids. Change s.droids to s.starshipDroids. And for clarity, call it starshipDroid, because that's what it really is. Now count them instead of the nonexistent droids.

With that sorted, we'll refresh the homepage and... another error! It's

Warning: Undefined property: App\Entity\Starship::$droids.

This is coming from ship.droidNames in the homepage template. We know that when we call ship.droidNames, it calls $starship->getDroidNames() and we're still referencing the droids property.

Hide that Join Entity

Next, we're going to hide the join entity and make this work exactly like the ManyToMany relationship we had before. Magic!