Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

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!

11
Login or Register to join the conversation
MattWelander Avatar
MattWelander Avatar MattWelander | posted 11 months ago | edited

Hi, I ran into some trouble with my entities after moving to symfony 5 (5.4). I while back (already in sym 4 possibly) we were instructed to reference our entities with a "::class" at the end in the getRepository() function. I had nested my Entities to keep them cleaner in the directory structure and therefore opted to keep my way of referencing them (as it still worked back then). My directory structure looks like this

Entity
  Hazardlog
    Analysis
    Risk

I currently do this when I pick up an entity
$analyses = $em->getRepository('App:Hazardlog\Analysis')->findAll();

That no longer works. I've tried all three of these with no luck:

$analyses = $em->getRepository('App:Hazardlog\Analysis::class')->findAll();
$analyses = $em->getRepository('Hazardlog\Analysis::class')->findAll();
$analyses = $em->getRepository('Analysis::class')->findAll();

Is there no longer a way to keep my entities neatly organized into folders? Or is there a trick to referencing them?

Reply
MattWelander Avatar
MattWelander Avatar MattWelander | MattWelander | posted 11 months ago | edited

Hokey, I figured out that I could to this instead:

$analyses = $em->getRepository('App\Entity\Hazardlog\Analysis')->findAll();
Reply

Hey Matt,

Awesome, I'm happy you were able to figure it out yourself, well done!

Btw, you were very close in your 3rd try, i.e. the option with $analyses = $em->getRepository('Analysis::class')->findAll(); is almost correct, but it should be this way:

$analyses = $em->getRepository(\App\Entity\Hazardlog\Analysis::class)->findAll();
// or if you add the namespace as a use statement above your class - just:
$analyses = $em->getRepository(Analysis::class)->findAll();

I.e. It should not be in quotes ;)

Cheers!

Reply
MattWelander Avatar
MattWelander Avatar MattWelander | posted 1 year ago | edited

Hi, apparently my mySql is a MariaDB, for server version phpMyAdmin says <b>10.3.35-MariaDB-log - FreeBSD ports</b> so should my doctrine.yaml then look like this?

doctrine:
    dbal:
        server_version: 10.3.35```


Or do I need anything more (or different) in there? (indentation seems lost in this comment field, indented for yaml obviously)
Reply

Hey Mattias,

Good question! Well, it should look like "server_version: 'mariadb-10.3.35'", i.e. you should add that "mariadb-" prefix for MariaDB SQL servers - that's a standard you should just follow :)

But keep in mind that it's better to write there the version you have on production. And ideally, you should have the same version locally and on production so that it works without problems. Otherwise, you can try to specify the lowest version from both and see if it will work well. Otherwise, you would need to specify that version different for different environment using .env files.

I hope this helps!

Cheers!

Reply
MattWelander Avatar
MattWelander Avatar MattWelander | Victor | posted 1 year ago | edited

Thank you! Yes, that was from the production server. But oh, I just realized that I have MariaDB in production, but for my local server I'm running <b>"Server version: 5.7.32 - MySQL Community Server (GPL)"</b> so they are completely different then I suppose.

So one way would be to put the respective versions in the DB connection strings.

I suppose another way would be to put a doctrine.yaml file in each of the config/packages/prod and config/packages/dev directories, and put this in the config/packages/dev/doctrine.yaml

doctrine:
    dbal:
        server_version: 'mysql-5.7.32'```

is that correct?
Reply

Hey Mattias,

Hm, well, they are not that much different fairly speaking.. but yeah, it's better to separate them. The more specific version you specify there - less problems you will have, and queries that Doctrine generates in migrations will be more optimized for too. But I used to have MariaDB on prod and MySQL locally and it works OK with the MariaDB server_version for me. So, it depends.

> I suppose another way would be to put a doctrine.yaml file in each of the config/packages/prod and config/packages/dev directories, and put this in the config/packages/dev/doctrine.yaml

Well, it might be enough for you, but it's not quite correct IMO, because you may load your project in prod mode locally... and what then? The website will use MariaDB version locally? If you never do this - probably it will be OK, at least this strategy is simple. But keep in mind that if you will generate a migration in dev mode - it will use MySQL version instead of MariaDB, but the problem that you want to use MariaDB as you need to run that migration on prod.

A better would be to create an env var in .env files and set it to MySQL version locally and to MariaDB version on prod- this should be the correct way to solve this.

But ideally, install and start using MariaDB locally too :)

> server_version: 'mysql-5.7.32'

Haha, uh oh, almost like that :) With MySQL it's simplified to just "5.7.32", you can see more config options here in the docs: https://symfony.com/doc/cur... . As you can see, MariaDB complicates that value a bit, but it's simple with MySQL. Actually, it's difficult to remember, so I usually look at the docs when I need to configure it.

Cheers!

Reply
Jonathan P. Avatar
Jonathan P. Avatar Jonathan P. | posted 3 years ago

Hello, when i saw the naming strategy changed in the doctrine.yaml
file i got scared ! When i dump the sql for the schema:update cmd, i see
a lot of changes regarding some tables. Thing is i have a lot of
relations in there, so if i actually udpate my database schema, it
breaks because of foreign keys. What can i do about that ? I do not want
to loose my data, and my database has a lot of tables !

Reply

Hey @elbarto

You can choose whatever naming strategy is convenient for you, or you can create your own strategy in case you need to. Check line 298 https://symfony.com/doc/current/bundles/DoctrineBundle/configuration.html

I believe the CamelCaseNamingStrategy is set by default, you can change it to use the UnderscoreNamingStrategy

I hope it helps. Cheers!

Reply
Jonathan P. Avatar

Hello Diego,

If i put back doctrine.orm.naming_strategy.underscore in my doctrine.yaml file, i then get a deprecation message : User Deprecated: Creating Doctrine\ORM\Mapping\UnderscoreNamingStrategy without making it number aware is deprecated and will be removed in Doctrine ORM 3.0.

The thing is, what solutions do we have if i want to pass from underscore to underscore_number_aware strategy and updating my database. Because as i said earlier, if i have few tables named table1_test , table2_test. They will become table_1_test and table_2_test.
On a schema:update it breaks because i have a bunch of relations (FK) inside of them, thus forbidding me to update my schema.

Thanks in advance !

Reply

Hmm, you can ignore that deprecation :D
or, another solution is to manually define the name of all the tables you have problems with


/**
 * @ORM\Table(name="table2_test")
 */
class SomeEntity

The naming strategy is only used when Doctrine have to give it a name to your tables/fields

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

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
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "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
        "doctrine/persistence": "^1.3.7", // 1.3.8
        "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.17.6
        "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
    }
}
userVoice