This course is still being released! Check back later for more chapters.
Part Entity
We already have starships showing up on the homepage thanks to the Starship
entity we built in the last tutorial. But now, it's time to step up our game. We need to track the individual parts used in each Starship
. Here's the plan: each part will belong to exactly one Starship
, and each Starship
will have many parts. But before we dive into relationships, we need to start simple: we need a new entity to keep track of these parts! Fire up your terminal, open a new tab (since our server is humming along in the other), and run:
symfony console make:entity
Call it StarshipPart
, skip broadcasting, and give it a few fields: name
will be a string and won't be nullable, price
will be an integer (in credits, of course), and also won't be nullable. Lastly, add a notes
field which will be a text
type (so it can be longer), and will be nullable. Once you've added these fields, create a new migration for the entity by copying and pasting symfony console make:migration
.
Running Migrations and Adding Timestamps
Now, if you check out your migrations, you'll see just the new one we created: I cleaned up the old migrations from the last course.
// ... lines 1 - 12 | |
final class Version20250306155858 extends AbstractMigration | |
{ | |
// ... lines 15 - 19 | |
public function up(Schema $schema): void | |
{ | |
// this up() migration is auto-generated, please modify it to your needs | |
$this->addSql('CREATE TABLE starship_part (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, price INT NOT NULL, notes TEXT DEFAULT NULL, PRIMARY KEY(id))'); | |
} | |
// ... lines 25 - 30 | |
} |
So this one is all about StarshipPart
. Run it with:
symfony console doctrine:migrations:migrate
The table is in the database! But there are two fields that I like to add to all my entities: createdAt
and updatedAt
. You can see these inside of Starship
, under TimestampableEntity
.
// ... lines 1 - 11 | |
class Starship | |
{ | |
use TimestampableEntity; | |
// ... lines 15 - 137 | |
} |
Copy that, and paste right on top of StarshipPart
. Both properties are automatically set thanks to a library we installed in the last tutorial. And because we added two new fields, we need a migration!
symfony console make:migration
Then migrate:
symfony console doctrine:migrations:migrate
Using Factories to Create Dummy Objects
In the last tutorial, we used a cool library called Foundry
to quickly create a bunch of dummy data. We're going to do the same for StarshipPart
. Step 1 - since we don't have one yet - is to generate a factory for the entity with:
symfony console make:factory
Go check it out in src/Factory/StarshipPartFactory.php
. It added some defaults for each field, but we can make it more interesting. At the top of StarshipPartFactory
, I'll paste in some code with example parts (you can grab this from the code block on this page). Also replace the return in defaults()
with code that uses that data.
// ... lines 1 - 10 | |
final class StarshipPartFactory extends PersistentProxyObjectFactory | |
{ | |
private static array $partIdeas = [ | |
'warp core' => 'looks cool AND zoom', | |
'shield generator' => 'in case you run into any Borg', | |
'captain\'s chair' => 'just slightly more comfortable than the others', | |
'fuzzy dice' => 'obviously', | |
'photon torpedoes' => 'for when the fuzzy dice don\'t work', | |
'holodeck' => 'parental controls? No way!', | |
'Tactical Whoopee Cushion Array' => 'can\'t beat them? Embarrass them!', | |
'Temporal Seat Warmers' => 'warm your seat before you sit down', | |
'Food Replicator' => 'Earl Grey, hot', | |
'Self-Destruct Button Cover' => 'for when you have a cat', | |
'Redshirt Dispenser' => 'Instantly replenishes expendable crew members.', | |
]; | |
// ... lines 26 - 44 | |
protected function defaults(): array|callable | |
{ | |
$randomPartKey = self::faker()->randomKey(self::$partIdeas); | |
$randomPart = [$randomPartKey, self::$partIdeas[$randomPartKey]]; | |
return [ | |
'name' => $randomPart[0], | |
'notes' => $randomPart[1], | |
'price' => self::faker()->randomNumber(5), | |
]; | |
} | |
// ... lines 56 - 65 | |
} |
Finally, use this in the fixtures. At the bottom, create 50 random parts using StarshipPartFactory::createMany(50)
.
// ... lines 1 - 10 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager): void | |
{ | |
// ... lines 15 - 38 | |
StarshipFactory::createMany(20); | |
StarshipPartFactory::createMany(50); | |
} | |
} |
Back in the terminal, run:
symfony console doctrine:fixtures:load
Confirm that we want to vent the oxygen from our database, then check out the new parts with:
symfony console doctrine:query:sql
And then: select * from starship_part
And with just a few lines of delightful code, we have 50 random parts in the database. Next: let's start linking these parts to their respective ships by creating our first relationship: the all-important ManyToOne
relationship.