開発におけるデータベースに関する悩み
Githubからプルしたら、ある画面が動かない。
結合フェーズに入ったら、システムが動かない。
原因はテーブルのスキーマがいつの間にか変わっていた…
なんてことはありませんか?
Laravelにおいてはマイグレーションとシーダーを使えばこのような問題を解決できます!
マイグレーションのメリット
Laravel(をはじめとするPHPフレームワーク)にはマイグレーションという機能があり、プログラムでデータベースのスキーマを管理できます。
プログラムなのでバージョン管理が可能なので、テーブル設計を変更の影響を最小限にしたり、開発メンバー間での共有もテーブル変更をマイグレーションファイルに記載したら、GitHubにコミットし、メンバーにプルしてもらい、マイグレーションを実行してもらうだけで、テーブルを共通化することが可能です。
マイグレーションとモデルの密接な関係
Laravelにおいてモデルはテーブルに1対1で関連づきます。
マイグレーションファイルと同時に作るほうが便利です。
シーダー
初期データ挿入の際にはシーダーを利用します。
こちらもマイグレーションと同様にプログラム化できるので、GitHubなどでデータ共有を楽にすることが可能です。
マイグレーションファイルとモデルファイルを自動生成
モデルファイルを自動生成する際に、オプションで"-m"をつけると、マイグレーションファイルも生成されます。
$ php artisan make:model {モデル名} -m
$ php artisan make:model round -m
Model created successfully.
Created Migration: 2017_12_12_231913_create_rounds_table
上記コマンドを実行すると"/database/migrations"フォルダに
2017_12_12_224229_create_rounds_table.php
というファイルが生成され"/app"フォルダに
Round.php
というファイルが生成されます。
モデル名は単数形(Round)、テーブルは複数形(rounds)になります。
マイグレーション
マイグレーションファイルはテーブルのスキーマを定義します。
ほとんど空のマイグレーションファイルに、テーブルのスキーマをメソッドを使い定義します。
下記はマイグレーションファイルの例です。
シンプルなマイグレーションファイル
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateRoundsTable extends Migration
{
/**
* マイグレーション実行
*
* @return void
*/
public function up()
{
Schema::create('rounds', function (Blueprint $table) {
$table
->increments('id')
->comment('ID');
$table
->string('name')
->comment('試験実施回名');
$table
->timestamp('created_at')
->default(DB::raw('CURRENT_TIMESTAMP'))
->comment('登録日');
$table
->timestamp('updated_at')
->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'))
->comment('更新日');
});
}
/**
* ロールバック時に実行
*
* @return void
*/
public function down()
{
Schema::dropIfExists('rounds');
}
}
upメソッドにマイグレーション実行時に動作するプログラム、downメソッドにロールバック時に実行に動作するプログラムを書きます。
downメソッドにはテーブルを削除する記述を書きます。
テーブル定義する際のメソッドの説明はLaravel 5.5 データベース:マイグレーション
の「カラム」を参照してください。
外部参照制約をつけたマイグレーションファイル
下記のようなコードを書くと、外部参照制約を追加できます。
$table
->foreign('firstcategory_id')
->references('id')
->on('firstcategories')
->onDelete('RESTRICT')
->onUpdate('RESTRICT');
外部参照制約を追加した例
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateQuestionsTable extends Migration
{
/**
* マイグレーション実行
*
* @return void
*/
public function up()
{
Schema::create('questions', function (Blueprint $table) {
$table
->increments('id')
->comment('ID');
$table
->integer('number')
->unsigned()
->comment('問題番号');
$table
->text('body')
->comment('問題文');
$table
->text('commentary')
->comment('解説');
$table
->integer('firstcategory_id')
->unsigned()
->comment('小項目ID 外部参照 firstcategories.id');
$table
->integer('divition_id')
->unsigned()
->comment('問題種別ID 外部参照 divitions.id');
$table
->integer('round_id')
->unsigned()
->comment('試験実施回ID 外部参照 rounds.id');
$table
->integer('examination_id')
->unsigned()
->comment('試験区分ID 外部参照 examinations.id');
$table
->timestamp('created_at')
->default(DB::raw('CURRENT_TIMESTAMP'))
->comment('登録日');
$table
->timestamp('updated_at')
->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'))
->comment('更新日');
/*
* 外部参照制約 小項目
*/
$table
->foreign('firstcategory_id')
->references('id')
->on('firstcategories')
->onDelete('RESTRICT')
->onUpdate('RESTRICT');
/*
* 外部参照制約 問題種別
*/
$table
->foreign('divition_id')
->references('id')
->on('divitions')
->onDelete('RESTRICT')
->onUpdate('RESTRICT');
/*
* 外部参照制約 試験実施回
*/
$table
->foreign('round_id')
->references('id')
->on('rounds')
->onDelete('RESTRICT')
->onUpdate('RESTRICT');
/*
* 外部参照制約 試験区分
*/
$table
->foreign('examination_id')
->references('id')
->on('examinations')
->onDelete('RESTRICT')
->onUpdate('RESTRICT');
});
}
/**
* ロールバック時に実行
*
* @return void
*/
public function down()
{
Schema::dropIfExists('questions');
}
}
マイグレーション実行
次のコマンドでマイグレーションを実行すると、テーブルが生成されます。
$ php artisan migrate
Migrating: 2017_12_12_224229_create_rounds_table
Migrated: 2017_12_12_224229_create_rounds_table
この通りテーブルが作成されました!
モデル
Laravelの命名に規則に従えばモデルは空でも役割を果たします。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Round extends Model
{
//
}
ORマッパーのEloquentを使い際は、モデルにhasOneやhasManyやbelongsToを記載します。
※2017年12月13日時点でEloquentを利用して動作確認をしていません。
※メソッド名が異なるとうまく動作しないので注意してください(結構シビアです)。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Question extends Model
{
/*
* belongsTo設定
* 小項目を参照
*/
public function firstcategory()
{
return $this->belongsTo('App\Firstcategory');
}
/*
* belongsTo設定
* 問題種別を参照
*/
public function divition()
{
return $this->belongsTo('App\Divition');
}
/*
* belongsTo設定
* 試験実施回を参照
*/
public function round()
{
return $this->belongsTo('App\Round');
}
/*
* belongsTo設定
* 試験区分を参照
*/
public function examination()
{
return $this->belongsTo('App\Examination');
}
}
シーダー
テンプレートを自動生成
$ php artisan make:seeder RoundsTableSeeder
Seeder created successfully.
コマンドを実行すると、"database/seeds"に
RoundsTableSeeder.php
が生成されます。
このテンプレートに追加するデータを作ります。
プログラムで管理できるので、単純な繰り返しはループを使えてデータ生成が楽にできます。
シーダーファイルを作成
<?php
use Illuminate\Database\Seeder;
/*
* Eloquentを利用するのでRoundモデルを使う
*/
use App\Round;
class RoundsTableSeeder extends Seeder
{
/**
* シーダーを実行
*
* @return void
*/
public function run()
{
/*
* 必要な情報を配列化
*/
$round_names[] = '平成21年度春';
$round_names[] = '平成21年度秋';
$round_names[] = '平成22年度春';
$round_names[] = '平成22年度秋';
$round_names[] = '平成23年度特別';
$round_names[] = '平成23年度秋';
$round_names[] = '平成24年度春';
$round_names[] = '平成24年度秋';
$round_names[] = '平成25年度春';
$round_names[] = '平成25年度秋';
$round_names[] = '平成26年度春';
$round_names[] = '平成26年度秋';
$round_names[] = '平成27年度春';
$round_names[] = '平成27年度秋';
$round_names[] = '平成28年度春';
$round_names[] = '平成28年度秋';
$round_names[] = '平成29年度春';
$round_names[] = '平成29年度秋';
/*
* ループしてデータを作成
*/
foreach($round_names as $round_name)
{
Round::create([
'name' => $round_name,
]);
}
}
}
シーダーを実行
シーダーを実行するとデータが入ります。
$ php artisan db:seed --class=ExaminationsTableSeeder
(特に成功メッセージなし)
この通りデータが入りました!
マイグレーションとしーだを使えばデータベース管理が楽にできます!