Auto Slug and Timestamps with Doctrine Extensions
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeWe 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!