Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Data Fixtures

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 $12.00

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

Login Subscribe

Our /questions/new page is nice... it gave us a simple way to create and save some dummy data, so that we could have enough to work on the homepage & show page.

Having a rich set of data to work with while you're developing is pretty important. Without it, you're going to spend a lot of time constantly setting up your database before you work on something. It's a big waste in the long-run.

Installing DoctrineFixturesBundle

This "dummy data" has a special name: data fixtures. And instead of creating them in a random controller like QuestionController, we can install a bundle to do it properly. Find your terminal and run:

composer require orm-fixtures --dev

This is another flex alias: orm-fixtures installs doctrine/doctrine-fixtures-bundle

When this finishes... it installed a recipe! I committed all of my changes before recording, so I'll run:

git status

to see what it did. Ok: it updated the normal composer.json, composed.lock and symfony.lock files, it enabled the bundle and ooh: it created a new src/DataFixtures/ directory! Let's go see what's inside src/DataFixtures/ - a shiny new AppFixtures class!

... lines 1 - 2
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
// $product = new Product();
// $manager->persist($product);
$manager->flush();
}
}

The DoctrineFixturesBundle that we just installed is a beautifully simple bundle. First, we create one or more of these fixture classes: classes that extend Fixture and have this load() method. Second, inside load(), we write normal PHP code to create as many dummy objects as we want. And third, we run a new console command that will call the load() method on every fixture class.

Fixing the Type-Hint

Before we get to work, PhpStorm is mad! The details aren't too important and this code would work... despite what PhpStorm is saying. But to remove the warning and make our code future-proof with newer versions of Doctrine, find the ObjectManager type-hint and replace it with one from Doctrine\Persistence.

... lines 1 - 2
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
... lines 12 - 15
}
}

Creating Dummy Data

Anyways: let's see this bundle in action. Find the new() method in the controller, copy all of the question-creating code and delete it. We'll properly create this page in a future tutorial when we talk about forms... so let's just render that: this sounds like a great feature for V2!

... lines 1 - 14
class QuestionController extends AbstractController
{
... lines 17 - 41
public function new()
{
return new Response('Sounds like a GREAT feature for V2!');
}
... lines 46 - 86
}

Back in AppFixtures, paste the code and... check it out! PhpStorm was smart enough to see that we're using the Question class and ask us if we want to import its use statement. We definitely do!

... lines 1 - 4
use App\Entity\Question;
... lines 6 - 8
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$question = new Question();
$question->setName('Missing pants')
->setSlug('missing-pants-'.rand(0, 1000))
->setQuestion(<<<EOF
Hi! So... I'm having a *weird* day. Yesterday, I cast a spell
to make my dishes wash themselves. But while I was casting it,
I slipped a little and I think `I also hit my pants with the spell`.
When I woke up this morning, I caught a quick glimpse of my pants
opening the front door and walking out! I've been out all afternoon
(with no pants mind you) searching for them.
Does anyone have a spell to call your pants back?
EOF
);
if (rand(1, 10) > 2) {
$question->setAskedAt(new \DateTime(sprintf('-%d days', rand(1, 100))));
}
$question->setVotes(rand(-20, 50));
$entityManager->persist($question);
$manager->flush();
}
}

The only problem now is that we don't have an $entityManager variable. Hmm, but we do have a $manager variable that's passed to the load() method - it's an ObjectManager?

This is actually the entity manager in disguise: ObjectManager is an interface that it implements. So change the persist() call to $manager... and we only need one flush().

... lines 1 - 4
use App\Entity\Question;
... lines 6 - 8
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
... lines 13 - 34
$manager->persist($question);
$manager->flush();
}
}

Done! Well, this isn't a very interesting fixture class... it's only going to create one Question.... but it's a good start. Let's see if it works!

Executing the Fixtures

Head over to your terminal. The new bundle gave us one new command: doctrine:fixtures:load. Execute that through the symfony binary:

symfony console doctrine:fixtures:load

It asks us to confirm because each time we run this command, it will completely empty the database before loading the new data. And... I think it worked! Go check out the homepage. Refresh and... yes! We have the one question from the fixture class.

This isn't that useful yet, but it gave us a chance to see how the bundle works. Oh, and if you don't see anything on this page, it's probably because the one Question that was loaded has an askedAt set to null... so it's not showing up. Try re-running the command once or twice to get a fresh Question.

So what I love about DoctrineFixturesBundle is how simple it is: I have a load() method where I can create and save as many objects as I want. We can even create multiple fixtures classes to organize better and we can control the order in which each is called.

What I hate about DoctrineFixturesBundle is that... I need to do all this work by hand! If you start creating a lot of objects - especially once you have database relationships where objects are linked to other objects... these classes can get ugly fast. And they're not much fun to write.

So, next: let's use a shiny new library called Foundry to create numerous, random, rich dummy data.

Leave a comment!

15
Login or Register to join the conversation
Rene V. Avatar

After executing "composer require orm-fixtures --dev"
i run into the following error:
PHP Fatal error: During class fetch: Uncaught ReflectionException: Class "Symfony\Component\Validator\Mapping\Loader\AutoMappingTrait" not found while loading "Symfony\Bridge\Doctrine\Validator\DoctrineLoader". in /home/renevorndran/code-symfony-doctrine/start/vendor/symfony/error-handler/DebugClassLoader.php:346
Any Ideas how to solve it?

1 Reply

Hey Rene V.!

Yikes! That's a scary error! My guess is that there is some version problem - like a bug that was probably fixed in some later version of a library. For example, I found this - https://github.com/symfony/... - and it looks like your issue might be fixed with DoctrineBundle 2.43. or higher. Try: composer update doctrine/doctrine-bundle and let me know if it helped :).

Cheers!

Reply
Rene V. Avatar

I also found that tip but did not manage to get that dependancies to a working state. Updating doctrine-bundel told me to also update doctrine-extension-bundle, so i ran the following:
symfony composer update doctrine/doctrine-bundle stof/doctrine-extensions-bundle -w
But the error still pops up.
My composer.lock currently looks like: https://pastebin.com/2Ex0Lfnu

Currently running PHP 8.1.2 (cli) so i even tried with --ignore-plaform-reqs because the following problem appeared:

Problem 1
- stof/doctrine-extensions-bundle[v1.4.0, ..., v1.5.0] require php ^7.1.3 -> your php version (8.1.2) does not satisfy that requirement.
- stof/doctrine-extensions-bundle v1.6.0 requires symfony/config ^4.4 || ^5.2 -> found symfony/config[v4.4.0, ..., v4.4.37, v5.2.0, ..., v5.4.3] but these were not loaded, likely because it conflicts with another require.
- stof/doctrine-extensions-bundle v1.7.0 requires symfony/config ^4.4 || ^5.2 || ^6.0 -> found symfony/config[v4.4.0, ..., v4.4.37, v5.2.0, ..., v5.4.3, v6.0.0, v6.0.2, v6.0.3] but these were not loaded, likely because it conflicts with another require.
- Root composer.json requires stof/doctrine-extensions-bundle ^1.4 -> satisfiable by stof/doctrine-extensions-bundle[v1.4.0, v1.5.0, v1.6.0, v1.7.0].
Reply

Hey Rene,

From the log, looks like your PHP version that is 8.1.2 does not fit requirements for those bundles. "stof/doctrine-extensions-bundle" of v1.5.0 does not work on PHP 8 yet. But for the next available version of stof/doctrine-extensions-bundle which is v1.6.0 you need to have symfony/config 5.2, but if you're using project code from this course you most probably have 5,1 installed. So, to be able to upgrade "stof/doctrine-extensions-bundle" to 1.6.0 at least - you need to upgrade Symfony packages first, at least to 5.2 version.

I hope this is clear and helps!

Cheers!

2 Reply
Rene V. Avatar

Ahh actually it seems to be that easy...


"extra": {
"symfony": {
"allow-contrib": false,
"require": "5.2.*"
}
}

did the trick! Thank you guys!

1 Reply

Hey Rene,

If you're talking about changing "extra.symfony.require" - yes, you have to match the version you use for "symfony/*" packages. If you changed their constraints - you also need to change that place too to match them.

Anyway, I'm happy to hear you were able to fix it finally, good job!

Cheers!

1 Reply
Sean Avatar

Ran into issue when running composer require orm-fixtures --dev, gave huge nasty error. Solution at https://stackoverflow.com/q... fixed it for me. Just putting here in case anyone else runs into issue :)

1 Reply

Hey Sean!

Thanks for the post here! Thanks to this ping, I'm now following up and pushing on the fix in doctrine/data-fixtures so we can get it as soon as possible - https://github.com/doctrine...

In the mean time, the StackOverflow you linked to has a few workarounds. Not ideal, but doable :).

Thanks!

1 Reply
Abdel Avatar

Hello, first of all thank you for this tutorial. I just want to report this little "mistake" in AppFixtures.php file `$entityManager->persist($question);` you forgot to rename `$entityManager` to `$manager` ^^.

Reply

Hey @Abdel!

Ah! You're absolutely right! I'm not sure how that happened :). We'll get it fixed up asap!
Thanks for the note!

EDIT: I replied too soon! Yes, when we paste the code in, we paste in $entityManager - it's in this code block - https://symfonycasts.com/sc... - but we fix it in the very next code block:

> The only problem now is that we don't have an $entityManager variable.

And the next code block fixes it https://symfonycasts.com/sc...

So, I think it's all correct - but I appreciate you keeping a close eye on things!!

Cheers!

Reply

Hey Nicolas,

Glad you figured it out, well done! Yeah, when you work with entities - you should pass a real objects instead of just ids :)

Cheers!

Reply

Hey Nicolas,

It's clearly from the error that the method setGenre() expects an instance of MusicGenre but got string... First of all, I'd recommend you to debug what is that string exactly, it will give you more context. For this, temporarily remove that type MusicGenre type from the setGenre() and add dd($genre) into that method to dump the actual value. Then run the same steps to see more context now - it should help you to figure out what you do wrong.

If it still does not help, try to see all your setGenre() method calls in code, in particular, in your fixtures code to see where you pass the string instead of an object :)

I hope this helps!

Cheers!

Reply
Cat in space

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

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.4.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^2.1", // 2.1.1
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
        "doctrine/orm": "^2.7", // 2.8.2
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "knplabs/knp-time-bundle": "^1.11", // v1.16.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "sentry/sentry-symfony": "^4.0", // 4.0.3
        "stof/doctrine-extensions-bundle": "^1.4", // v1.5.0
        "symfony/asset": "5.1.*", // v5.1.2
        "symfony/console": "5.1.*", // v5.1.2
        "symfony/dotenv": "5.1.*", // v5.1.2
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.1.*", // v5.1.2
        "symfony/monolog-bundle": "^3.0", // v3.5.0
        "symfony/stopwatch": "5.1.*", // v5.1.2
        "symfony/twig-bundle": "5.1.*", // v5.1.2
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.1.*", // v5.1.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.0.4
        "twig/twig": "^2.12|^3.0" // v3.0.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
        "symfony/debug-bundle": "5.1.*", // v5.1.2
        "symfony/maker-bundle": "^1.15", // v1.23.0
        "symfony/var-dumper": "5.1.*", // v5.1.2
        "symfony/web-profiler-bundle": "5.1.*", // v5.1.2
        "zenstruck/foundry": "^1.1" // v1.5.0
    }
}