WEBVTT

NOTE Created by CaptionSync from Automatic Sync Technologies www.automaticsync.com

00:00:01.086 --> 00:00:02.676 align:middle
We need to upgrade the doctrine-bundle...

00:00:02.986 --> 00:00:08.676 align:middle
but before we do, I want to give a refresher on
a really cool Doctrine feature: lazy objects.

00:00:09.326 --> 00:00:12.496 align:middle
Open up src/Entity/StarshipPart.php.

00:00:13.186 --> 00:00:18.566 align:middle
This entity has a Starship property with a
many-to-one relationship to our Starship entity.

00:00:19.206 --> 00:00:24.616 align:middle
Each StarshipPart has one Starship, and
each Starship can have many StarshipParts.

00:00:25.436 --> 00:00:29.436 align:middle
When fetching entities with
relationships, Doctrine uses some cool magic

00:00:29.436 --> 00:00:31.816 align:middle
to avoid unnecessary database queries.

00:00:32.226 --> 00:00:33.586 align:middle
Let's see how it works in action.

00:00:34.236 --> 00:00:36.436 align:middle
Over in your terminal, create a new controller:

00:00:36.776 --> 00:00:43.266 align:middle
symfony console make:controller Name it
LazyController, and no need for tests.

00:00:44.296 --> 00:00:48.536 align:middle
Open the new controller in
src/Controller/LazyController.php.

00:00:49.426 --> 00:00:53.196 align:middle
Ok, we have this index() method
whose route is set to /lazy.

00:00:53.746 --> 00:00:57.526 align:middle
Inject StarshipPartRepository
$repository into it.

00:00:58.216 --> 00:01:04.566 align:middle
Then, grab the first part from the
repository with $part = $repository-&gt;find(1).

00:01:05.386 --> 00:01:12.366 align:middle
Dump it with dump($part): Now, head back to
our app and manually navigate to the /lazy url.

00:01:13.336 --> 00:01:16.526 align:middle
Looking at the web debug
toolbar, we see a single query.

00:01:17.026 --> 00:01:19.006 align:middle
That's the one fetching the StarshipPart.

00:01:19.586 --> 00:01:24.056 align:middle
If we open the dump profiler panel, we
see the fetched StarshipPart object.

00:01:24.686 --> 00:01:29.436 align:middle
Note the starship property, it's
type is this funky Proxy CG thing.

00:01:30.066 --> 00:01:31.936 align:middle
This is a Doctrine lazy object.

00:01:32.476 --> 00:01:33.166 align:middle
Look inside.

00:01:33.396 --> 00:01:36.436 align:middle
All the properties are unset except for the ID.

00:01:36.936 --> 00:01:39.786 align:middle
The ID is the only thing
Doctrine knows about the Starship

00:01:39.976 --> 00:01:43.006 align:middle
until it queries the database
for the rest of the data.

00:01:43.616 --> 00:01:47.996 align:middle
Only when accessing a property of the
Starship does Doctrine trigger a second query

00:01:48.166 --> 00:01:49.096 align:middle
to fetch the rest.

00:01:49.756 --> 00:01:51.236 align:middle
Let's trigger this second query!

00:01:51.716 --> 00:01:57.696 align:middle
In LazyController::index(), add
$part-&gt;getStarship()-&gt;getName()

00:01:57.916 --> 00:02:02.256 align:middle
as the first argument to the
dump(): Refresh the /lazy page...

00:02:02.786 --> 00:02:04.356 align:middle
There are now two queries.

00:02:04.546 --> 00:02:06.506 align:middle
The first one fetches the StarshipPart,

00:02:06.856 --> 00:02:10.496 align:middle
and the second one fetches the
Starship because we accessed its name.

00:02:11.176 --> 00:02:16.036 align:middle
In the dump panel, we can see the Starship
is now fully loaded with all its properties.

00:02:17.076 --> 00:02:21.206 align:middle
So this CG Proxy thing is a real
class Doctrine generates on the fly.

00:02:21.686 --> 00:02:24.566 align:middle
It contains all the logic to
fetch the data when needed.

00:02:24.986 --> 00:02:28.516 align:middle
The key is, it extends the real Starship entity.

00:02:29.226 --> 00:02:34.436 align:middle
That's how it can be used as a stand-in for
the Starship until the actual data is needed.

00:02:34.876 --> 00:02:40.836 align:middle
That's also why entities cannot be final, they
need to be extendable by these proxy classes.

00:02:41.466 --> 00:02:46.196 align:middle
As you might imagine, the logic to do all this
is pretty complex and difficult to maintain.

00:02:46.616 --> 00:02:53.566 align:middle
But... that all changed in PHP 8.4, which
introduced native lazy objects to PHP itself.

00:02:53.956 --> 00:02:57.606 align:middle
And Doctrine Bundle 3, allows our
Symfony apps to take advantage!

00:02:57.926 --> 00:02:58.736 align:middle
So let's upgrade!

00:02:59.396 --> 00:03:01.196 align:middle
First, open our composer.json.

00:03:01.726 --> 00:03:03.836 align:middle
Look for where we're requiring
the doctrine-bundle.

00:03:04.306 --> 00:03:06.786 align:middle
Change its version to ^3.0.

00:03:07.626 --> 00:03:14.286 align:middle
Now, in the terminal, run: symfony
composer update Oooo, a composer error.

00:03:14.286 --> 00:03:16.476 align:middle
Our dependencies couldn't be resolved...

00:03:16.886 --> 00:03:20.026 align:middle
composer.json requires doctrine-bundle 3.

00:03:20.286 --> 00:03:25.876 align:middle
Yeah... doctrine-bundle 3 requires
doctrine/dbal 4 but this conflicts

00:03:25.876 --> 00:03:28.396 align:middle
with our requirement for doctrine/dbal 3.

00:03:29.116 --> 00:03:33.796 align:middle
For reference, doctrine/dbal is the
database abstraction layer that Doctrine uses

00:03:33.796 --> 00:03:35.636 align:middle
to communicate with different databases.

00:03:36.506 --> 00:03:37.996 align:middle
Let's check our composer.json.

00:03:38.476 --> 00:03:40.826 align:middle
We're requiring just dbal version 3,

00:03:41.106 --> 00:03:44.466 align:middle
but the doctrine-bundle needs
version 4 according to that error.

00:03:45.426 --> 00:03:47.566 align:middle
Ok, this is a bit of a legacy issue.

00:03:48.106 --> 00:03:51.536 align:middle
Previous versions of doctrine-bundle
didn't support dbal 4,

00:03:51.846 --> 00:03:54.246 align:middle
so we had to ensure version 3 was used.

00:03:54.596 --> 00:03:57.766 align:middle
This is no longer required, so
we can just remove it entirely,

00:03:57.876 --> 00:04:00.446 align:middle
and let doctrine-bundle decide
which version to use.

00:04:00.886 --> 00:04:01.876 align:middle
Try the update again...

00:04:04.886 --> 00:04:06.796 align:middle
Another error, but a different one.

00:04:07.396 --> 00:04:08.126 align:middle
Scroll up a bit...

00:04:08.886 --> 00:04:12.936 align:middle
we can see doctrine-bundle 3 and
dbal 4 were successfully installed.

00:04:13.746 --> 00:04:16.336 align:middle
The error was caused when
attempting to clear the cache.

00:04:16.636 --> 00:04:20.956 align:middle
Looks like our doctrine configuration is using
some options that are no longer supported.

00:04:21.766 --> 00:04:26.606 align:middle
We could fix these manually, but I'm pretty sure
upgrading the Flex recipe will resolve this.

00:04:27.556 --> 00:04:32.016 align:middle
We need a clean git state before we upgrade
the recipe, so let's check our git status.

00:04:32.916 --> 00:04:35.836 align:middle
We have some modified changes
and some untracked files.

00:04:36.296 --> 00:04:37.306 align:middle
Run: git add .

00:04:37.386 --> 00:04:40.466 align:middle
To track everything and run git status again.

00:04:41.196 --> 00:04:43.556 align:middle
Now that everything is tracked,
we can commit it with:

00:04:44.046 --> 00:04:51.436 align:middle
git commit -a -m "update doctrine-bundle"
git status again to confirm we're clean.

00:04:52.266 --> 00:04:58.596 align:middle
Finally, upgrade the recipe with: symfony
composer recipe:update Sure enough,

00:04:58.686 --> 00:05:00.366 align:middle
it found an update for doctrine-bundle.

00:05:00.706 --> 00:05:04.586 align:middle
Apply it! Run a git status to see the changes.

00:05:05.086 --> 00:05:07.636 align:middle
Perfect, it updated the doctrine.yaml file.

00:05:08.416 --> 00:05:12.696 align:middle
Back in our code, open up
config/packages/doctrine.yaml.

00:05:13.536 --> 00:05:16.086 align:middle
First of all, it removed 3 config options.

00:05:16.266 --> 00:05:18.996 align:middle
These were the ones that caused
that error when clearing the cache.

00:05:19.646 --> 00:05:22.566 align:middle
Next, it looks like it changed
the default naming strategy...

00:05:22.846 --> 00:05:25.036 align:middle
and removed some controller resolver config.

00:05:26.286 --> 00:05:30.696 align:middle
Down under the production config, it
removed the auto_generate_proxy_classes

00:05:30.806 --> 00:05:32.486 align:middle
and proxy_dir options.

00:05:33.056 --> 00:05:37.796 align:middle
With the original lazy object system,
in production, Doctrine generated files

00:05:37.796 --> 00:05:40.176 align:middle
for the proxy classes to improve performance.

00:05:40.736 --> 00:05:43.546 align:middle
None of that is needed anymore
with native lazy objects!

00:05:44.356 --> 00:05:47.166 align:middle
Ok, let's see what our lazy
objects look like now.

00:05:47.716 --> 00:05:52.436 align:middle
First, head back to LazyController:index()
and remove the first argument from the dump():

00:05:53.046 --> 00:05:56.866 align:middle
This should be dumping the part with a
starship instance that isn't fully loaded.

00:05:57.476 --> 00:05:59.266 align:middle
Refresh the /lazy page in your browser.

00:06:00.016 --> 00:06:02.196 align:middle
Ok, just one query, which is expected.

00:06:02.996 --> 00:06:05.866 align:middle
Open the debug panel and
check the starship property.

00:06:06.286 --> 00:06:10.916 align:middle
This is now our normal Starship entity
- not that generated proxy class.

00:06:11.556 --> 00:06:12.396 align:middle
But look inside!

00:06:12.626 --> 00:06:15.496 align:middle
All the properties are unset except for the ID.

00:06:16.026 --> 00:06:18.786 align:middle
This looks just like we saw
in the old proxy class.

00:06:19.166 --> 00:06:21.386 align:middle
This is native lazy objects in action!

00:06:22.096 --> 00:06:29.166 align:middle
Back in LazyController::index(), re-add the
$part-&gt;getStarship()-&gt;getName() to the dump()...

00:06:29.346 --> 00:06:30.936 align:middle
and refresh the /lazy page again.

00:06:31.726 --> 00:06:35.296 align:middle
Two queries, and if we look at the
starship property in the dump panel.

00:06:35.596 --> 00:06:37.596 align:middle
Still just our normal Starship entity...

00:06:37.966 --> 00:06:38.976 align:middle
and if we expand it...

00:06:39.376 --> 00:06:40.806 align:middle
all its properties are loaded!

00:06:41.496 --> 00:06:45.236 align:middle
Ok... this is cool, but what does it
really mean for me as a developer?

00:06:45.716 --> 00:06:49.396 align:middle
Well, there probably is some performance
improvement with native lazy objects.

00:06:49.636 --> 00:06:54.596 align:middle
But the primary takeaway is we can now,
finally, mark our entities as final.

00:06:55.006 --> 00:06:59.536 align:middle
So, yeah, not super life-changing, but it's
nice we don't need a hacky workaround anymore.

00:07:00.126 --> 00:07:02.196 align:middle
So... let's mark our entities as final!

00:07:02.746 --> 00:07:11.496 align:middle
src/Entity/Droid.php: final: Starship.php:
final: StarshipDroid.php: final:

00:07:12.076 --> 00:07:17.626 align:middle
and finally StarshipPart.php:
final: Refresh our lazy page...

00:07:17.686 --> 00:07:19.646 align:middle
and... it all still works!

00:07:20.426 --> 00:07:25.656 align:middle
We're almost ready to make the push to Symfony
8, but before we do, let's revisit deprecations.

