Login to bookmark this video
07.

The Two Sides of a Relation: Owning vs Inverse

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

Fun fact for your next party: Every relationship can be viewed from two different sides. Take Starship as an example: it has multiple parts, making it a one-to-many relationship from the Starship perspective. But, flip the telescope around and look from the StarshipPart end, and you'll find a many-to-one relationship. One of these perspectives is always known as the owning side, and the other, the inverse side.

Now, you might be thinking, "Why do I care about this?" Well, stick with me for a three-minute dive: it might just save you from a big headache down the road. Plus, you'll have a new bit of trivia to impress your family with at your next family holiday. You can thank me later.

The Owning Side Unveiled

First off, which side is the owning side? For a many-to-one: it's always the side that has the ManyToOne attribute, which is on the entity that will have the foreign key column. In our case, that's StarshipPart.

The Importance of Ownership

But why does this matter? Two reasons. First, the JoinColumn can only live on the owning side. That makes sense: it controls the foreign key column. Second, you can only set the owning side of the relationship. Let me show you:

Pop open src/DataFixtures/AppFixtures.php and let's play a bit: $starship = StarshipFactory::createOne();. My AI overlord was almost right. Below this, I'll sprinkle in code that creates two StarshipPart objects, persist & flush them. I haven't set any relations yet, but let's recklessly load the fixtures anyway:

symfony console doctrine:fixtures:load

And voilĂ , our favorite error pops up. starship_id cannot be null. Totally expected.

The Owning vs Inverse Side in Action

To demonstrate the owning vs inverse issue, add _real() to the end of $starship. When you create an entity via foundry, it actually wraps that in a little gift called a proxy object. This is usually inconsequential, but occasionally, it can cause some confusion. By calling _real(), we can unwrap the proxy and get the real Starship object.

Ok: time to connect these parts to this starship. Normally, we'd say $part1->setStarship($starship);, which sets the owning side. This time try setting the inverse side. That would be $starship->addPart($part1); and $starship->addPart($part2);.

Now, based on what I just explained, this should not work because we are only setting the inverse side. But let's roll the dice and load the fixtures anyway:

symfony console doctrine:fixtures:load

But surprise, surprise! No errors. In fact, if you check the database:

symfony console doctrine:query:sql "SELECT * FROM starship_part"

Sure enough, there are two new parts each related to a starship.

So, what gives? We just set the inverse side of the relationship, and it still saved to the database. That is the opposite of what I just told you!

The Plot Twist: Inverse Side Setting the Owning Side

Open the Starship entity and find the addPart() method. Aha! This method calls $part->setStarship($this);. It sets the owning side. When we set the inverse side, our own code generated by the make:entity command also sets the owning side. Clever girl, eh?

## Owning vs Inverse vs I don't Care

So here's the takeaway: every relation has an owning side and an inverse side. The inverse side is optional. make:entity asked if we wanted to generate the inverse side, and we said yes. That gave us the super convenient $ship->getParts() method.

So yes, technically, you can only set the relationship from the owning side (i.e., $starshipPart->setShip();), but in practice, you can set it from either side thanks to our own code that synchronizes both sides. So go astound your friends with your newfound knowledge, then promptly forget about it: it's not critical in practice.

Clean up our temporary code here and freshen things up by reloading the fixtures:

symfony console doctrine:fixtures:load

Alright, next up: orphanRemoval. It's not nearly as mean as it sounds.