Using Faker for Seeding Data

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

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!

  • 2020-03-09 Diego Aguiar

    Hey Caeema

    The only problem with choosing a OneToOne relationship randomly is that it may assign the same object twice, you would have to remove it from the array after being selected.

    Cheers!

  • 2020-03-09 Victor Bocharsky

    Hey JLChafardet,

    Well done! Glad you solved this yourself! Yeah, error message was pretty clear I suppose, you just need to convert that int number into a real entity, it sounds like you just miss something in your code.

    Cheers!

  • 2020-03-09 Caeema

    Is this al, system can be use also for a OneToOne relation ?
    I'm not sure how to deal with this but I'm really interested to have your advices about it.

    Thanks!

  • 2020-03-05 JLChafardet

    got it in the end, its further ahead in the doctrine track, although isnt as elegant as one would want, its great for the usage in faker :D

  • 2020-03-05 JLChafardet

    this is going to be a tad tough lol, no clue how to randomize that _0 yet.

  • 2020-03-05 JLChafardet

    think got it, using the refference that we added earlier in baseFixture.

    $novel->setAuthor($this->getReference(Author::class.'_0'));

    now gota figure out how to "randomize" that _0 so it loops through the list of authors, and assigns each novel to a random existing author.

  • 2020-03-05 JLChafardet

    hey @weaverryan it's been a while, how are things going?

    I've got a question here regarding Faker and fixtures, I'm trying to create some fixtures for fake content (as its supposed to be), and I've got a couple of relationships between the tables, Author, Novel, Chapter (entities), 1 Author can have many Novel, 1 Novel can have many Chapter,

    so in Author Entity:


    /**
    * @ORM\OneToMany(targetEntity="App\Entity\Novel", mappedBy="author")
    */
    private $novels;

    in Novel Entity:


    /**
    * @ORM\ManyToOne(targetEntity="App\Entity\Author", inversedBy="novels")
    * @ORM\JoinColumn(nullable=false)
    */
    private $author;

    in Chapter Entity:


    /**
    * @ORM\ManyToOne(targetEntity="App\Entity\Novel", inversedBy="chapter")
    * @ORM\JoinColumn(nullable=false)
    */
    private $novel;

    now whenever I try to fake a random number for the author id in the novel's fixture, i get an error telling me it must be an instance of Author

    In Novel.php line 132:


    Argument 1 passed to App\Entity\Novel::setAuthor() must be an instance of App\Entity\Author or null,
    int given, called in D:\vhosts\novelreader\src\DataFixtures\NovelFixtures.php on line 18

    The code for that particular field is $novel->setAuthor($this->faker->numberBetween($min = 1, $max = 10)); as I have only 10 authors with ids from 1 to 10

    how can I get that working? as I see it happening when I create fixtures for the chapters of each novel all the same. I want to create 50 novels, and each novel should have X amount of Chaptes
    thats not important though, the thing is, I'm certain I'll get the same issue, telling me that setNovel should be an instance of App\Entity\Novel or null.

  • 2020-01-23 weaverryan

    Hey Ed Barnard!

    > Symfony's structure was completely non-intuitive to me, as was Doctrine's idea of writing the queries for me

    Yea, Symfony 2's structure was big and weird - I'm happy that it's much smaller now. And for someone with a lot of database experience, I *consistently* see that problem with Doctrine: it's super hard to think about classes & objects instead of tables and columns, especially with Doctrine relations :).

    > Starting with an empty repo, trying to embrace the Symfony 5 way of doing things, is a challenge - and I'm trying to help out by leaving Symfony 5 notes as I go.

    We appreciate that!

    > I just discovered "Object Design Style Guide" by Matthias Noback. It turns out to be a philosophy of how to write code in the Symfony 5 ecosystem

    Matthias is a SUPER smart and also pragmatic guy. I definitely recommend his stuff - good find!

    Cheers!

  • 2020-01-23 Ed Barnard

    For me it was Symfony 2 in 2013. My first non-proprietary PHP framework. Symfony's structure was completely non-intuitive to me, as was Doctrine's idea of writing the queries for me. So I grabbed the only book available, Fabien's. It's still on the shelf here! I'm just returning to Symfony from years of CakePHP, thus working through your (plural, the whole team's) tutorial sequences. Starting with an empty repo, trying to embrace the Symfony 5 way of doing things, is a challenge - and I'm trying to help out by leaving Symfony 5 notes as I go.

    I just discovered "Object Design Style Guide" by Matthias Noback. It turns out to be a philosophy of how to write code in the Symfony 5 ecosystem. It carefully keeps that fact a secret which makes it fun! For example it explains when you should inject dependencies into the constructor of a service object, and when to inject the dependency into the method. A service, once constructed, should be immutable. So inject the logger into the constructor, but inject the User or Request object into the method, as they are specific to that method call, and the next call could be for a different user or request. (Noback's book is a Manning publication.)

  • 2020-01-23 weaverryan

    Hey Ed Barnard!

    You've got a great memory... or you've just been around Symfony for as LONG as I have ;). I first learned Symfony (version 1) because I was searching for a framework in PHP and Symfony had GREAT documentation. That was Francois' work - so I still owe him a lot for giving me such a great start :). Were you also a symfony 1 user?

    Cheers!

  • 2020-01-18 Ed Barnard

    Aha! This introduction explains the co-author of Fabien's first Symfony book. Francois was the documentation lead at the time.

  • 2019-09-17 Victor Bocharsky

    Hey Dion,

    Thank you for this tip! Yeah, you need to create Faker instance before calling loadData() where you're going to use it.

    I hope this will help Лёлик!

    Cheers!

  • 2019-09-16 Dion Potkamp

    I've got the same error but i fixed it myself. The order is wrong:
    $this->manager = $manager;
    $this->loadData($manager);
    $this->faker = Factory::create();

    loadData and Faker must be switched, otherwise it will use faker before it is created...
    $this->manager = $manager;
    $this->faker = Factory::create();
    $this->loadData($manager);

  • 2019-09-10 Victor Bocharsky

    Hey Damir,

    Glad it helped!

    Cheers!

  • 2019-09-09 Damir Vakhitov

    Hey Victor!
    You are right. The problem was solved after changing "Order" to "TOrder".
    Thanks a lot!

  • 2019-09-09 Victor Bocharsky

    Hey Damir,

    I suppose it depends on queries, if you escape table and column names with back ticks - it should work even if you use reserved words. But better do not use reserved words at all, just simplify a lot of things :)

    Cheers!

  • 2019-09-06 Damir Vakhitov

    Hey Victor,
    Thank u for replay.
    Maybe you are right.
    But, when I migrated entities, there were no problems with "order" table name.
    I know, that in PostgreSQl 11 "order" word are reserved.
    I already have added "shopping carts" by migration. I hope, when I will add "orders", it's won't be impossible.
    Cheers!

  • 2019-09-06 Victor Bocharsky

    Hey Damir,

    I think I know the problem! Looks like "order" is a reserved word in PostgreSql, so the query looks invalid for it. Try to rename your "order" table to something different, e.g. to "orders". I suppose the same might be needed for "user" table, because "user" might be a reserved word as well.

    Cheers!

  • 2019-09-05 Damir Vakhitov

    Hello!
    In new project I made ShoppingCartFixtures that extends BaseFixtures. But when I try to load fixtures I get error:
    "An exception occurred while executing 'DELETE FROM order':

    SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "order"
    LINE 1: DELETE FROM order"

    I didn't make OrderFixtures yet, and ShopingCart does not contain any reference to Order entity.
    I use postgresql 9.
    Thanks in advance!

  • 2019-08-29 Victor Bocharsky

    Hey Cesar,

    For unique things I think you can try unique() modifier from Faker, see their docs for more info: https://github.com/fzaninot... . As a workaround, you can manually add some unique indexes for generated strings to be extra sure it's unique, for example like:


    for ($i = 0; $i < 10; $i++) {
    $article = new Article();
    // ...
    $article->setSlug($this->faker->slug . '-' . $i)
    }

    I hope this helps!

    Cheers!

  • 2019-08-28 Cesar Delgado

    Hello. I finished to create my Fixtures and they are working well. Except for one little thing: They are not respecting a UniqueEntity constrain that I have in a couple of Entities. Do you know a way to run the fixtures with this kind of constrain? Just in case, I don't remember if when you were using Alice that was possible.

  • 2019-08-12 Victor Bocharsky

    Hey Damir,

    Ah, ok, sounds reasonable! :) Thank you for your feedback, might be useful for other users.

    Cheers!

  • 2019-08-12 Damir Vakhitov

    No. By mistake I deleted "extends BaseFixtures" in one of tow fixtures. Banal carelessness.
    Thanks.

  • 2019-08-12 Victor Bocharsky

    Hey Damir,

    Glad you were able to fix it. I bet the problem was in missing implemented interface, so the system does not know that you have a few fixture files.

    Cheers!

  • 2019-08-09 Damir Vakhitov

    Thanks, but I solve this problem.

  • 2019-08-09 Damir Vakhitov

    Hello. I made BaseFixtures, and made to Classes extends this class.
    But, when I call "php bin/console doctrine:fixtures:load", it execute only one fixture.
    How can I load many fixtures, that extends BaseFixtures?

  • 2019-05-10 Diego Aguiar

    haha, if you win it, invite us some beers!

  • 2019-05-10 Coder

    winning lottery numbers, thats the cool thing, I will be rich now, thanks :)

  • 2019-02-04 Victor Bocharsky

    Hey Cryptoblob,

    Yes, exactly, that's because they are static. And sure, if you removed "static" keyword - you'd need to access them via "$this". I don't think there's a strong reason *why* exactly they are static. Mostly, you make properties or methods static if you want to call them without creating an object, i.e. call them in class context. But here, as I see, we call them in object context, so they might not be static. So I suppose it's just kinda personal convention, not sure :)

    Cheers!

  • 2019-02-02 Cryptoblob

    I've just realised they are static properties, thats why self was used. If they weren't static, could we use '$this->articleTitles'?

    Is there any reason they're static?

  • 2019-02-02 Cryptoblob

    why are we using 'self::$articleTitles' instead of '$this->articleTitles'?

  • 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