laravel-migrations

I used to look at Laravel’s migrations as a shortcut to creating tables quickly without having to write out SQL. In a bigger environment, I saw the usage of it as a way of “versioning” the database schema. Yet, I found another great usage for it especially along with seeds.

First of all, how are you supposed to use migrations?

“Versioning”

Migrations are great because they allow you to track changes to how your database is set up, step by step, and update or rollback those changes. For example, if you add a new table, that’s a migration. If you drop a table, that’s a rollback on a migration. If you want to add a new column to a table, you can easily setup a migration for that, as well as a rollback to delete that extra column.

Here’s what a migration looks like:

<?php

use IlluminateDatabaseMigrationsMigration;

class CreateUserTable extends Migration {

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

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

}

The “up()” function specifies what to do when you enact your migration (via command line and artisan) and the “down()” function tells Laravel how to rollback. The migration features go further, you can:

  • create indices
  • create foreign keys
  • add/remove/rename columns

On top of that, you can pretty much specify whatever you want for the migration to do, including moving data, and running raw queries imp source.

What’s great with the “down()” is the reversal of whatever you just did. Okay so what?

Well, on big systems, you can launch features and have a reversal ready right away. Similar to how you can undo a commit on a git repository and deploy.

Seeding

Alright, so seeding is exactly what is sounds like. You add data to your tables. Yes, you can do it via migrations but that’s a bad practice since that would interfere with your production data. Imagine adding sample data to your system every time you run a migration, not a good practice at all. So this is where seeding comes in. Seeding is actually meant more for the testing environment.

Instead of sampling live data, you can use Seeds to work. You can seed every table with as much data as you want. Laravel will take the seeds, place them in your database so you’re ready to go (seeing is done via commandline and a file). Here’s what a seeding file looks like:

<?php

class UserTableSeeder extends Seeder {

    public function run()
    {
        DB::table('users')->delete();
        $user = new User;

        $user->fill(array(
            'email'    => 'antonin@antjanus.com',
            'username' => 'antjanus',
            'name'     => 'Antonin Januska'
            ));

        $user->password = Hash::make('admin');

        $user->save();
    }

}

Phew, so, let’s go over this real quick. This is my User Table Seeder. First, notice that the first thing my seeder does is delete all existing data. This is a good thing but not a necessary thing. The reason I do this is because every time I run the seeder, I would duplicate my data, effectively. Why would I run the seeder multiple times? Because every time I make a migration, I want ready testing data. So yeah, delete all rows in a table. I use Laravel’s Eloquent ORM to create an object based on my User model, use mass assignment (don’t forget to enable it!), and save it to the database.

users

Every time I run my seeder, this is what I run:

<?php

class DatabaseSeeder extends Seeder {

    public function run()
    {
        $this->call('UserTableSeeder');
        $this->command->info('User table seeded!');
        $this->call('LinkTableSeeder');
        $this->command->info('Link table seeded!');
        $this->call('PostTableSeeder');
        $this->command->info('Post table seeded!');
    }

}

Check this out. When I run the seeder via command line, I get feedback when seeding is done. This is the DatabaseSeeder file which gets run. You can skip out on seeds and add new seeds as makes sense. For example, if you just dropped a table, no reason to keep seeding it (you’d get an error) so you delete the line but can still keep the original seeder file in case you want to rollback your migration (and add the table back).

migrate-and-seed

This is an example of when I ran two migrations for my project: links and posts. Then I seeded my tables.

Where does that lead us?

The ultimate testing environment, reproducible on different platforms. If you ever clone a repo of mine that features Laravel with migrations or seeds, there’s no reason for you to download an SQL file or run some weird script. Everything’s built in. With a few commands, you can:

  • run all migrations thus creating all tables, indices, foreign keys, and everything else. 
  • run all seeders and populate all your tables thus be supplied with ready testing data

No more need for creating “testing posts” for your blog, running sql queries to fill your db with random data. You have a unified environment. What’s even better? You can play around with the database, add data, use your stuff and then RESET.

Yep, once you’re done playing around, just rollback your migrations, and run them again. And you’re ready with a fresh new database. Run the seeder again, and you’re ready to keep working.

This means that you environment is pretty much indestructible. You’re not tied to a production database, you’re not stuck downloading/uploading large SQL files or copies of your production database. You’re basically “stuck” running two commands and letting Laravel do everything. Wait, wait, let’s make it simpler:

php artisan migrate:refresh --seed

This little command rolls back ALL of your migrations, migrates them again AND seeds your database. It’s the ultimate “refresh” ;D

I’ve been playing around with this for a while 🙂

What about other Frameworks?

Of course, this is not unique to Laravel. I first encountered this with Symfony which also has migrations and has “fixtures” which are basically “seeds”.

So…how do you do this?

It’s simple. Laravel’s documentation’s pretty good at explaining everything but here’s the quick overview. Don’t forget the requirements:

  • command line access to Laravel
  • PHP access via command line

Creating A Migration.

The first step is creating a migration. All this requires is a single command for Artisan (the command line tool for Laravel):

php artisan migrate:make create_your_table

This will create a file in your app/database/migrations folder with a time stamp and named “create_your_table”, you may want to be more semantic than that in your project. Open your file up and fill it:

<?php

use IlluminateDatabaseMigrationsMigration;

class CreateUserTable extends Migration {

    public function up()
    {
        Schema::create('users', function($table)
        {
            $table->increments('id');
            $table->string('email')->unique();
            $table->string('username');
            $table->string('name');
            $table->string('password');
            $table->timestamps();
        });
    }

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

}

Go ahead and read over the Schema documentation on Laravel to get a better idea of the versatility you get with migrations. Great! Notice that I used my previous “user” example named CreateUserTable (create_user_table in the command line).

Migrate

Okay, time to migrate your data.

php artisan migrate

You should get either a success or error message in regard to your migration. If this was your first migration, Laravel will also create a “migrations” table that details the names and batches of migrations run. In case you create 5 different migrations and migrate them at once, they’ll be grouped in a “batch” and thus will be rolled back together. This is useful if your migrations depend on each other for functionality.

migrations

Create A Seeder

For seeding, you’ll have to create your own file like so:

<?php

class UserTableSeeder extends Seeder {

    public function run()
    {
        DB::table('users')->delete();
        $user = new User;

        $user->fill(array(
            'email'    => 'antonin@antjanus.com',
            'username' => 'antjanus',
            'name'     => 'Antonin Januska'
            ));

        $user->password = Hash::make('admin');

        $user->save();
    }

}

Again, make sure the class name makes sense. Also, make sure you already have a model built! Save this file in the database/seeds folder. Next, add a reference to this class in the DatabaseSeeder:

<?php

class DatabaseSeeder extends Seeder {

    public function run()
    {
        $this->call('UserTableSeeder');
        $this->command->info('User table seeded!');
    }

}

Make sure you add that little bit to let you know if the database has been seeded. It’s nice feedback info.

Seed!

Last thing, run your seed command!

php artisan db:seed

Done! 🙂