Laravel advanced database seeding

Jurgen Bosch
Mar 8, 2018 · 5 min read

Migrations and seeders are a wonderful part of the Laravel framework. Migrations help you to create and modify your database structure. Migrations prevent you from getting into issues with database changes when working with teammates (if you’ve been there you’ll probably recognize it!). Migrations are best defined as version control for your database.

More information about basic migrations can be found in the Laravel documentation at https://laravel.com/docs/5.6/migrations.

Below an example of a basic migration in Laravel

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('users');
}
}

The table above is the most simple migration available. Let’s try to extend this migration a little bit.

Create a table with a relation

Lets say that our users have a phone or maybe multiple phones. If we want to store that information in our database we have to create a ‘phones’ table and create a relation between the phone and the owner of the phone. We assume that each phone belongs to a single user.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePhonesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('phones', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->string('brand');
$table->string('model');
$table->timestamps();

$table->foreign('user_id')->references('id')->on('users');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('phones');
}
}

In the example above we’re seeing some changes compared to the first migration. The column ‘user_id’ is the column where we will store the id of the user. The naming convention is ‘table_column’ (notice that the table name is singular).

Also note that I added the ‘unsigned’ function to the user_id column. The referenced column ‘ID’ in the users table is created with the increments function which is by default unsigned and as most of you know; you cannot use a foreign key from a signed field to a table with an unsigned field.

$table->foreign('user_id')->references('id')->on('users');

The code above creates the MySQL foreign key. Creating the foreign key is not mandatory but I personally prefer to do it. You can read more about MySQL’s foreign keys at https://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html


Seeding your database (with a related model reference)

When your database structure is ready you want to test your database with data in it and/or have some data in your database to work with in your software. Let’s create a simple database seeder.

<?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
\App\User::insert([
'name' => 'John Doe',
'email' => 'john@doe.com'
]);

\App\User::insert([
'name' => 'Jane Doe',
'email' => 'jane@doe.com'
]);
}
}

Pretty straight forward right? But what if you want to add 1,000 users? Well thats where a factory comes in!

Creating a seeder with Faker data (using a factory)

In Laravel we can create a factory to perform a seeder multiple times and produce fake data. A factory can easily be made with the following command

php artisan make:factory UserFactory

Our factory should look like this

<?php

use Faker\Generator as Faker;

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

In the example above you can see that we define a factory for the User model and we use Faker to generate fake data. More information about Faker can be found at https://github.com/fzaninotto/Faker.

Once we have our factory in place we can use this factory to create the seeder for the database. We modify the previous used seeder to this:

<?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$users = factory(App\User::class, 1000)->create();
}
}

As you can see we are now creating a 1,000 different users and because we use the unique() function for the email address all users will have a unique value in our database.

Pretty useful right? But there is more!

Seed your database with a relation

After we created the seeder for our users table we also want to seed our phones table and connect each phone with a random user. Of course we can pick a random number between 1 and 1,000 and use that as the user_id, but what if we work with real users and (for some reason) user ID’s 6, 140 and 578 are deleted. Using this ID’s can create an issue in our software. Lets see how we can avoid this and connect a phone with a real user.

<?php

use Illuminate\Database\Seeder;

class PhonesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
# Lets create 150 random phones
$phones = factory(\App\Phone::class, 150)->create([
'user_id' => $this->getRandomUserId()
]);
}

private function getRandomUserId() {
$user = \App\User::inRandomOrder()->first();
return $user->id;
}
}

In the example above I use a factory to generate 150 phones. In the create function you can overwrite any value from the factory for each record created in the database. In this example I overwrite the user_id that is defined in the factory and collect 1 random ID from the users table. Pretty simple but highly effective.

Use default values and null values

In every database design there are mandatory fields and fields which are not mandatory. It is a common mistake that in migrations and seeders all fields are populated with the right data. Populating all fields always with a value can cause problems in your software when using it in real life.

When I create a seeder for a field that not mandatory I usually use the array_random function to seed the column either with a (faker) value or a null value. An example is shown below;

<?php

use Faker\Generator as Faker;

$factory->define(\App\Company::class, function (Faker $faker) {
return [
'name' => $faker->company,
'headquarter' => array_random([$faker->city, null]),
'no_employees' => array_random([rand(1, 250), null]),
'cover_letter' => array_random([$faker->realText(350), null])
];
});

In the example above the company name is mandatory. The other columns are not so I used the ‘nullable()’ function in the migration. Using the array_random function gives me the ability to seed the database with null values … sometimes.

After reading all this I hope everybody is convinced about the added value of migrations and seeders. If you are not convinced yet, there is 1 really good additional reason: You can use these migrations and seeders in your tests which prevents you from repeating yourself.

I hope this information helps you to write better migrations and if you have any question or comments to improve this article then please share it with me ;)

Jurgen Bosch

Written by

Entrepreneur / freelance web developer in Deventer (NL) and Miami (USA). https://jurgenbosch.nl https://whereveryouare.io

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade