Login to bookmark this video
05.

Setting Relations in Foundry

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 have a couple of parts and a few starships, but to fill our testing data fleet: I want a lot more. This is a job perfectly suited for our good friend, Foundry. Remove the manual code, then anywhere, say: StarshipPartFactory::createMany(100) and try the fixtures:

symfony console doctrine:fixtures:load

Uh-oh, we hit a snag!

starship_id cannot be null in starship_part.

This traces all the way back to StarshipPartFactory, down in the defaults() method. This is the data passed to each new StarshipPart when it's created. The golden rule is to make defaults() return a key for every required property on the object. Right now, we're obviously missing the starship property, so let's add that. Set starship, not starship_id to a nifty method called Starship::randomOrCreate() and pass an array.

Setting the Stage for Starship Parts

On the homepage, where we're only listing starships with 'in progress' or 'waiting' status. To make sure our parts are related to a ship with 'in progress' status, add a status key to the array set to StarshipStatusEnum::IN_PROGRESS.

This is an impressive method: it first looks in the database to find a Starship that matches this criteria (an "in progress" ship"). If it finds one, it uses that. If it does not, it creates one with that status.

Try the fixtures now.

symfony console doctrine:fixtures:load

No errors!

Check the database:

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

Look closely: we have 100 parts each tied to a random Starship, which should be a Starship with an 'in progress' status. Wow! That's my most productive day ever!

Taking Control in Foundry

But what if we need more control? What if we want to assign all 100 of these parts to the same ship? I know it sounds a bit not useful, but it'll help us understand Foundry and relationships even better.

Start by getting a ship variable,: $ship = Starship::createOne(). Then, in StarshipPartFactory::createMany(), pass a second argument to specify that we want starship to be set to this specific ship.

Load up those fixtures again.

symfony console doctrine:fixtures:load

And voila! All parts are now related to the same one ship. And if we query the Starships, we have 23: the 20 at the bottom, plus the extra 3 we added. Everything's coming together!

The Foundry Plot Twist

Here's where things get interesting. In StarshipPartFactory, instead of using randomOrCreate(), switch to createOne().

Load the fixtures again.

symfony console doctrine:fixtures:load

And... query for all the ships.

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

Whoa, we suddenly have a fleet! 123 ships to be exact. What happened?

For each part, defaults() is called. So for all 100 parts, it's triggering this line, which creates and saves a Starship, even though we never use that Starship because we override it moments later.

The solution? Change this to StarshipFactory::new(). This is the secret sauce: it creates a new instance of the factory, not an object in the database. Try it:

symfony console doctrine:fixtures:load

Query the ships. Perfect! We're back to 23.

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

Factories are Object Recipes

Fun fact! We can use these factory instances like recipes for creating objects. Starship::new(['status' => StarshipStatusEnum::STATUS_IN_PROGRESS]) doesn't actually create an object in the database. Nope: new() means a new instance of the factory, not a new object in the database. And when you pass a factory for a property, Foundry delays creating the object until and if it's needed. Only if the Starship is not overridden will it create a new Starship with status "in progress" and save it. This is the best-practice when setting relationships in Foundry: set them to a factory instance.

Clean up our fixtures by removing the override and... switch back to randomOrCreate() because, let's be honest, it's a pretty useful method.

Reload the fixtures one last time to make sure we didn't break anything

symfony console doctrine:fixtures:load

Nope! We'll try harder next time.