Ship Upgrades: Updating an Entity
…on our homepage only lists incomplete starships, so we need to find a
completed one. In your terminal, run:
symfony console doctrine:query:sql 'SELECT slug, status FROM starship'
lunar-marauder-1 is a completed ship. Copy the slug, and back in the app,
visit…
Many To One: The King of Relationships
…null.
But that's not allowed, thanks to the nullable: false. Clear those 50 rows with:
symfony console doctrine:query:sql "DELETE FROM starship_part"
And run the migration again:
So, how do we go about linking a StarshipPart object with its Starship?
Buckle up…
Setting Relations in Foundry
…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!
starship_id cannot be null in starship_part.
This traces all the way back to StarshipPartFactory, down in
the defaults…
Many To Many with Foundry
…not
technically required - good luck cleaning the bathroom without them! -
I like to keep them out of defaults() and set them where we're using
StarshipFactory.
Next, we'll learn how to JOIN across ManyToMany relationships. Once again,
Doctrine handles the heavy lifting for us.
EXTRA_LAZY Relationships
…to count them... isn't the greatest thing for efficiency.
If you find yourself in this situation, you can tell Doctrine to be clever with
how it loads the relation. Go into the Category entity and find the OneToMany
relationship for $fortuneCookies. At the end…
JOINs
…a database perspective, in order to update our
WHERE clause to include WHERE fortune_cookie.fortune = :searchTerm, we first
need to JOIN to the fortune_cookie table.
And that is what we're going to do in Doctrine... except with a twist. Instead
of…
Bundle Configuration
…reference
With no arguments. This lists all the loaded bundle's, and their configuration
key (or extension alias). DoctrineBundle, alias doctrine, DoctrineMigrationsBundle,
alias doctrine_migrations. Here's our bundle: ObjectTranslationBundle, alias
object_translation.
Hmm, I'd like to prefix it with symfonycasts, like the Tailwind…
JOINs and addSelect Reduce Queries
…fortuneCookies|length.
In PHP land, we're calling the getFortuneCookies() method on Category. But until
now, Doctrine has not yet queried for the FortuneCookie data for this Category.
However, as soon as we access the $this->fortuneCookies property, it magically
makes that query, basically saying:
Give…
Pagination & Column Sorting
…go fix it!
To use Pagerfanta, we'll install three libraries:
composer require babdev/pagerfanta-bundle pagerfanta/doctrine-orm-adapter pagerfanta/twig
Cool beans! Let's get the PHP side working first. Open src/Controller/MainController.php.
The current page will be stored on the…
SELECTing into a New DTO Object
… I like to work with
objects whenever possible. Fortunately, Doctrine gives us a simple way to improve
this situation: we query for the data we want... but tell it to give us an object.
First, we need to create a new class that will hold…
Raw SQL Queries
…s as boring as SQL queries can
get. I used fortune_cookie for the table name because I know that, by default,
Doctrine underscores my entities to make table names.
Now that we have the query string, we need to create a Statement with
$stmt…
Translated Object Wrapper
…ObjectTranslator, we have
this todo waiting for us.
There's a couple ways we can approach this. One way is to directly modify
the properties on the object. But since this is a Doctrine entity, it'll
be marked as modified. If you unintentionally flush…
Reusing Queries in the Query Builder
…
Anywhere inside here, add a new private function called
addFortuneCookieJoinAndSelect(). This will accept a QueryBuilder object (make
sure you get the one from Doctrine\ORM - the "Object Relational Mapper"), and let's
call it $qb. This will also return a QueryBuilder.
The next step is…
Translatable Mapping
…and slick approach is to use PHP
attributes.
I'm all for slick and modern, so let's go with attributes.
These will be similar to these Doctrine ORM mapping attributes. Ours will
map translatable entities and properties.
In our object-translation-bundle/src directory…
The Failure Transport
…an expanded
format where you put the dsn on its own line and then add an options key with
whatever you need below that.
What options can you put here? Each transport type - like doctrine or amqp -
has its own set of options. Right now…
Performance Optimization 2: Caching
…ObjectTranslator service. Down here in the
translationsFor() method, this findBy() is what's making the queries.
This is what we want to cache!
Doctrine does offer some result caching, but it can get complex isn't
as flexible as the Symfony Cache component. So, let…
Testing Messenger
…you guessed
it, Zenstruck!
composer require zenstruck/messenger-test --dev
Cool! This messenger-test library adds a special Messenger transport called test.
We'll still use Doctrine by default, but now open up
config/packages/messenger.yaml. Uncomment the async transport, which uses
MESSENGER_TRANSPORT…
Factory Data Seeding
…and look at src/Factory/LockDownFactory.php. I'm not going to talk
too much about these factory classes: we already cover them in our Doctrine tutorial.
But this class will make it easy to create LockDown objects, even setting
createdAt to a random DateTime…
Bundle Dependencies
…because we need the services the translation component
provides, like the LocaleAwareInterface. Following that, run:
symfony composer require doctrine/doctrine-bundle doctrine/orm
Cool, these are the bare minimum dependencies our bundle requires to function.
Now, it's time to add some development dependencies. Run:
…
Partial Handler Failures & Advanced Routing
…there's a problem removing this ImagePost from
the database, Doctrine will throw an exception right here and the file will never
be deleted. That's perfect: the row in the database and file on the filesystem
will both remain.
But if deleting the row…
x
1000+