If you are thinking to yourself, “What Are Migrations” – this tutorial is for you. When talking about migrations in the context of Laravel, we are referring to a way to alter and update the database in a structured way. The most common database to use with PHP is MySQL, and many times, it is easy enough to create your tables manually using SQL statements. In this way, you can log in to MySQL manually, and run any of the various data definition language statements to create and design databases and tables. In the case of a content management system like WordPress, the system just runs a script and builds out all of the tables in one shot. With a dedicated framework like Laravel, developers create highly customized applications from scratch, that require a flexible way to build out the database over time. Migrations provide this and work like dedicated version control, which keeps track of all changes and updates to the database over time.
Starting With A Blank Slate
Here we have a database with no tables yet. We’re using the popular phpMyAdmin tool to view the database in a graphical way.
It would be easy enough to create tables right here in the graphical user interface. A drawback however is that we lose that feature of version control that is a part of migrations. In addition, with custom applications, database tables may change over time. Indeed the entire schema is subject to change, so this is why we need the flexibility of migrations. Laravel provides a nice implementation of migrations that we’ll take a look at now.
Laravel Built In Migrations
Laravel ships with two migrations already created for you. Each is a PHP file which makes use of a PHP class to create a database table and its associated fields or columns. One migration creates a users table, and the other creates a password_resets table. Let’s take a look at them now.
create_users_table.php source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php 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')->unique(); $table->string('password', 60); $table->rememberToken(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('users'); } } |
create_password_resets_table.php source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePasswordResetsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('password_resets', function(Blueprint $table) { $table->string('email')->index(); $table->string('token')->index(); $table->timestamp('created_at'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('password_resets'); } } |
Great. What Do These Files Do?
By inspecting the migration files, we can see that each has a method named up()
and down()
. up()
is always associated with making new tables or fields in the database. Usually, every up()
will also have a corresponding down()
. The down()
method is there to undo, or rollback, whatever the up()
method does. This is for the purpose of being able to get a second shot at creating the schema of your database. If you make a mistake, you can rollback the migration, make any updates you need like a column re name or some other detail, then re run the migration.
Migrate The Database
Database migrations must be run in order to actually affect the database. With the default Laravel install, we do have these migration files, but they have not yet been run. We know this because when we connected to the database in question with phpMyAdmin, we did in fact have a blank database named homestead. Before you try to run migration files, it is important to make sure you have your database credentials configured in the Laravel environment. For this example we are using the default of a homestead
database name, homestead
user name, and a password of secret
. In addition, the database itself must already be present in MySQL, which in this case it is as homestead
.
$
vagrant ssh
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-30-generic x86_64)
* Documentation: https://help.ubuntu.com/
System information as of Mon Mar 2 15:20:21 UTC 2015
System load: 0.97 Processes: 99
Usage of /: 5.0% of 39.34GB Users logged in: 0
Memory usage: 33% IP address for eth0: 10.0.2.15
Swap usage: 0% IP address for eth1: 192.168.33.10
Graph this data and manage this system at:
https://landscape.canonical.com/
Get cloud support with Ubuntu Advantage Cloud Guest:
http://www.ubuntu.com/business/services/cloud
Last login: Fri Dec 19 15:01:15 2014 from 10.0.2.2
vagrant@homestead:~$ cd Code
vagrant@homestead:~/Code$ cd Laravel
vagrant@homestead:~/Code/Laravel$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
vagrant@homestead:~/Code/Laravel$
In this snippet above, we have now run the migrations with the command php artisan migrate
. Let’s inspect the database now and see what we get.
Success! As a result of actually running the migrations, we now have three new tables in our homestead database. We of course have the users
and password_resets
tables now created, and we also have a third table of migrations
. This table keeps track of when migrations are run, and is what gives us the version control aspect of using migrations. Let’s take a look at the structure of the users
and password_resets
tables in gui form to see how the PHP migration files above translate to actual tables in the database.
users
Table
password_resets
Table
Fantastic! We can see our tables in all of their glory here. They are empty at the moment, but we can put them to use right away. You may have noticed that Laravel ships with a user authentication system implemented. Simply visit the /home route, and you will be given a chance to register as a user.
Trying To Log In Without Registering
Register A New User
User Can Access Application
Very Nice! With this boilerplate provided for us, we can see how an authentication system works with Laravel. Now that we have actually registered a user, and logged into the application, we can examine the database. Our database tables and fields that were created as a result of running the migrations earlier should now have some data within them. As we can see below, we do in fact have a new entry in the users table for the user we registered.
Migrations With Laravel Artisan
The built in artisan tool of Laravel has many ways to work with migrations. With our new Linux Piping skills, lets find out what commands are available to us.
php artisan | grep migration
1 2 3 4 5 6 7 8 9 10 11 |
migrate Run the database migrations cache:table Create a migration for the cache database table make:migration Create a new migration file migrate:install Create the migration repository migrate:refresh Reset and re-run all migrations migrate:reset Rollback all database migrations migrate:rollback Rollback the last database migration migrate:status Show a list of migrations up/down queue:failed-table Create a migration for the failed queue jobs database table queue:table Create a migration for the queue jobs database table session:table Create a migration for the session database table |
Now that we can see the commands available to us with relation to migrations, lets try some out. The first one that looks interesting is migrate:status, lets try it out.
vagrant@homestead:~/Code/Laravel$
php artisan migrate:status
1 2 3 4 5 6 |
+------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2014_10_12_000000_create_users_table | | Y | 2014_10_12_100000_create_password_resets_table | +------+------------------------------------------------+ |
This gives us a summary of what we have done with migrations so far in our application. Let’s try another one.
php artisan migrate:rollback
Rolled back: 2014_10_12_100000_create_password_resets_table
Rolled back: 2014_10_12_000000_create_users_table
vagrant@homestead:~/Code/Laravel$
php artisan migrate:status
1 2 3 4 5 6 |
+------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | N | 2014_10_12_000000_create_users_table | | N | 2014_10_12_100000_create_password_resets_table | +------+------------------------------------------------+ |
We just did a rollback on the last database migration. This means that whatever as done the last time we ran php artisan migrate, is now undone. Well the last time we ran a migration, we created two tables and several associated fields. In addition to this we registered a user with the application. Well with this rollback, those tables, and any data in them are now history. What if we had registered 100 users with the application so far. Those users will also be lost. The takeaway is to know why you are doing a rollback before you actually do it. Now check it out. This give us a good chance to show the concept of getting a second shot at our table schema. Imagine you also want the user to have a nick name associated with their account. We can modify the migration file, then run migrations again.
Update The Migration File
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?php 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('nick_name'); $table->string('email')->unique(); $table->string('password', 60); $table->rememberToken(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('users'); } } |
Run Migrations
php artisan migrate
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
vagrant@homestead:~/Code/Laravel$
php artisan migrate:status
1 2 3 4 5 6 |
+------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2014_10_12_000000_create_users_table | | Y | 2014_10_12_100000_create_password_resets_table | +------+------------------------------------------------+ |
Observe New Schema
Create New Migration Files
In looking at the prior snippets of the PHP that actually creates a migration file, you may have been thinking, “Jeez, that’s a lot of code to write just to create a database table.” Fear not, as we will use automatic code generation by way of the artisan tool to create the files for us. Let’s imagine we want to create a table to hold bookmarks. Here is how we might do that.
php artisan make:migration create_bookmarks_table --create="bookmarks"
Created Migration: 2015_03_02_184309_create_bookmarks_table
It’s worth discussing the format of this command. php artisan make:migration specifies that we want to create a new migration file. The very next part is the name argument. This is a required argument to the make:migration command, and the convention is to choose a name that describes what this migration file does. In our case, we are creating a migration file that is going to create a bookmarks table. Therefore, create_bookmarks_table is a nice descriptive name of what this migration will do. The next option of –create=”bookmarks” instructs Laravel that we are creating a table with the name of bookmarks
. Let’s look at what has been generated for us.
create_bookmarks_table.php source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateBookmarksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('bookmarks', function(Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('bookmarks'); } } |
Excellent. Laravel has provided some nice boilerplate for us to get us started. In the up() method, we see that we already have two methods to create an auto increment id field, and a timestamps field. At this point, we need to manually add the fields that we might use for holding bookmarks. We’ll need a field to hold the url, a title, and a description. Let’s add those now.
create_bookmarks_table.php source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateBookmarksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('bookmarks', function (Blueprint $table) { $table->increments('id'); $table->string('url'); $table->string('title'); $table->text('description'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('bookmarks'); } } |
With that update, lets run the migrations and see what we get.
vagrant@homestead:~/Code/Laravel$
php artisan migrate
Migrated: 2015_03_02_184309_create_bookmarks_table
vagrant@homestead:~/Code/Laravel$
php artisan migrate:status
1 2 3 4 5 6 7 |
+------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2014_10_12_000000_create_users_table | | Y | 2014_10_12_100000_create_password_resets_table | | Y | 2015_03_02_184309_create_bookmarks_table | +------+------------------------------------------------+ |
Migrations For Tables You Can’t Rollback
The last thing we’ll talk about in this episode is dealing with making adjustments to tables when you can no longer rollback. When might this happen? Well, consider the example earlier where we rolled back the users table. When we did that, we lost all users that where in the table. In our case, that was only the one example user we had registered. What if we already had an application in development however, and there were hundreds or thousands of user record in that table? You don’t want to really rollback at that point, since all data contained is lost. In a case like that, you can create a new migration file for the same table. This way, you can alter a table without blowing it away entirely first.
php artisan make:migration add_tags_to_bookmarks_table --table="bookmarks"
Created Migration: 2015_03_02_195049_add_tags_to_bookmarks_table
We make the adjustment of adding our column of tags in the up() method, and the ability to undo this column with dropColumn() in the down() method. We can now run the migration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddTagsToBookmarksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('bookmarks', function(Blueprint $table) { $table->string('tags'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('bookmarks', function(Blueprint $table) { $table->dropColumn('tags'); }); } } |
php artisan migrate
Migrated: 2015_03_02_195049_add_tags_to_bookmarks_table
vagrant@homestead:~/Code/Laravel$
php artisan migrate:status
1 2 3 4 5 6 7 8 |
+------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2014_10_12_000000_create_users_table | | Y | 2014_10_12_100000_create_password_resets_table | | Y | 2015_03_02_184309_create_bookmarks_table | | Y | 2015_03_02_195049_add_tags_to_bookmarks_table | +------+------------------------------------------------+ |
Like magic, we have the new column in our database now.
What Are Migrations In Laravel Conclusion
In this episode we had a good look at what migrations are in Laravel, and why it is beneficial to use them. Admittedly, when first starting to use migrations, they can seem like additional work. Once you’re up to speed with them however, they really are a great way to implement version control for your database and will actually speed up your workflow in the long run.