This course is still being released! Check back later for more chapters.
Starship Entity
We have a database and can connect to it but... it doesn't have any tables!
The Doctrine ORM uses PHP classes to represent tables in the database, like if you need a table for products, you create a Product
class. Doctrine calls these classes "entities", but they're really just standard boring PHP classes. I like boring!
In our StarShop app, we need to track Starships... so we need a Starship
table... so we need a Starship
entity class. What does a Starship look like? In the last tutorial, we created a Starship
model class in the src/Model
directory. Open it up. We decided that each Starship has an id
, name
, class
, captain
, status
, and arrivedAt
.
This is almost a Doctrine entity: it's just missing some config that helps Doctrine understand how to map this class to a table in the database. We could easily add that by hand. But... we have a tool that can do this for us: the MakerBundle!
make:entity
Run:
symfony console make:entity
For the name, use Starship
. We're not using Symfony UX Turbo, so answer no
to that question. This already created a Starship
class in the Entity/
directory and a StarshipRepository
class. We'll talk about that later.
But we're not done! This command is awesome: it interactively asks what properties - or columns if you want to think that way - our entity needs. Jump back to the Starship model to see what we need. MakerBundle will add id
automatically, so skip to name
. Field type? Use the default: string
. Field length? 255
is fine. Can this field be null in the database? No, every Starship needs a name.
Next is class
, it'll be the same as name
... then captain
is also a simple string
. Next: status
. Doctrine defaults to a string
, but... look at our Starship
model, status
is an enum. How can we map this to a column? Back in the terminal, hit ?
to see all the different types we can add. At the bottom... enum
! Use that. Enum class
? Use the full class name of our enum: App\Model\StarshipStatusEnum
.
Can this field store multiple values? No, a Starship can only have one status at a time. Can this field be null? Nope!
Finally, add arrivedAt
. Cool! Maker defaults to datetime_immutable
instead of string
. This is because we suffixed our property name with At
. Smart! Can this field be null? No.
[ORM\Entity]
Let's take a look at our newly minted Starship
entity: in src/Entity/
.
Notice: this is a standard PHP class with properties... and one special thing: some PHP attributes:
The #[ORM\Entity]
attribute on the class tells Doctrine that this not just a boring PHP class, but an entity that should be mapped to a table in our database. The table name can be customized, but we'll use the default which is the snake cased class name: starship
.
[ORM\Column]
Check out the properties: each has #[ORM\Column]
. This tells Doctrine that these properties are columns in our table. For the type, Doctrine is smart and guesses from the type hint. For example, id
will be an integer type, name
will be a string type, and arrivedAt
will be a timestamp type. Nice!
// ... lines 1 - 8 | |
#[ORM\Entity(repositoryClass: StarshipRepository::class)] | |
class Starship | |
{ | |
#[ORM\Id] | |
#[ORM\GeneratedValue] | |
#[ORM\Column] | |
private ?int $id = null; | |
#[ORM\Column(length: 255)] | |
private ?string $name = null; | |
#[ORM\Column(length: 255)] | |
private ?string $class = null; | |
#[ORM\Column(length: 255)] | |
private ?string $captain = null; | |
#[ORM\Column(enumType: StarshipStatusEnum::class)] | |
private ?StarshipStatusEnum $status = null; | |
#[ORM\Column] | |
private ?\DateTimeImmutable $arrivedAt = null; | |
// ... lines 31 - 95 | |
} |
id
has a few extra attributes that mark it as the primary key and tells the database to auto-generate this as an auto-incrementing integer.
Oh, and we can remove the length
argument from the string columns: this is the default.
// ... lines 1 - 9 | |
class Starship | |
{ | |
// ... lines 12 - 16 | |
#[ORM\Column] | |
private ?string $name = null; | |
#[ORM\Column] | |
private ?string $class = null; | |
#[ORM\Column] | |
private ?string $captain = null; | |
// ... lines 25 - 109 | |
} |
The status
property is a StarshipStatusEnum
type but Doctrine will store this as a string in the database. Cool! We can actually remove the enumType
argument: Doctrine can guess that from the property type too!
// ... lines 1 - 9 | |
class Starship | |
{ | |
// ... lines 12 - 25 | |
#[ORM\Column] | |
private ?StarshipStatusEnum $status = null; | |
// ... lines 28 - 109 | |
} |
Down below, the maker generated getters and setters for all our properties. Our old Starship
model had two extra methods: getStatusString()
and getStatusImageFilename()
. Copy those from the model class... and at the bottom of the entity class, paste!
// ... lines 1 - 9 | |
class Starship | |
{ | |
// ... lines 12 - 96 | |
public function getStatusString(): string | |
{ | |
return $this->status->value; | |
} | |
public function getStatusImageFilename(): string | |
{ | |
return match ($this->status) { | |
StarshipStatusEnum::WAITING => 'images/status-waiting.png', | |
StarshipStatusEnum::IN_PROGRESS => 'images/status-in-progress.png', | |
StarshipStatusEnum::COMPLETED => 'images/status-complete.png', | |
}; | |
} | |
} |
Entity done! We can even double-check our work. At your terminal, run:
Schema Validation
symfony console doctrine:schema:validate
This means that Doctrine sees and can read our attributes. Then... our database is out of sync?
We have an entity class... but we don't actually have the starship
table in the database.
There are a few ways to get the table into the database, but the best way is migrations. That's next!