Buy

Awesome Random Fixtures

This Chapter isn't quite ready yet

Rest assured, the gnomes are hard at work on completing this video

Look at ArticleFixtures: we created 10 articles. So, the system has references from 0 to 9. In CommentFixture, spice things up: replace the 0 with $this->faker->numberBetween(0, 9). Try the fixtures again:

php bin/console doctrine:fixtures:load

No errors! And... check the database:

php bin/console doctrine:query:sql 'SELECT * FROM comment'

That is much better! Just like that, each comment is related to a random article!

Making this Random Reference System Reusable

I really like this idea, where we can fetch random objects in our fixtures. So, let's make it easier! In BaseFixture, add a new private property on top called $referencesIndex. Set that to an empty array.

I'm adding this because, at the bottom of this class, I'm going to paste in a new, method that I prepared. It's a little ugly, but this new getRandomReference() does exactly what its name says: you pass it a class, like the Article class, and it will find a random Article for you. That's super friendly!

In CommentFixture, use it: $comment->setArticle() with $this->getRandomReference(Article::class).

To make sure my function works, try the fixtures one last time:

php bin/console doctrine:fixtures:load

And, query for the comments:

php bin/console doctrine:query:sql 'SELECT * FROM comment'

Brilliant!

Fixture Ordering

There is one last minor problem with our fixtures... they only work due to pure luck. Check this out: I'll right click on CommentFixture and rename the class to A0CommentFixture. Also allow PhpStorm to rename the file. Some of you might already see the problem. Try the fixtures now:

php bin/console doctrine:fixtures:load

Bah! Explosion!

Cannot find any references to App\Entity\Article

The error comes from BaseFixture and it basically means that no articles have been set into the reference system yet! You can see the problem in the file tree. We have not been thinking at all about what order each fixture class is executed. By default, it loads them alphabetically. But now, this is a problem! The A0CommentFixture class is being loaded before ArticleFixtures... which totally ruins our cool system!

You can also see this in the terminal: it loaded A0CommentFixture first.

DependentFixtureInterface

The solution is pretty cool. As soon as you have a fixture class that is dependent on another fixture class, you need to implement an interface called DependentFixtureInterface. This will require you to have one method. Move to the bottom, then, go to the Code -> Generate menu, or Command+N on a Mac, select "Implement Methods" and choose getDependencies(). I'll add the public before the function. Just return an array with ArticleFixtures::class.

That's it! Load them again:

php bin/console doctrine:fixtures:load

Bye bye error! It loaded ArticleFixtures first and then the comments below that. The fixtures library looks at all of the dependencies and figures out an order that makes sense.

With that fixed, let's rename the class back from this ridiculous name to CommentFixture.

To celebrate, move over, refresh and... awesome! 8, random comments. We rock!

Next, let's learn about some tricks to control how Doctrine fetches the comments for an article, like, their order.

Leave a comment!

  • 2018-05-11 Matt Johnson

    Thank you!

  • 2018-05-10 Matt Johnson

    Thank you!

  • 2018-05-10 weaverryan

    Haha, hey again Matt Johnson! I'm also happy to post our version, fwiw. It looks like this:


    abstract class BaseFixture extends Fixture
    {
    // ...
    private $referencesIndex = [];

    // ...

    protected function getRandomReference(string $className) {
    if (!isset($this->referencesIndex[$className])) {
    $this->referencesIndex[$className] = [];

    foreach ($this->referenceRepository->getReferences() as $key => $ref) {
    if (strpos($key, $className.'_') === 0) {
    $this->referencesIndex[$className][] = $key;
    }
    }
    }

    if (empty($this->referencesIndex[$className])) {
    throw new \Exception(sprintf('Cannot find any references for class "%s"', $className));
    }

    $randomReferenceKey = $this->faker->randomElement($this->referencesIndex[$className]);

    return $this->getReference($randomReferenceKey);
    }
    }

    Cheers!

  • 2018-05-10 Matt Johnson

    If anyone's like me and going through this before it's finished and would like an example for the getRandomReference function, here you go:

    protected function getRandomReference($class)
    {
    $all = $this->manager->getRepository($class)->findAll();
    return $all[array_rand($all)];
    }