Quantcast
Channel: Laravel – Code Chewing
Viewing all articles
Browse latest Browse all 6

How to setup table schema with Laravel migration

$
0
0

A great feature of Laravel is the ability to create table schemas through their migration mechanism, which acts like version control for your database. It provides an audit trail, and an opportunity to use their rollback system should you ever need to.

I found the whole process a little odd to begin with, so I thought I’d share a start to finish guide.

This article is based on Laravel 5. The following may or may not vary across versions.

For this example, we’re going to create three tables. The tables will hold a list of toys, a list of owners, and a table to for the one to many relationship between owner and their toys. This will provide us with the opportunity to use some varied schema structures, such as foreign key constraints and composite keys.

Toys table (toy):

+---------+----------+-----------+
|   id    |   type   |  colour   |
+---------+----------+-----------+
|   1     |   plane  |   blue    |
|   2     |   train  |   red     |
|   3     |   truck  |   orange  |
+---------+----------+-----------+

Owner table (owner):

+---------+----------+
|   id    |   name   |
+---------+----------+
|   1     |   Peter  |
|   2     |   Julie  |
|   3     |   Wendy  |
+---------+----------+

Owner toy relationship table (owner_toy_rel):

+---------+----------+
| ownerId |  toyId   |
+---------+----------+
|   1     |    1     |
|   1     |    3     |
|   2     |    2     |
|   3     |    1     |
+---------+----------+

Creating table schemas

In order to create the above schemas, we’re going to do it the idiomatic way in Laravel, via database migrations. To do so, we use Laravel’s schema builder along with some Artisan commands.

Run the following commands from the command line within your Laravel app directory:

php artisan make:migration create_toy_table --create=toy
php artisan make:migration create_owner_table --create=owner
php artisan make:migration create_owner_toy_rel_table --create=owner_toy_rel

After executing the above lines, take a look inside of app/database/migrations/ and you’ll see three files that have been automatically generated:

  • 2016_05_08_181517_create_toy_table.php
  • 2016_05_08_181517_create_owner_table.php
  • 2016_05_08_181517_create_owner_toy_rel_table.php

These files have been created for you, with a boilerplate structure already in place. By specifying the argument --create on the command line with a table name, means that the boilerplate file includes a ready made function for us to create the schema for the table. This is a worthwhile convenience.

Here’s what the guts of 2016_05_08_181517_create_toy_table.php looks like:

<?php

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

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

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

It’s important to know that the timestamp dates in the file name correspond to the order in which they’ll be run. In other words, when we perform the actual migration (aka table creation), the files will be executed in chronological date order.

Notice that the toy table schema has been automatically given a column with a name of id. Laravel appears to implement the surrogate key convention as default. The increments method creates a primary key of type integer, unsigned.

It also provides you with the timestamps method, which will create the columns created_at and updated_at.

Let’s modify the toy schema file (2016_05_08_181517_create_toy_table.php), so that it meets our requirements:

<?php

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

class CreateToyTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('toy', function (Blueprint $table) {
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('type');
            $table->string('colour');
        });
    }

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

We’ve chosen to use the InnoDB engine, as we’re going to need to support foreign key constraints. The rest is pretty much self explanatory.

The down method that appears in all the schema boilerplate classes essentially reverse the migration. So in this case, it simply drops the table.

Now let’s modify the owner table schema (2016_05_08_181517_create_owner_table.php) so that it meets our requirements:

<?php

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

class CreateOwnerTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('owner', function (Blueprint $table) {
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
        });
    }

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

And finally the owner toy relationship/mapping table file (2016_05_08_181517_create_owner_toy_rel_table.php):

<?php

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

class CreateOwnerToyRelTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('owner_toy_rel', function (Blueprint $table) {
            $table->engine = 'InnoDB';
            $table->integer('ownerId')->unsigned();
            $table->integer('toyId')->unsigned();
            $table->primary(['ownerId', 'toyId']);
        });
    }

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

Two things in particular to bring attention to here:

  1. We’ve ensured that the columns ownerId and toyId are of the same type as the table columns they reference. That is, unsigned integers. We can chain on the unsigned method as you can see above.
  2. Secondly, we’ve created a composite key with the primary method based on both the columns in this table. This means that our relationship (a one to many relationship), that an owner can have many toys, is enforced by the database, so that there are no duplicate entries. GIGO.

Alter tables to include foreign key constraints

Now that our table creation schemas are complete, we can setup our foreign key constraints. It is important that you create the tables that reference foreign key constraints, before applying foreign key constraints on those tables! If you don’t, you’ll hit conflict errors.

The tables toy and owner are independent of one another, but the table owner_toy_rel isn’t. This latter table contains two foreign keys: the owner id and the toy id.

Let’s ensure our database enforces this relationship, so that entries can’t be added unless they reference active toy or owner IDs. Again, keeping garbage out of the database, and also leveraging the powerful cascading functionality.

Go back to the command line, and ensure you’re in your Laravel app directory, and type the following command:

php artisan make:migration alter_owner_toy_rel_table --table=owner_toy_rel

Once again, this creates a file within app/database/migrations/ with the latest timestamp. It’ll be named something like 2016_05_08_183219_alter_owner_toy_rel_table.php.

We passed a command line argument and value of --table=owner_toy_rel, which again is a convenience method so that the template file is generated with more useful data, so that we can immediately get going.

Here’s the guts of the our alter table boilerplate upon automatic generation:

<?php

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

class AlterOwnerToyRelTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('owner_toy_rel', function (Blueprint $table) {
            //
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('owner_toy_rel', function (Blueprint $table) {
            //
        });
    }
}

Instead of Schema::create which we saw in the table creation classes, we’re instead kick started with Schema::table, as we know (because of the order of migration) that the table already exists.

Let’s modify this so that is represents the foreign key constraints we require:

<?php

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

class AlterOwnerToyRelTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('owner_toy_rel', function (Blueprint $table) {
            $table->foreign('ownerId')
                  ->references('id')
                  ->on('owner')
                  ->onUpdate('cascade')
                  ->onDelete('cascade');

            $table->foreign('toyId')
                  ->references('id')
                  ->on('toy')
                  ->onUpdate('cascade')
                  ->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('owner_toy_rel', function (Blueprint $table) {
            $table->dropForeign('owner_toy_rel_ownerId_foreign');
            $table->dropForeign('owner_toy_rel_toyId_foreign');
        });
    }
}

This should be mostly self explanatory if you’re familiar with foreign key constraints. The syntax from the schema builder is very expressive.

You can see that we’ve used the onUpdate and onDelete methods to keep the database up-to-date with any changes made to the referencing tables. If the referencing IDs update, they’ll be reflected here too. If the referencing IDs are deleted, all entries will be removed from here too.

Notice that the drop method from our migration class unassigns the foreign key constraints. Respectively, the order goes: tableName, _, columnName, _, then finally _foreign is tacked on.

Actually create the tables

Now that we’ve completely expressed in code how we’d like the schema to be, we can finally actually create the tables within the database.

Head back to the command line and type the execute the following command:

php artisan migrate

All being well, this will create your tables as specified.

If you inspect your database, there should now be a migrations table that lists the migration steps that have been executed. This is a way of keeping track of the migration steps.

If you run into any errors during migration, copy and paste the error into a search engine and you’ll normally stumble into some great stackoverflow articles to help you along. Such as database connection errors and stuff like that.

We’ve covered a lot of ground here, and you should now feel comfortable creating tables and schemas with the Laravel migration tools and conventions.


Viewing all articles
Browse latest Browse all 6

Latest Images

Trending Articles





Latest Images