Buy
Buy

Using Faker for Seeding Data

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

Login Subscribe

The problem now is that our dummy data is super, duper boring. It's all the same stuff, over, and over again. Honestly, I keep falling asleep when I see the homepage. Obviously, as good PHP developers, you guys know that we could put some random code here and there to spice things up. I mean, we do already have a random $publishedAt date:

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
public function loadData(ObjectManager $manager)
{
$this->createMany(Article::class, 10, function(Article $article, $count) {
... lines 13 - 35
if (rand(1, 10) > 2) {
$article->setPublishedAt(new \DateTime(sprintf('-%d days', rand(1, 100))));
}
... lines 39 - 43
});
... lines 45 - 46
}
}

But, instead of creating that random data by hand, there's a much cooler way. We're going to use a library called Faker. Google for "Faker PHP" to find the GitHub page from Francois Zaninotto. Fun fact, Francois was the original documentation lead for symfony 1. He's awesome.

Anyways, this library is all about creating dummy data. Check it out: you can use it to generate random names, random addresses, random text, random letters, numbers between this and that, paragraphs, street codes and even winning lottery numbers! Basically, it's awesome.

Installing Faker

So let's get it installed. Copy the composer require line, move over and paste. But, add the --dev at the end:

composer require fzaninotto/faker --dev

Because we're going to use this library for our fixtures only - it's not needed on production.

Setting up Faker

When that finishes, head back to its docs so we can see how to use it. Ok: we just need to say $faker = Faker\Factory::create(). Open our BaseFixture class: let's setup Faker in this, central spot. Create a new protected $faker property:

... lines 1 - 9
abstract class BaseFixture extends Fixture
{
... lines 12 - 15
protected $faker;
... lines 17 - 38
}

And down below, I'll say, $this->faker = and look for a class called Factory from Faker, and ::create():

... lines 1 - 6
use Faker\Factory;
... lines 8 - 9
abstract class BaseFixture extends Fixture
{
... lines 12 - 15
protected $faker;
... lines 17 - 19
public function load(ObjectManager $manager)
{
$this->manager = $manager;
$this->faker = Factory::create();
... lines 24 - 25
}
... lines 27 - 38
}

We should also add some PHPDoc above the property to help PhpStorm know what type of object it is. Hold Command - or Ctrl - and click the create() method: let's see what this returns exactly. Apparently, it returns a Generator.

Cool! Above the property, add /** @var Generator */ - the one from Faker:

... lines 1 - 7
use Faker\Generator;
abstract class BaseFixture extends Fixture
{
... lines 12 - 14
/** @var Generator */
protected $faker;
... lines 17 - 38
}

Perfect! Now, using Faker will be as easy as pie! Specifically, eating pie, cause, that's super easy.

Generating Fake Data

Open ArticleFixtures. We already have a little bit of randomness. But, Faker can even help here: change this to if $this->faker->boolean() where the first argument is the chance of getting true. Let's use 70: a 70% chance that each article will be published:

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
public function loadData(ObjectManager $manager)
{
$this->createMany(Article::class, 10, function(Article $article, $count) {
... lines 13 - 34
// publish most articles
if ($this->faker->boolean(70)) {
... line 37
}
... lines 39 - 43
});
... lines 45 - 46
}
}

And below, we had this long expression to create a random date. Now say, $this->faker->dateTimeBetween('-100 days', '-1 days'):

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
public function loadData(ObjectManager $manager)
{
$this->createMany(Article::class, 10, function(Article $article, $count) {
... lines 13 - 34
// publish most articles
if ($this->faker->boolean(70)) {
$article->setPublishedAt($this->faker->dateTimeBetween('-100 days', '-1 days'));
}
... lines 39 - 43
});
... lines 45 - 46
}
}

I love it! Down for heartCount, use another Faker function: $this->faker->numberBetween(5, 100):

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
public function loadData(ObjectManager $manager)
{
$this->createMany(Article::class, 10, function(Article $article, $count) {
... lines 13 - 39
$article->setAuthor('Mike Ferengi')
->setHeartCount($this->faker->numberBetween(5, 100))
... line 42
;
});
... lines 45 - 46
}
}

After these few improvements, let's make sure the system is actually as easy as pie. Find your terminal and run:

php bin/console doctrine:fixtures:load

No errors and... back on the browser, it works! Of course, the big problem is that the title, author and article images are always the same. Snooze.

Faker does have methods to generate random titles, random names and even random images. But, the more realistic you make your fake data, the easier it will be to build real features for your app.

Generating Controller, Realistic Data

So here's the plan: go back to ArticleFixtures. At the top, I'm going to paste in a few static properties:

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
private static $articleTitles = [
'Why Asteroids Taste Like Bacon',
'Life on Planet Mercury: Tan, Relaxing and Fabulous',
'Light Speed Travel: Fountain of Youth or Fallacy',
];
private static $articleImages = [
'asteroid.jpeg',
'mercury.jpeg',
'lightspeed.png',
];
private static $articleAuthors = [
'Mike Ferengi',
'Amy Oort',
];
... lines 26 - 64
}

These represent some realistic article titles, article images that exist, and two article authors. So, instead of making completely random titles, authors and images, we'll randomly choose from this list.

But even here, Faker can help us. For title, say $this->faker->randomElement() and pass self::$articleTitles:

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
... lines 10 - 26
public function loadData(ObjectManager $manager)
{
$this->createMany(Article::class, 10, function(Article $article, $count) {
$article->setTitle($this->faker->randomElement(self::$articleTitles))
... lines 31 - 60
});
... lines 62 - 63
}
}

We'll let Faker do all the hard work.

For setSlug(), we could continue to use this, but there is also a $faker->slug method:

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
... lines 10 - 26
public function loadData(ObjectManager $manager)
{
$this->createMany(Article::class, 10, function(Article $article, $count) {
$article->setTitle($this->faker->randomElement(self::$articleTitles))
->setSlug($this->faker->slug)
... lines 32 - 60
});
... lines 62 - 63
}
}

The slug will now be totally different than the article title, but honestly, who cares?

For author, do the same thing: $this->faker->randomElement() and pass self::$articleAuthors:

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
... lines 10 - 26
public function loadData(ObjectManager $manager)
{
$this->createMany(Article::class, 10, function(Article $article, $count) {
... lines 30 - 56
$article->setAuthor($this->faker->randomElement(self::$articleAuthors))
... lines 58 - 59
;
});
... lines 62 - 63
}
}

Copy that, and repeat it one more time for the imageFile, this time using self::$articleImages:

... lines 1 - 7
class ArticleFixtures extends BaseFixture
{
... lines 10 - 26
public function loadData(ObjectManager $manager)
{
$this->createMany(Article::class, 10, function(Article $article, $count) {
... lines 30 - 56
$article->setAuthor($this->faker->randomElement(self::$articleAuthors))
... line 58
->setImageFilename($this->faker->randomElement(self::$articleImages))
;
});
... lines 62 - 63
}
}

Awesome! Let's go reload those fixtures!

php bin/console doctrine:fixtures:load

No errors! Find your browser and, try it! Oh, it's so much better.

If creating nice, random data seems like a small thing, it's not! Having rich data that you can easily load will increase your ability to create new features and fix bugs fast. It's totally worth it.

Next! Let's install a really cool library with automatic slugging super-powers.

Leave a comment!

  • 2018-07-27 Diego Aguiar

    I'm not sure if I understood your question correctly, but what you want is to create a new Author when calling setAuthor on a User object? If that's the case, that's not how it is supposed to work. You have to create a new Author instance, fulfill its data and then pass it to the setter method (And of course, calling persist & flush)

    Cheers!

  • 2018-07-26 cybernet2u

    how do i make "setAuthor" to generate random entry from database ? or at least random generate with predefined id's ?
    ```
    /**
    * @ORM\ManyToOne(targetEntity="App\Entity\Users", inversedBy="adverts")
    * @ORM\JoinColumn(nullable=false)
    */
    private $author;
    ```

  • 2018-07-12 weaverryan

    Hey Лёлик!

    Hmm. So, it sounds like $this->fake is null for some reason. Make sure that your fixture class is extending the BaseFixture AND that your method is called loadData() instead of load() (otherwise, you will override the code that sets this property).

    Let me know if this helps!

    Cheers!

  • 2018-07-12 Лёлик

    When i call php ./bin/console doctrine:fixtures:load
    I get an error
    Call to a member function text() on null