laravelbook.com

Warning: Work in Progress!

This site contains a a few lessons on Laravel 4. It's still in it's early phases. Besides, Laravel 4 itself is a moving target and a few things may have changed by the time you read this.

I will regularly update the content. Thanks!

Database Seeding with Laravel

Database-driven applications often need data pre-seeded into the system for testing and demo purposes.

What is seed data?

Seed data is anything that must be loaded for an application to work properly. Most applications need some reference data loaded in order to run successfully in development, test, and production. You can think of this as background knowledge for your app.

It’s generally not the data your users create, though they may update it from time to time; it’s data on which their data depends. Examples include everything from an initial user account to small enumerations to huge lists of data (i.e. list of cities).

Seed data is usually unchanging. Typically, it won’t be edited in your application. But requirements can and do change, so seed data may need to be reloaded on deployed applications.

The ideal solution would be automatic: you shouldn’t have to think about it. When you check out the code and start up your app, it should be ready. It should provide data integrity: the created records should pass your validations. And it should be easy to update your seed data.

Database Seeding with Migrations

For a long time there was no good way to do this in LaravelΓÇè-ΓÇèthe primary option was populating data directly in migrations. Unfortunately, this doesn’t always work well - it makes migrations very fragile and migrations become cumbersome to maintain.

Let’s begin by creating a new database for this lesson. You will need to set your connection details in app/config/database.php, navigate to the Laravel root directory and execute php artisan migrate:install to install the migration tables.

We’ll create two new tables named “roles” and “users” using the artisan migrate:make command:

$ php artisan migrate:make create_roles_table --table=roles --create
Migration created successfully!
$ php artisan migrate:make create_users_table --table=users --create
Migration created successfully!

Let’s edit the migration files in the app/database/migrations directory and define the table schemas in the following manner.

Paste the following snippet in the *_create_roles_table.php file:

<?php

use Illuminate\Database\Migrations\Migration;

class CreateRolesTable extends Migration 
{

        public function up() 
        {
                Schema::create('roles', function($t) {
                        $t->increments('id');
                        $t->string('name', 40)->unique();
                });
        }
        
        public function down() 
        {
                Schema::drop('roles');
        }
        
}

Next, edit the *_create_users_table.php file:

<?php

use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration 
{

        public function up() 
        {
                Schema::create('users', function($t) {
                        $t->increments('id');
                        $t->string('username', 16)->unique();
                        $t->string('password', 64);
                        $t->integer('role_id')->unsigned();
                        $t->timestamps();
                });
        }

        public function down() 
        {
                Schema::drop('users');
        }

}

The snippets above should look familiar to you. The roles table has One-To-Many relationship with the users table.

Let’s create a couple of new migrations to seed data into our tables:

$ php artisan migrate:make seed_roles_table  
Migration created successfully!
$ php artisan migrate:make seed_users_table  
Migration created successfully!

Since migrations are just PHP code, they can be used to populate data in the up() method.

Content of the SeedRolesTable migration:

<?php

use Illuminate\Database\Migrations\Migration;

class SeedRolesTable extends Migration 
{

        public function up()
        {
                DB::table('roles')->insert(
                        array(
                                array('name' => 'admin'),
                                array('name' => 'user'),
                                array('name' => 'moderator'),                                
                        ));
        }

        public function down()
        {
                DB::table('roles')->delete();
        }

}

Content of the SeedUsersTable migration:

<?php

use Illuminate\Database\Migrations\Migration;

class SeedUsersTable extends Migration 
{

        public function up()
        {

                $admin_role = DB::table('roles')
                                        ->select('id')
                                        ->where('name', 'admin')
                                        ->first()
                                        ->id;
                
                $user_role = DB::table('roles')
                                        ->select('id')
                                        ->where('name', 'user')
                                        ->first()
                                        ->id;
                
                $mod_role  = DB::table('roles')
                                        ->select('id')
                                        ->where('name', 'moderator')
                                        ->first()
                                        ->id;
                        
                DB::table('users')->insert(
                        array(
                                array(
                                        'username' => 'max',
                                        'password' => Hash::make('test'),
                                        'role_id'  => $admin_role
                                ),
                                array(
                                        'username' => 'john',
                                        'password' => Hash::make('test'),
                                        'role_id'  => $user_role
                                ),
                                array(
                                        'username' => 'mary',
                                        'password' => Hash::make('test'),
                                        'role_id'  => $mod_role
                                ),
                        ));
        }

        public function down()
        {
                DB::table('users')->delete();
        }

}

We used Laravel Fluent Query Builder class to insert seed data into the table. Alternatively , you could utilize your Eloquent model object to do the same:

...
User::create(array(
                        'username' => 'john',
                        'password' => Hash::make('test'),
                        'role_id'  => $user_role));

Now that we have demonstrated data seeding using migrations, let’s rollback all the migrations by running the migrate:reset command:

$ php artisan migrate:reset
Rolled back: 2013_01_08_131059_seed_users_table
Rolled back: 2013_01_08_131051_seed_roles_table
Rolled back: 2013_01_08_115437_create_roles_table
Rolled back: 2012_12_31_075518_create_users_table

This will drop the “roles” and “users” table. Let’s delete the *_seed_roles_table.php and *_seed_users_table.php files from your app/database/migrations folder - we won’t be needing these migrations anymore. Finally, let’s reapply the migrations so that our tables are re-created:

$ php artisan migrate
Migrated: 2012_12_31_075518_create_users_table
Migrated: 2013_01_08_115437_create_roles_table

Disadvantages of Database Seeding With Migrations

Using migrations to populate data may seem attractive because they get run automatically. However, they have many disadvantages. Adding or changing data is cumbersome. Adding a new migration seems annoying. But going back into your old migrations to change your data won’t work either. The more migrations you have, the less likely the older ones are to work. If you force your migrations to populate the database tables, they are more likely to break as your models change.

It also obscures the location of the seed data. By the time you have a lot of migrations, you’re unlikely to remember that 2012_03_04_075511_alter_roles_table.php is also the place where you’re adding your default roles.

Also migrations aren’t really designed to import data into the database; their primary job is to modify the database structure itself, and it should remain that way. In the example above, we had populated our database using migrations. Just think, what would happen if we need to change the data afterwards? Should we rollback all the subsequent migrations just to modify that specific migration? Sounds pretty scary doesn’t it?

So, if we’re not using migrations for seed data, how do we populate our database? Fortunately, Laravel 4 provides an elegant solution to that problem.

Laravel Database Seeder

Starting with version 4, Artisan now offers a slick way of populating your database. Migrations should never be used for seeding example or base data required by your application. Laravel largely solves the problem with the introduction of the artisan db:seed command. The new mechanism makes it very easy to populate database tables, at the same time avoiding the problems associated with migrations.

If you go to your app/database directory of your Laravel application, you’ll see a directory named seeds there. Creating a new seed is simply done by creating a new PHP file in app/database/seeds folder. Note that the name of the seed file should match the name of the table being populated. In our case, the seed files should be named roles.php and users.php. At its simplest, the contents of a seed file is simply one or more arrays that contain baseline data for your application.

Creating New Seed File

We’re going to make the seed for our “roles” and “users” tables. Create two new files named roles.php and users.php in the app/database/seeds directory.

Let’s edit the roles.php file first:

<?php

return array(
        array('name' => 'admin'),
        array('name' => 'user'),
        array('name' => 'moderator'),
);

The code presents a multi-dimensional array where every member array is a database record. A seed file should simply return an array of data that is going to be parsed by Laravel and subsequently inserted into the database. In the snippet above, we are inserting 3 new records into the roles table.

Note that our seed file is named “roles.php” which allows Laravel to automatically deduce the correct table name (roles in our case) from the filename. Alternatively, we could explicitly provide the table name inside the array in the following manner:

<?php

return array(
        'table' => 'roles',       # name of the table
        
        array('name' => 'admin'),
        array('name' => 'user'),
        array('name' => 'moderator'),
);

This method is useful if, for some reason, the seed filename doesn’t match the name of the corresponding table.

Let’s flesh out the users.php seed file:

<?php

$admin_role = DB::table('roles')
                                ->select('id')
                                ->where('name', 'admin')
                                ->first()
                                ->id;
$user_role = DB::table('roles')
                                ->select('id')
                                ->where('name', 'user')
                                ->first()
                                ->id;
$mod_role  = DB::table('roles')
                                ->select('id')
                                ->where('name', 'moderator')
                                ->first()
                                ->id;

$now = date('Y-m-d H:i:s');

return array(
        'table' => 'users',

        array(
                'username'   => 'max',
                'password'          => Hash::make('test'),
                'created_at' => $now,
                'updated_at' => $now,
                'role_id'          => $admin_role
        ),
        
        array(
                'username'   => 'john',
                'password'          => Hash::make('test'),
                'created_at' => $now,
                'updated_at' => $now,
                'role_id'          => $mod_role          
        ),

        array(
                'username'   => 'mary',
                'password'          => Hash::make('test'),
                'created_at' => $now,
                'updated_at' => $now,
                'role_id'          => $user_role
        ),

);

Let’s seed it:

$ php artisan db:seed

Done!

You may wish to verify your database:

MariaDB [laravel_db]> SELECT * FROM roles;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | admin     |
|  2 | user      |
|  3 | moderator |
+----+-----------+
3 rows in set (0.00 sec)

MariaDB [laravel_db]> SELECT * FROM users;
+----+----------+---------------+---------+---------------------+---------------------+
| id | username | password      | role_id | created_at          | updated_at          |
+----+----------+---------------+---------+---------------------+---------------------+
|  1 | max      | $2a$08...L/2k |       1 | 2013-01-08 13:47:51 | 2013-01-08 13:47:51 |
|  2 | john     | $2a$08...lj.M |       3 | 2013-01-08 13:47:51 | 2013-01-08 13:47:51 |
|  3 | mary     | $2a$08...gAeh |       2 | 2013-01-08 13:47:51 | 2013-01-08 13:47:51 |
+----+----------+---------------+---------+---------------------+---------------------+
3 rows in set (0.00 sec)