Auto Slug and Timestamps with Doctrine Extensions
Lucky you! You found an early release chapter - it will be fully polished and published shortly!
This Chapter isn't quite ready...
Rest assured, the gnomes are hard at work
completing this video!
We added three new fields to Starship
: slug
,
updatedAt
, and createdAt
. But now our fixtures don't load! The reason is simple:
all 3 fields are required in the database, but StarshipFactory
doesn't set them.
We could add these, but we shouldn't have to. In a perfect world slug
would be
automatically generated from the name
, updatedAt
:
set to the current time when the entity changes and createdAt
: set to the current
time when the entity is created.
DoctrineExtensions
And there's a package that can do this DoctrineExtensions
! In your terminal,
run:
composer require stof/doctrine-extensions-bundle
This bundle has a recipe... but it isn't considered official. It's a third-party, or contrib recipe. These are typically fine, just know that contrib recipes are added by the community and not policed by Symfony's core team.
Scroll up to see what we have. The most important packages are gedmo/doctrine-extensions
,
which contains the real logic, and stof/doctrine-extensions-bundle
, that
integrates that with Symfony. No need to worry about the other stuff.
Run:
git status
to see what the recipe added. It configured the bundle and added a new config file. Cool! For this bundle, we do need to edit this config to enable the extensions where each extension is like a superpower for your entities.
Enabling Extensions
Open config/packages/stof_doctrine_extensions.yaml
. Below default_local
add a new key: orm:
, then default:
and inside that, enable 2 superpowers, I mean extensions:
timestampable: true
and sluggable: true
:
// ... lines 1 - 2 | |
stof_doctrine_extensions: | |
default_locale: en_US | |
orm: | |
default: | |
timestampable: true | |
sluggable: true |
These are now activated in general, but we need to a bit more config to bring them
to life for the Starship
entity. Open that up again.
Using Extensions
Above the $slug
property, add a new attribute: #[Slug]
, importing the class from
Gedmo\Mapping\Annotation
. Inside, add fields:
set it to an array containing
name
:
// ... lines 1 - 10 | |
class Starship | |
{ | |
// ... lines 13 - 33 | |
fields: ['name']) | (|
private ?string $slug = null; | |
// ... lines 36 - 158 | |
} |
This tells the extension to generate the slug from the $name
property when the entity
is first persisted.
Above $updatedAt
, add #[Timestampable]
with on: 'update'
so it knows to
set this field to the current time on entity update:
// ... lines 1 - 10 | |
class Starship | |
{ | |
// ... lines 13 - 41 | |
on: 'update') | (|
private ?\DateTimeImmutable $updatedAt = null; | |
// ... lines 44 - 158 | |
} |
Same for $createdAt
, but with on: 'create'
:
// ... lines 1 - 10 | |
class Starship | |
{ | |
// ... lines 13 - 37 | |
on: 'create') | (|
private ?\DateTimeImmutable $createdAt = null; | |
// ... lines 40 - 158 | |
} |
Reloading the Fixtures
Let's try it! At your terminal, run:
symfony console doctrine:fixtures:load
And... it worked! Run our SQL query to see the values:
symfony console doctrine:query:sql 'SELECT name, slug, updated_at, created_at FROM starship'
Yeah! Our slug
is generated from the name
, and updatedAt
and createdAt
are set
to the timestamp of when the entity was created. Doctrine considers the
initial save also as an update: that's why updatedAt
and createdAt
have the same value.
Slugs are Kept Unique
Scroll down a bit. Notice these slugs are suffixed with -1
? What's that about?
This is because our slug
field is unique but our name
is not. We have some starships,
like Lunar Marauder
here, that have the same name. The slug extension is smart enough
to detect this, and automatically add a numeric suffix (-1
, -2
, etc.) to keep them
unique. Smart!
Now that we have a unique, human-readable slug for our starships, let's use it instead
of this ugly id
in our URLs. We'll also use something called Controller
Value Resolvers to make our controllers high-tech! That's next!