マイグレーションはあったほうが当然良いツールなのですが、レガシーな現場には意外と現場に浸透していないと感じます。
運良く、Laravelのような若いフレームワークで開発する機会に恵まれたのならば、是非ともマイグレーションを使ってテーブル構成を管理していきたいものです。

ということで、Laraveのマイグレーションで覚えておいがほうが良さそうなことをまとめてみました。

確認した環境

  • Cloud9 (PHP5.5.9)
  • Laravel 5.2

テーブル構成を定義する

マイグレーションファイルを作成する

マイグレーションファイルを新規作成するにはプロジェクトルート以下で次のコマンドを実行します。

1$ php artisan make:migration create_posts_table

artisanがこんな返答を返してくれればコマンド実行OKです。

1Created Migration: 2016_11_17_011700_create_posts_table

ファイルは、プロジェクトルート以下のdatabase/migrationsに生成されます。
出来上がったファイルをチェックしてみましょう。

このディレクトリには、Laravelのプロジェクトをnewした際、すでに2つのマイグレーションファイルが生成されているので、こんな感じになると思います。

1$ ls -la database/migrations
2total 28
3drwxr-xr-x 2 ubuntu ubuntu 4096 Nov 19 01:06 ./
4drwxr-xr-x 5 ubuntu ubuntu 4096 Apr 27  2016 ../
5-rw-r--r-- 1 ubuntu ubuntu    1 Apr 27  2016 .gitkeep
6-rw-r--r-- 1 ubuntu ubuntu  699 Apr 27  2016 2014_10_12_000000_create_users_table.php
7-rw-r--r-- 1 ubuntu ubuntu  633 Apr 27  2016 2014_10_12_100000_create_password_resets_table.php
8-rw-r--r-- 1 ubuntu ubuntu  373 Nov 17 01:17 2016_11_17_011700_create_posts_table.php

マイグレーションファイルの命名ルール

上記の通り [日付]_[任意の名称].php というフォーマットです。

項目 フォーマット 説明
日付 YYYY_MM_DD_HHIISS 日付の小さいものから実行される
名称 [create/update]_[テーブル名]_table そのままクラス名に利用されます

名称は、実はなんでも良いのですが、マイグレーションの実行内容がわかる名前をつけたほうが、当然良いわけですね。

また、マイグレーションの中でクラス名の重複がある場合はPHPがエラーを吐いてしまいますから、それっぽい名前をつけておきましょう。
結局、運用段階でテーブル構成を変更する可能性を考慮すると、createなのかalter table(update)なのかは明確にしておいたほうが良いので、上記の表のようにいたしました。

マイグレーションファイルを編集する

先ほど作成したファイルを編集してスキーマを定義していきましょう。

これが作成した直後のマイグレーションファイルです。

 1<?php
 2
 3use Illuminate\Database\Schema\Blueprint;
 4use Illuminate\Database\Migrations\Migration;
 5
 6class CreatePostsTable extends Migration
 7{
 8    /**
 9     * Run the migrations.
10     *
11     * @return void
12     */
13    public function up()
14    {
15        //
16    }
17
18    /**
19     * Reverse the migrations.
20     *
21     * @return void
22     */
23    public function down()
24    {
25        //
26    }
27}
28

主キーと投稿本文、タイムスタンプのフィールドを追加します。

 1public function up()
 2{
 3    Schema::create('posts', function (Blueprint $table) {
 4        $table->increments('id');
 5        $table->string('post_body');
 6        $table->timestamps();
 7    });
 8}
 9public function down()
10{
11    Schema::drop('posts');
12}
13
コマンド 説明
increments int(10) unsignedでautoincrementなprimaryKeyフィールドを生成
string varchar(255)のフィールドを生成
timestamps timestamp型のフィールドcreated_at, updated_atを生成

Laravelでは常に主キーとして$table->increments('id');を定義しておくのが吉です。

また、timestampsを入れるとEloquent ORM側で自動更新してくれますから、こちらも常に定義しておくのが良いと思います。


利用可能なコマンドと、生成されるフィールドの対応は、私はいつも以下を確認しております。

Laravel 5.2 データベース:マイグレーション
https://readouble.com/laravel/5.2/ja/migrations.html

マイグレーションを実行する

マイグレーションファイルを用意したら、定義を実行してDBに反映していきましょう。 プロジェクトルートで以下を実行すると、DBが更新されます。

1$ php artisan migrate

マイグレーションを実行すると、デフォルトデータベースのmigrationsテーブルにデータが残ります。

とは言っても、テーブルの内容はいたってシンプルなものです。
SELECT * FROM migrationsすると以下のような結果が得られます。

id migration batch
1 2014_10_12_000000_create_users_table.php 1
2 2014_10_12_100000_create_password_resets_table.php 1
3 2016_11_17_011700_create_posts_table.php 1

Eloquent ORMのクラスを作成する

テーブルを作成したら、PHPから対象テーブルにアクセスするためのORMを作成しましょう。

次のコマンドを打って、空のクラスを生成します。
テーブル名はpostsで複数形だけれども、ORMはPostで単数系なのに注意です。

1$ php artisan make:model Model/Post

artisanより以下の応答があればファイルの作成は成功です。

1Model created successfully.

(2014年12月14日にこの辺りを修正) Modelを指定しないとappディレクトリ直下にファイルが生成されてしまい、「気がつけばapp以下に大量のファイルが、、、」状態になってしまうので注意です!

生成直後のモデルクラスはこんな感じです。

 1<?php
 2
 3namespace App\Model;
 4
 5use Illuminate\Database\Eloquent\Model;
 6
 7class Post extends Model
 8{
 9    //
10}
11

上記のファイルに、テーブル名の定義を追加します。

 1<?php
 2namespace App\Model;
 3
 4use Illuminate\Database\Eloquent\Model;
 5
 6class Post extends Model
 7{
 8    protected $table = 'posts';
 9}
10

ORMを作成すれば、以下のようにDBへの問い合わせができるようになります。

1$posts = App\Model\Post::all();
2

Eloquentを細かく見ていくと長くなります。とりあえずこれで。

なお、以下のコマンドでマイグレーションとモデルを同時に生成してくれます。

1$ php artisan make:model Model/Post -m

初期データを用意する

管理画面の初期アカウントやデバッグデータの準備に活躍するのがシーディングです。

次のartisanコマンドを実行して、空のシーダーを用意します。

1$ php artisan make:seeder UsersTableSeeder

コマンドを実行して、次のおとうが得られるとdatabase/seedsにファイルが生成されます。

1Seeder created successfully.

こちらが生成直後のファイルです。

 1<?php
 2
 3use Illuminate\Database\Seeder;
 4
 5class UsersTableSeeder extends Seeder
 6{
 7    /**
 8     * Run the database seeds.
 9     *
10     * @return void
11     */
12    public function run()
13    {
14        //
15    }
16}
17

シーダーの書き方はいろいろありますが、ここではORMを呼び出し、1レコードをCreateしててみましょう。

 1<?php
 2
 3use Hash;
 4use Illuminate\Database\Seeder;
 5use App\Model\User;
 6
 7class UsersTableSeeder extends Seeder
 8{
 9    public function run()
10    {
11        UserModel::create([
12            'account'  => 'atuweb',
13            'password' => Hash::make('password'),
14        ]);
15    }
16}
17

シーダーを作成したら、あらかじめ保存されているdatabase/seeds/DatabaseSeeder.phpに新しいシーダー呼び出しを追加します。

 1<?php
 2
 3use Illuminate\Database\Seeder;
 4use Illuminate\Database\Eloquent\Model;
 5
 6class DatabaseSeeder extends Seeder
 7{
 8    /**
 9     * Run the database seeds.
10     *
11     * @return void
12     */
13    public function run()
14    {
15        Model::unguard();
16
17        $this->call('UsersTableSeeder');
18        $this->call('PostsTableSeeder');
19
20        Model::reguard();
21    }
22}

最後にシーディングを実行すれば、データの登録は完了です。

1$ php artisan db:seed

シーダーを使えば、ダミーや検証用データの共有が楽にできますので、私は使い勝手が良いと感じました。

注意点として、シーダークラスはdatabaseディレクトリ以下に保存されますので、オートロードの対象外です。
そのため、ファイル追加後にcomposer dump-autoloadしてあげないと、シーダーから見つけることができずにClassNotFoundが発生することがあますよ。

Laravel 5.2 : シーディング
https://readouble.com/laravel/5.2/ja/seeding.html

課題:シーダーで環境を判別したい

例えば「公開環境用にはデバッグデータを含みたくない」など、環境によって登録したいデータには差があるものです。

シーダーの中でConfig::get()し、app.envをチェックすることができれば、環境ごとに実行ファイルを切り分けることができると思ったのですが、今の所うまくできずに調べ中です。
(やり方をご存知の方教えてください)

取り急ぎは、次のように、実行ファイルを指定することで対処しました。

1$ php artisan db:seed --class=UserTableSeeder

テーブルの構成を変更する

開発初期

開発初期は設計が不味かったり、仕様に調整が入ったりと、やはりテーブルの構成が変わってくるものです。

シーディングでデータの復活が容易な状況では、既存のマイグレーションファイルを更新してしまうのが手っ取り早いでしょう。

マイグレーションをゼロからやりなおすには以下のコマンドを実行します。

1$ php artisan migrate:reset

テーブルをリセットして、マイグレーション、シーディングをやり直すとこうですね。

1$ php artisan migrate:reset
2$ php artisan migrate
3$ php artisan db:seed

上記を一発で実行する便利コマンドが以下です。

1$ php artisan migrate:refresh --seed
リセットの注意点

マイグレーションのリセットは、ファイルの増減前に実行しましょう。

というのは、database/migrations以下のファイル名と、migrationsテーブルに保存されている内容で、ファイルの増減やファイル名が異なっているなど、何らかの差があると、リセットが失敗してしまうためです。
マイグレーションをリセットresetする際にファイルが増減していたり、フィル名が異なっている場合はエラーが発生してしまうので注意です。

収拾不可能な状態に陥った場合は、データベース内の全てのテーブルをdropするという荒治療を行わなければなりません。

開発後期、運用段階

マイグレーションのリセットが難しくなってきましたら、既存のテーブルをdropしたい気持ちを抑えてマイグレーションファイルを追加していく段階に入ります。
ここで、ライブラリの追加が必要です。

dinoteの追加

既存のテーブル変更を入れる場合はdinoteに依存しております。
dinoteがインストールされていない場合はエラーとなりインストールが促されますよ。

プロジェクトルートのcomposer.jsonを編集して、dinoteを追加しましょう。

1{
2    :
3    "require": {
4        "php": ">=5.5.9",
5        "laravel/framework": "5.2.*",
6        "doctrine/dbal": "2.*"
7    },

ファイルを編集後、お決まりのコマンドでライブラリの更新を実行しましょう。

1$ composer update
マイグレーションファイルの追加

先程と同様にコマンドを打つなりしてマイグレーションファイルを作成します。

1$ php artisan make:migration add_title_and_tag_posts_table

多少ファイル名が長くても、何度かテーブル構成を変更することを考えると、「どういった変更を行うか」がわかる名称が良さそうです。

 1<?php
 2
 3use Illuminate\Support\Facades\Schema;
 4use Illuminate\Database\Schema\Blueprint;
 5use Illuminate\Database\Migrations\Migration;
 6
 7class AddTitleAndTagPostsTable extends Migration
 8{
 9    public function up()
10    {
11        Schema::table('posts', function ($table) {
12            $table->string('title')->after('id');
13            $table->string('tags')->after('post_body');
14        });
15    }
16}

フィールドの追加は、テーブルの新規作成時と同様でOKです。
単純に追加すると、一番下にフィールドが追加されてしまうため、after()などを利用してフィールドの位置を指定すると美しいですね。

ファイル作成後にphp artisan migrateを実行するのは今までと同じです。

migrationsテーブルにはbatch=2のレコードが追加されていることが確認いただけると思います。

コマンドは覚えなくても

色々とartisanコマンドをご紹介いたしましたが、これらを覚える必要は全くありませんよ。
それは、引数なしでartisanコマンドを打てば、manが表示されるためです。

 1$ php artisan
 2
 3Laravel Framework version 5.2.45
 4
 5Usage:
 6  command [options] [arguments]
 7
 8Options:
 9  -h, --help            Display this help message
10  -q, --quiet           Do not output any message
11  -V, --version         Display this application version
12      --ansi            Force ANSI output
13      --no-ansi         Disable ANSI output
14  -n, --no-interaction  Do not ask any interactive question
15      --env[=ENV]       The environment the command should run under.
16  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
17
18Available commands:
19  clear-compiled      Remove the compiled class file
20  down                Put the application into maintenance mode
21  env                 Display the current framework environment
22  help                Displays help for a command
23  list                Lists commands
24  migrate             Run the database migrations
25  optimize            Optimize the framework for better performance
26  serve               Serve the application on the PHP development server
27  tinker              Interact with your application
28  up                  Bring the application out of maintenance mode
29 app
30  app:name            Set the application namespace
31 auth
32  auth:clear-resets   Flush expired password reset tokens
33 cache
34  cache:clear         Flush the application cache
35  cache:table         Create a migration for the cache database table
36 config
37  config:cache        Create a cache file for faster configuration loading
38  config:clear        Remove the configuration cache file
39 db
40  db:seed             Seed the database with records
41 event
42  event:generate      Generate the missing events and listeners based on registration
43 key
44  key:generate        Set the application key
45 make
46  make:auth           Scaffold basic login and registration views and routes
47  make:console        Create a new Artisan command
48  make:controller     Create a new controller class
49  make:event          Create a new event class
50  make:job            Create a new job class
51  make:listener       Create a new event listener class
52  make:middleware     Create a new middleware class
53  make:migration      Create a new migration file
54  make:model          Create a new Eloquent model class
55  make:policy         Create a new policy class
56  make:provider       Create a new service provider class
57  make:request        Create a new form request class
58  make:seeder         Create a new seeder class
59  make:test           Create a new test class
60 migrate
61  migrate:install     Create the migration repository
62  migrate:refresh     Reset and re-run all migrations
63  migrate:reset       Rollback all database migrations
64  migrate:rollback    Rollback the last database migration
65  migrate:status      Show the status of each migration
66 queue
67  queue:failed        List all of the failed queue jobs
68  queue:failed-table  Create a migration for the failed queue jobs database table
69  queue:flush         Flush all of the failed queue jobs
70  queue:forget        Delete a failed queue job
71  queue:listen        Listen to a given queue
72  queue:restart       Restart queue worker daemons after their current job
73  queue:retry         Retry a failed queue job
74  queue:table         Create a migration for the queue jobs database table
75  queue:work          Process the next job on a queue
76 route
77  route:cache         Create a route cache file for faster route registration
78  route:clear         Remove the route cache file
79  route:list          List all registered routes
80 schedule
81  schedule:run        Run the scheduled commands
82 session
83  session:table       Create a migration for the session database table
84 vendor
85  vendor:publish      Publish any publishable assets from vendor packages
86 view
87  view:clear          Clear all compiled view files

とりあえず引数なしで打ってみる。これ大切です。

おわりに

細かいことはたくさんありますが、この辺りまで対応できるようになると、困ることはぐっと減ると思います。

また、マイグレーションといってもライブラリによって思想、実装に差があると感じます。
まあ「テーブル構成をファイルをで管理して環境差異をなくそう 」と、ざっくり考えていただければいいのかなと考えています。

大切なことは、 コードで管理するという思想 です。 プロジェクトに関わるすべてのものをコードで管理しましょう!

PHPフレームワーク Laravel Webアプリケーション開発 バージョン5.5 LTS対応

竹澤 有貴,栗生 和明,新原 雅司,大村 創太郎
出版社:ソシム  発売日:2018-09-26

Amazonで詳細を見る

PHPフレームワーク Laravel入門

掌田津耶乃
出版社:秀和システム  発売日:2017-09-16

Amazonで詳細を見る

初めてのPHP

David Sklar
出版社:オライリージャパン  発売日:2017-03-18

Amazonで詳細を見る