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

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

確認した環境

  • Cloud9 (PHP5.5.9)
  • Laravel 5.2

テーブル構成を定義する

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

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

$ php artisan make:migration create_posts_table

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

Created Migration: 2016_11_17_011700_create_posts_table

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

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

$ ls -la database/migrations
total 28
drwxr-xr-x 2 ubuntu ubuntu 4096 Nov 19 01:06 ./
drwxr-xr-x 5 ubuntu ubuntu 4096 Apr 27  2016 ../
-rw-r--r-- 1 ubuntu ubuntu    1 Apr 27  2016 .gitkeep
-rw-r--r-- 1 ubuntu ubuntu  699 Apr 27  2016 2014_10_12_000000_create_users_table.php
-rw-r--r-- 1 ubuntu ubuntu  633 Apr 27  2016 2014_10_12_100000_create_password_resets_table.php
-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)なのかは明確にしておいたほうが良いので、上記の表のようにいたしました。

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

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

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

<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        //
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        //
    }
}

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

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->increments('id');
        $table->string('post_body');
        $table->timestamps();
    });
}
public function down()
{
    Schema::drop('posts');
}

|コマンド|説明|
|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が更新されます。

$ 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で単数系なのに注意です。

$ php artisan make:model Post

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

Model created successfully.

Modelは、appディレクトリ直下にファイルが生成されます。

生成直後のファイルを開くとこうなっています。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    //
}

私は、app直下にファイルが増えるのが好まないため、Modelディレクトリを追加しています。

上記のファイルに、名前空間の移動と、テーブル名の定義を追加します。

<?php
namespace App\Model;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $table = 'posts';
}

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

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

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

初期データを用意する

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

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

$ php artisan make:seeder UsersTableSeeder

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

Seeder created successfully.

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

<?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //
    }
}

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

<?php

use Hash;
use Illuminate\Database\Seeder;
use App\Model\User;

class UsersTableSeeder extends Seeder
{
    public function run()
    {
        UserModel::create([
            'account'  => 'atuweb',
            'password' => Hash::make('password'),
        ]);
    }
}

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

<?php

use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        Model::unguard();

        $this->call('UsersTableSeeder');
        $this->call('PostsTableSeeder');

        Model::reguard();
    }
}

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

$ 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をチェックすることができれば、環境ごとに実行ファイルを切り分けることができると思ったのですが、今の所うまくできずに調べ中です。
(やり方をご存知の方教えてください)

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

$ php artisan db:seed --class=UserTableSeeder

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

開発初期

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

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

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

$ php artisan migrate:reset

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

$ php artisan migrate:reset
$ php artisan migrate
$ php artisan db:seed

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

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

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

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

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

開発後期、運用段階

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

dinoteの追加

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

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

{
    :
    "require": {
        "php": ">=5.5.9",
        "laravel/framework": "5.2.*",
        "doctrine/dbal": "2.*"
    },

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

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

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

$ php artisan make:migration add_title_and_tag_posts_table

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

<?php

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

class AddTitleAndTagPostsTable extends Migration
{
    public function up()
    {
        Schema::table('posts', function ($table) {
            $table->string('title')->after('id');
            $table->string('tags')->after('post_body');
        });
    }
    :

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

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

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

コマンドは覚えなくても

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

$ php artisan

Laravel Framework version 5.2.45

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under.
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  clear-compiled      Remove the compiled class file
  down                Put the application into maintenance mode
  env                 Display the current framework environment
  help                Displays help for a command
  list                Lists commands
  migrate             Run the database migrations
  optimize            Optimize the framework for better performance
  serve               Serve the application on the PHP development server
  tinker              Interact with your application
  up                  Bring the application out of maintenance mode
 app
  app:name            Set the application namespace
 auth
  auth:clear-resets   Flush expired password reset tokens
 cache
  cache:clear         Flush the application cache
  cache:table         Create a migration for the cache database table
 config
  config:cache        Create a cache file for faster configuration loading
  config:clear        Remove the configuration cache file
 db
  db:seed             Seed the database with records
 event
  event:generate      Generate the missing events and listeners based on registration
 key
  key:generate        Set the application key
 make
  make:auth           Scaffold basic login and registration views and routes
  make:console        Create a new Artisan command
  make:controller     Create a new controller class
  make:event          Create a new event class
  make:job            Create a new job class
  make:listener       Create a new event listener class
  make:middleware     Create a new middleware class
  make:migration      Create a new migration file
  make:model          Create a new Eloquent model class
  make:policy         Create a new policy class
  make:provider       Create a new service provider class
  make:request        Create a new form request class
  make:seeder         Create a new seeder class
  make:test           Create a new test class
 migrate
  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 the status of each migration
 queue
  queue:failed        List all of the failed queue jobs
  queue:failed-table  Create a migration for the failed queue jobs database table
  queue:flush         Flush all of the failed queue jobs
  queue:forget        Delete a failed queue job
  queue:listen        Listen to a given queue
  queue:restart       Restart queue worker daemons after their current job
  queue:retry         Retry a failed queue job
  queue:table         Create a migration for the queue jobs database table
  queue:work          Process the next job on a queue
 route
  route:cache         Create a route cache file for faster route registration
  route:clear         Remove the route cache file
  route:list          List all registered routes
 schedule
  schedule:run        Run the scheduled commands
 session
  session:table       Create a migration for the session database table
 vendor
  vendor:publish      Publish any publishable assets from vendor packages
 view
  view:clear          Clear all compiled view files

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

おわりに

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

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

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

この記事はtomita@atuwebがお届けしました。



Laravel: Up and Running: a Framework for Building Modern Php Apps 2016/11/28発売予定 (英語)

スポンサーリンク
ad_336
ad_336
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存
スポンサーリンク
ad_336
コメントの入力は終了しました。