DoctrineBundle Updates & Recipe Upgrade

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.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

We just upgraded from DoctrineBundle version 1 to version 2 and... it broke our app. That's lame! Hmm:

Cannot autowire service ApiTokenRepository: argument $registry references interface RegistryInterface but no such service exists.

Checking the CHANGELOG

Hmm. Ya know, instead of trying to figure this out... and digging for any other breaking changes... let's just go look at the bundle's CHANGELOG. Go back to the DoctrineBundle GitHub homepage. And... ah! Even better: upgrade files! Open UPGRADE-2.0.md.

There's a lot here: dropping old versions of PHP & Symfony and changes to commands. But if you look closely, you'll find that most of these are pretty minor. The most important changes are under "Services aliases". Previously, if you wanted to get the doctrine service, you could use the RegistryInterface type-hint for autowiring. Now you should use ManagerRegistry.

From RegistryInterface to ManagerRegistry in Repository Classes

Where do we use RegistryInterface? Move over to your terminal and run:

git grep RegistryInterface

We use it in every single repository class. This is code that the make:entity command generated for us. The newest version of that bundle uses ManagerRegistry.

Fixing this is as simple as changing a type-hint... it's just tedious. Open up every repository class. And, one-by-one, I'll change RegistryInterface to ManageRegistry in the constructor:

... lines 1 - 14
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
... line 19
}
... lines 21 - 85
}

Use ManagerRegistry from Doctrine\Persistence. There is also one from Doctrine\Common\Persistence:

... lines 1 - 6
use Doctrine\Persistence\ManagerRegistry;
... lines 8 - 14
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
... line 19
}
... lines 21 - 85
}

doctrine/common Split into doctrine/persistence (and other Packages)

That's another Doctrine change that's happening right now. Doctrine originally had a package called doctrine/common, which contained a lot of... well... "common" classes that other Doctrine libraries needed. Doctrine is now splitting doctrine/common into smaller, individual packages. Basically, the Persistence directory of doctrine/common is now its own package and you should use its classes: the old ones are deprecated.

What makes this a bit more confusing is that the UPGRADE log references the old class name. Like I said: there are a lot of moving pieces right now.

I'll also remove the old RegistryInterface use statement. Repeat this a bunch more times: change to ManagerRegistry, remove the use statement and keep going:

... lines 1 - 6
use Doctrine\Persistence\ManagerRegistry;
... lines 8 - 14
class TagRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
... line 19
}
... lines 21 - 49
}

... lines 1 - 8
use Doctrine\Persistence\ManagerRegistry;
... lines 10 - 17
class CommentRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
... line 22
}
... lines 24 - 80
}

Do you want to see how fast I can type?

... lines 1 - 8
use Doctrine\Persistence\ManagerRegistry;
... lines 10 - 16
class ArticleRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
... line 21
}
... lines 23 - 73
}

... lines 1 - 6
use Doctrine\Persistence\ManagerRegistry;
... lines 8 - 14
class ApiTokenRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
... line 19
}
... lines 21 - 49
}

Suuuuuuper faaaaaaaast. Ah! I sprained a finger.

Let's see if we're good! Spin over and just run:

php bin/console

Before those changes, running this would have caused an explosion - the same one that we saw after running composer update. So we are good: we're using a Symfony5-compatible version of DoctrineBundle.

Upgrading the DoctrineBundle Recipe

But... because this library is so important... and because we just did a major version upgrade, I also want to upgrade its recipe. Run:

composer recipes

Ok, yea, DoctrineBundle is one of the few recipes that still have an update available. Run:

composer recipes doctrine/doctrine-bundle

to get more info and copy the update command. Run it!

composer recipes:install doctrine/doctrine-bundle --force -v

Ok, it looks like this updated several files. Let's step through the changes. Clear the screen and run:

git add -p

.env Changes and serverVersion

The first changes are inside .env: it added a PostgreSQL example and, oh, this comment is important: it mentions that the serverVersion setting is required in this file or in config/packages/doctrine.yaml. That's actually not a new thing, but the new recipe now gives you a bit more info about this.

The setting tells Doctrine what version of your database you're using, like MySQL 5.7 or mariadb-10.2.12. Doctrine uses that to know which features are supported by your database.

The point is: this is something Doctrine needs to know and you can add that config inside your DATABASE_URL environment variable or in doctrine.yaml, which is what I prefer. I like to set this to my production database version and commit it inside doctrine.yaml so that the project works the same on any machine.

So... I guess I want these new comment changes, except that I want to keep my existing DATABASE_URL. Copy it, hit "y" to accept the changes, then "q" to quit the patch mode.

Back in our editor... find .env, look for DATABASE_URL, and paste our original value:

62 lines .env
... lines 1 - 22
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
DATABASE_URL=mysql://root:@127.0.0.1:3306/the_spacebar
###
... lines 30 - 62

Let's keep going!

git add -p

Accept the change we just made to .env. The next update is in composer.json, we definitely want this. Then... actually, hit "q". Let's add the files we know we want:

git add composer.json composer.lock symfony.lock src/Repository

Run:

git status

Much better! Back to:

git add -p

Updates to doctrine.yaml

In bundles.php, it removed DoctrineCacheBundle - that's a good change - and now we're inside of doctrine.yaml.

There are a bunch of interesting updates here. First, there used to be a parameter called env(DATABASE_URL). This was a workaround to prevent Doctrine from exploding in some edge cases. It's no longer needed. Progress!

Next, the driver setting isn't needed inside here because that part is always contained inside DATABASE_URL. It's just extra, so we can remove it. Oh, and server_version was just moved further down.

The recipe also removed these charset options, and that is interesting. If you use MySQL, these settings are needed to make sure that you can store unicode characters. Starting in DoctrineBundle 2.0, these values are no longer needed because they are the default. That's a nice cleanup.

Below, the server_version is now commented-out by default: you need to choose to put it in this file or inside your environment variable. I'll uncomment this in a minute.

Finally, this naming_strategy is a minor change: it controls how table and column names are generated from class and property names. The new setting handles situations when there is a number in the name. It's a good change... and the old setting is deprecated. However it is possible that this could cause Doctrine to try to rename some columns. You can run:

php bin/console doctrine:schema:update --dump-sql

after making this change to be sure. Enter "y" to accept all these changes, then "q" to quit. Find this file - config/packages/doctrine.yaml - uncomment server_version and adjust it to whatever you need for your app:

doctrine:
dbal:
... lines 3 - 6
server_version: '5.7'
... lines 8 - 19

Production doctrine.yaml Cache Changes

Back to work!

git add -p

Enter "y" for our server_version change. The last big update is in config/packages/prod/doctrine.yaml. This file configures cache settings in the prod environment: this is stuff you do want. When we originally installed the bundle, the old recipe created several cache services down here under the services key... and then used them above for the different cache drivers.

Basically, in DoctrineBundle 2.0, these services are created for you. This means that the config can be drastically simplified. Say "y" to this change.

And... we're done! Phew! Commit this:

git commit -m "upgrading to DoctrineBundle 2.0"

And celebrate!

The doctrine/persistence 1.3 Deprecations

Let's go see how the deprecations look now. When I refresh the homepage... down to 11 deprecations! Check them out.

Huh: a lot of them are still from doctrine... they all mention a deprecation in doctrine/persistence 1.3. doctrine/persistence is one of the libraries that was extracted from doctrine/common.

Ok, but why are we getting all these deprecations? Where are they coming from?

I have 2 things to say about this. First, because this is a deprecation warning about a change in doctrine/persistence 2.0... and because we're focusing right now on upgrading to Symfony 5.0, this is not a deprecation we need to fix. We can save it for later.

Second, if you Google'd this deprecation, you'd find that it is not coming from our code: it's coming from Doctrine itself, specifically doctrine/orm.

There's currently a pull request open on doctrine/orm - number 7953 - that fixes these. Basically, doctrine/orm is using some deprecated code from doctrine/persistence, but the fix hasn't been merged yet. The fix is targeted for version 2.8 of doctrine/orm. So hopefully when that's released in the future, you'll be able to update to it to remove this deprecation. But, as I said... it's not a problem right now: we can keep working through the Symfony-related deprecations and ignore these.

And... that list is getting pretty short! Time to finish them.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.3.0",
        "ext-iconv": "*",
        "antishov/doctrine-extensions-bundle": "^1.4", // v1.4.2
        "aws/aws-sdk-php": "^3.87", // 3.110.11
        "doctrine/doctrine-bundle": "^2.0", // 2.0.6
        "doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // 2.1.2
        "doctrine/orm": "^2.5.11", // v2.7.2
        "easycorp/easy-log-handler": "^1.0", // v1.0.9
        "http-interop/http-factory-guzzle": "^1.0", // 1.0.0
        "knplabs/knp-markdown-bundle": "^1.7", // 1.8.1
        "knplabs/knp-paginator-bundle": "^5.0", // v5.0.0
        "knplabs/knp-snappy-bundle": "^1.6", // v1.7.0
        "knplabs/knp-time-bundle": "^1.8", // v1.11.0
        "league/flysystem-aws-s3-v3": "^1.0", // 1.0.23
        "league/flysystem-cached-adapter": "^1.0", // 1.0.9
        "league/html-to-markdown": "^4.8", // 4.8.2
        "liip/imagine-bundle": "^2.1", // 2.3.0
        "nexylan/slack-bundle": "^2.1", // v2.2.1
        "oneup/flysystem-bundle": "^3.0", // 3.3.0
        "php-http/guzzle6-adapter": "^2.0", // v2.0.1
        "sensio/framework-extra-bundle": "^5.1", // v5.5.3
        "symfony/asset": "5.0.*", // v5.0.2
        "symfony/console": "5.0.*", // v5.0.2
        "symfony/dotenv": "5.0.*", // v5.0.2
        "symfony/flex": "^1.0", // v1.6.2
        "symfony/form": "5.0.*", // v5.0.2
        "symfony/framework-bundle": "5.0.*", // v5.0.2
        "symfony/mailer": "5.0.*", // v5.0.2
        "symfony/messenger": "5.0.*", // v5.0.2
        "symfony/monolog-bundle": "^3.5", // v3.5.0
        "symfony/security-bundle": "5.0.*", // v5.0.2
        "symfony/sendgrid-mailer": "5.0.*", // v5.0.2
        "symfony/serializer-pack": "^1.0", // v1.0.2
        "symfony/twig-bundle": "5.0.*", // v5.0.2
        "symfony/twig-pack": "^1.0", // v1.0.0
        "symfony/validator": "5.0.*", // v5.0.2
        "symfony/webpack-encore-bundle": "^1.4", // v1.7.2
        "symfony/yaml": "5.0.*", // v5.0.2
        "twig/cssinliner-extra": "^2.12", // v2.12.0
        "twig/extensions": "^1.5", // v1.5.4
        "twig/inky-extra": "^2.12" // v2.12.0
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.3.0
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/browser-kit": "5.0.*", // v5.0.2
        "symfony/debug-bundle": "5.0.*", // v5.0.2
        "symfony/maker-bundle": "^1.0", // v1.14.3
        "symfony/phpunit-bridge": "5.0.*", // v5.0.2
        "symfony/profiler-pack": "^1.0", // v1.0.4
        "symfony/var-dumper": "5.0.*" // v5.0.2
    }
}