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!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.5",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.8", // 1.8.2
        "doctrine/doctrine-bundle": "^2.1", // 2.1.0
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.1
        "doctrine/orm": "^2.7", // v2.7.3
        "knplabs/knp-markdown-bundle": "^1.8", // 1.8.1
        "knplabs/knp-time-bundle": "^1.11", // v1.12.0
        "sensio/framework-extra-bundle": "^5.5", // v5.6.1
        "sentry/sentry-symfony": "^3.4", // 3.5.2
        "stof/doctrine-extensions-bundle": "^1.4", // v1.4.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.9.0
        "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.7.3
        "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.3.1
        "symfony/debug-bundle": "5.1.*", // v5.1.2
        "symfony/maker-bundle": "^1.15", // v1.20.0
        "symfony/var-dumper": "5.1.*", // v5.1.2
        "symfony/web-profiler-bundle": "5.1.*", // v5.1.2
        "zenstruck/foundry": "^1.1" // v1.1.0
    }
}