Tutorial

How To Generate Placeholder Data with Model Factories in Laravel

Updated on September 15, 2020
author

Samuel Oloruntoba

How To Generate Placeholder Data with Model Factories in Laravel

This tutorial is out of date and no longer maintained.

Introduction

A while back, if I needed placeholder data for my local application environment. It involved importing a database partial of an existing site, or modifying MySQL’s sakila database, or just some funky way to get data. This process was time-consuming and error-prone.

In this article, you will use fzaninotto/faker to generate placeholder data quickly and efficiently.

Warning: Since publication, fzaninotto/Faker has been deprecated and forked to FakerPHP/Faker.

Understanding the Limitations of Seeding

Laravel provides us with seeding. Seeding lets the developer input data into the local database like it would appear on the main site.

Take this scenario for example. You need to create a database with a thousand users, each user has a certain number of posts (0 - n) attached to them.

// manually make a list of users
$users = [
	[
		'username' => 'firstuser',
		'name' => 'first user',
		'email' => 'firstuser@email.com'
	],
	[
		'username' => 'seconduser',
		'name' => 'second user',
		'email' => 'seconduser@email.com'
	]
	// ...
];

Nobody wants to do this a thousand times.

Running Faker

Thanks to libraries like fzaninotto/faker, we can create a lot of fake data that makes sense.

The Faker library is available on packagist so we can install it via Composer. You do not need to install this package if you are using Laravel 5 or higher.

  1. composer require fzaninotto/faker --dev

After installing faker, we can then use our seeder class to create as many users as we want. In the run method of the UserTableSeeder class or whatever you named your seed class, do this.

public function run() {
	$faker = Faker\Factory::create();
	
	for($i = 0; $i < 1000; $i++) {
		App\User::create([
			'username' => $faker->userName,
			'name' => $faker->name,
			'email' => $faker->email
		]);
	}
}

With the above snippet, we can create a thousand users without having to go through a lot of manual labor.

Although this method saves us a lot of time and effort, it is not the most effective solution out there. When you want to create fake data for testing, you then have to copy this code and paste it into your test class. This method is not maintainable because you now have duplicate, changing a table schema would be a nightmare.

Defining Model Factories

Model Factory became part of Laravel’s features as of Laravel 5. Model Factory lets us define a pattern used in generating fake data.

Note: A while back, Jeffrey Way of Laracasts created a package called TestDummy. TestDummy allowed developers to define a pattern for creating fake data.

To use factories, we have to first define them. In the database/factories/ModelFactory.php we can create a factory there. Laravel provides a $factory global object which we can extend to define our factories.

$factory->define(App\User::class, function (Faker\Generator $faker) {
	return [
		'username' => $faker->userName,
		'email' => $faker->email,
		'name' => $faker->name
	];
});

As you can see, the define method called on the $factory object takes in two parameters. The first one is an identifier (model FQN), used to later reference the factory. The second parameter is a closure that takes in Faker\Generator class and returns an array of users.

Using create()

Now that we have our factory defined, we can spin up fresh users anytime we want. To use the defined factory (either from your tests or seed), we use the factory function provided by Laravel.

// create a user and save them to the database
$user = factory(App\User::class)->create();

This creates a single user. To create many users — just pass a second parameter to the factory function.

// create 1000 users with just one line
$users = factory(App\User::class, 1000)->create();

If you want to override some of the default values of your model, you may pass an array of values to the create method. Only the specified values will change — while the rest of the values remain set to their default values as specified by the factory.

$user = factory(App\User::class)->create([
	'username' => 'userexampleone'
]);

Using make()

Sometimes we may not want to save the data created to the database. For that we use the make method on the factory object instead of create.

$user = factory(App\User::class)->make();

Just like we did with the create method, we can also overwrite attributes of the method also. It works on the same principle as the create method.

$user = factory(App\User::class)->make([
	'username' => 'userexampletwo'
]);

Using defineAs

A model can be of different types; we can configure a model factory to use many types. For example say we want some users to be an admin, we can create a factory with $factory->defineAs() which takes in three parameters. The first parameter is the base model, the second parameter is a type and the third parameter is a closure that generates data.

$factory->defineAs(App\User::class, 'admin', function (Faker\Generator $faker) {
	return [
		'username' => $faker->userName,
		'email' => $faker->email,
		'name' => $faker->name,
		'admin' => true
	];
});

Or if you want to extend a base model factory, you can do this:

$factory->defineAs(App\User::class, 'admin', function ($faker) use ($factory) {
	$post = $factory->raw('App\User');

	return array_merge($post, ['admin' => true]);
});

To use this factory created using defineAs method, we can change the second argument to the factory from a number of posts to the new factory identifier.

factory(App\User::class, 'admin')->create();

If you want to create admin users, you can pass a third parameter to the factory function.

factory(App\User::class, 'admin', 10)->create();

Creating a Model with Relation

A basic User model usually has relations with other models. For example, a user can have posts, if we wanted to make a user have posts using model factories, we can do this.

Note: This is assuming that you setup the Eloquent model relationship (e.g., hasMany, etc.).

factory(App\User::class, 50)->create()->each(function($u) {
	$u->posts()->save(factory(App\Post::class)->make());
});

The create method returned a collection of created users. We can now loop through the users, fetch a post from the database and attach it to the user.

Using Model Factories While Testing

With Model Factories set up, you can now set up a database for testing. If you change your schema, all you need to do is update your factory definition and that’s all.

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
	public function setUp() {
		parent::setUp();
		
		Artisan::call('migrate'); //run migrations
		Eloquent::unguard(); // disable eloquent guard
	}
	
	public function it_creates_at_least_hundred_fake_users() {
		$users = factory(App\Users::class, mt_rand(100, 1000))->create();
		$user_count = count($users) >= 100;
		
		$this->assertTrue($user_count);
	}
}

This is a simple and silly example. But using model factories to handle database seeding is a lot more efficient than doing this in every test you write.

public function it_creates_at_least_hundred_fake_users() {
	$users = [];
	$faker = Faker\Factory::create();
	
	for ($i = 0; $i < mt_rand(100, 1000); $i++) {
		$users[] = App\User::create([
			'username' => $faker->userName,
			'email' => $faker->email,
			'name' => $faker->name
		]);
	}
	
	$this->assertTrue(count($users) >= 100);
}

Conclusion

Laravel’s model factories are a powerful tool for testing and seeding. Having them integrated into Laravel makes it easier for developers to test and seed data.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Samuel Oloruntoba

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more