LoginSignup
52
53

More than 3 years have passed since last update.

【Laravel】初めてのマイグレーション

Last updated at Posted at 2020-10-17

Laravelでのモデル作成とマイグレーションファイル編集に関する備忘録。

■ モデル作成

モデル作成(※モデル名は、大文字から始まる単数形)
% php artisan make:model モデル名             // app直下にファイル生成
% php artisan make:model Entities/モデル名    // app/Entities下にファイル生成される
% php artisan make:model モデル名 -m -c -r    // モデル、マイグレーション、コントローラーを一気に生成

% php artisan make:model -h                 // どんなオプションがあるか?

【モデル作成(make:model)のオプション例】

オプション 機能
-a, --all モデルの移行、ファクトリ、リソースコントローラー生成
-c, --controller コントローラー生成
-f, --factory モデルの新しいファクトリー作成
--force モデルが存在する場合でもクラス作成
-m, --migration マイグレーション作成
-p, --pivot 生成モデルをカスタム中間テーブルモデルにする必要があるか?を示す
-r, --resource 生成コントローラーをリソースコントローラーにする必要があるか?を示す
-h, --help どんなオプションがあるかを見れる
-q, --quiet メッセージを出力しない
-V, --version アプリのバージョン表示
--ansi ANSI強制出力
--no-ansi ANSI出力を無効にする
-n, --no-interaction インタラクティブな質問を省略
--env[=ENV] コマンドを実行する環境の指定
-v, --verbose メッセージの詳細度の指定。1:通常出力、2:詳細出力、3:デバッグ用
  • マイグレーションクラスは、2つのメソッドがある(up、down)。
    • upメソッド : マイグレーション実行時の処理 を記述。
    • downメソッド : ロールバック時の処理を記述(upメソッドの逆)。
作成されるマイグレーションファイルのざっくりイメージ(database/migrations/xxxxx_create_xxxxx_table.php)
public function up() {           // マイグレーション実行時の処理は、up() に記述
    Schema::create('xxxxxs', function (Blueprint $table) {
      $table->increments('id');        // デフォルト
      $table->データ型('カラム名');        // カラム作成
      $table->tinyInteger('gender')->unsigned()->comment('性別 1:男、2:女');
      $table->timestamps();            // デフォルト
    });
}
public function down() {        // ロールバック時の処理は、down() に記述
    Schema::dropIfExists('xxxxxs');    // テーブル削除
    Schema::create('xxxxxs', function (Blueprint $table) {
      $table->dropColumn('カラム名');    // カラム削除
      $table->dropColumn('gender');
    });
}

よく使うマイグレーション関連のコマンド

マイグレーションに関するコマンド
% php artisan migrate:status              // 状態確認
% php artisan migrate                     // マイグレーション実行
% composer dump-autoload                  // 「クラスが見つかりません」エラーが発生したら、migrateコマンドを再発行してみる
% php artisan migrate:refresh             // 初期化→再実行(マイグレーションファイル追加でエラーが出た時など)
% php artisan migrate:fresh               // 全テーブル削除 →マイグレーション実行
% php artisan migrate:rollback            // ロールバック(1つ前)
% php artisan migrate:rollback --step=2   // ロールバック(戻る位置指定)
% php artisan migrate:reset               // 全マイグレーションをリセット(初期化)

■ マイグレーションファイルのみ作成したいなら

例)マイグレーションファイルのみ作成したい場合
% php artisan make:migration create_boxs_table --create=boxs             // boxsテーブル生成
% php artisan make:migration add_カラム名_to_users_table --table=users     // usersテーブルにカラム追加(既存テーブルを更新)
% php artisan make:migration rename_旧テーブル名_to_新テーブル名_table        // テーブル名変更
% php artisan make:migration change_カラム名_既存カラム型_to_新カラム型_on_テーブル名_table --table=テーブル名    // カラム型変更
% php artisan make:migration change_boxs_table --table=boxs              // ざっくりテーブル変更

// マイグレーションファイルの編集ができたら、実行
% php artisan migrate
% composer require doctrine/dbal     // 注)変更・更新には、更新用のパッケージのインストールが必要
% php -d memory_limit=-1 /usr/local/bin/composer require doctrine/dbal    // メモリー制限エラーが出た時は、メモリーを上げてインストールしてみる

■ マイグレーションファイルの書き方

テーブル関連

テーブル
// テーブル名変更
Schema::rename("旧テーブル名", "新テーブル名");

// テーブル削除
Schema::drop('users');
Schema::dropIfExists('users');     // テーブルがあれば削除して、なければ何もしない(エラー回避)

カラム関連

カラム追加・削除

カラム追加・削除
// カラム追加
$table->カラム型('カラム名');
$table->string('カラム名');

// カラム削除
$table->dropColumn('削除したいカラム名');
$table->dropColumn(['削除したいカラム名1', '削除したいカラム名2',..]);   // 複数のカラムを削除したい場合は、配列で渡す
  • カラム削除のコマンド
削除コマンド 説明
$table->dropRememberToken( ); remember_tokenカラム(Eloquent認証ドライバーを扱うためのカラム )
$table->dropSoftDeletes( ); deleted_atカラム(ソフトデリート用のNULL可能なカラム)
$table->dropSoftDeletesTz( ); ↑ と同義
$table->dropTimestamps( ); created_atとupdated_atカラム
$table->dropTimestampsTz( ); ↑ と同義
  • ソフトデリートとは?
    • 論理削除のこと。データは削除せず、データを削除したものをして取り扱う。

カラム変更

  • カラム変更前に、composer.jsonファイルでdoctrine/dbalの追加が必要。
    • 現在の状態を決め、指定カラムの修正を行うSQLクエリ生成に使われる。
composer require doctrine/dbal
カラム変更
// カラム名変更
$table -> renameColumn('以前のカラム名', '新しいカラム名');

// カラム属性の変更
$table->string('name', 50) -> change();                     // nameカラムのサイズ変更
$table->string('name', 50) -> nullable() -> change();       // NULLを許可する場合
$table->string('name', 50) -> nullable(false) -> change();  // NULLを許可しない場合

// カラム型の変更
$table->新しいデータ型('カラム名')->change();

  • chengeメソッド : カラム型、属性の変更。

インデックスや制約など

インデックス

  • Laravelでは、デフォルトで意味が通る名前をインデックスに付ける。
    • インデックス名 : テーブル名_インデックスしたカラム名_インデックスタイプ

インデックス 【追加】

インデックス追加コマンド 概要 使用例
index('カラム名') インデックス追加 $table->integer('category_id')->index('category_id');
unique('カラム名') ユニークキー追加 $table->unique('email');、$table->string('email')->unique();
primary('id'); 主キー追加 $table->primary('id');
primary(['id', 'parent_id']); 複合キー追加 $table->primary(['id', 'parent_id']);
spatialIndex('カラム名'); 空間インデックス追加(SQLite以外) $table->spatialIndex('location');

インデックス 【削除】

  • インデックス削除は、インデックス名を指定する。
インデックス削除コマンド 概要
$table->dropIndex('テーブル名_カラム名 _index'); インデックス削除
$table->dropUnique('テーブル名_カラム名 _unique'); ユニークキー削除
$table->dropPrimary('テーブル名_id _primary'); 主キー削除
$table->dropSpatialIndex('テーブル名_カラム名 _spatialindex'); 空間インデックス削除(SQLite以外)
  • カラムの配列をインデックスの削除メソッドに渡すと、インデックス名が自動生成される。
例)インデックス削除
$table -> dropIndex(['content']);   // 例) boxs_content_index

外部キー

外部キー 【追加】

  • 紐づけたいテーブルのid(unsined(符号無し))と同様の制約をつけないと形式不一致でエラーになるので注意。
既存カラムを外部キーにする(紐づける)場合
例)既存のuser_idカラムを外部キーにする
$table->integer('user_id')->unsigned()->change();                  // 符号無し属性に変更
$table->foreign('user_id')->references('id')->on('users');         // 外部キー参照
新規カラムとして、外部キーを作成する場合
例)usersテーブルのidカラムを使って、postsテーブルにuser_idカラムを定義する
Schema::table('posts', function (Blueprint $table) {
  $table->integer('user_id')->unsigned();                           // 符号無し属性を作成
  $table->foreign('user_id')->references('id')->on('users');        // 外部キー参照
});

// 参)外部キーを違う名前にしたい場合
$table->unsignedBigInteger('外部キー名');
$table->foreign('外部キー名')->references('id')->on('テーブル名')->onDelete('cascade');
  // モデルでのリレーションの書き方例(少し違うので注意)
  public function テーブル名() {
      return $this->belongsTo('App\Models\モデル名', '外部キー名');
      return $this->belongsTo('App\Models\モデル名','外部キー名')->withDefault();
      return $this->hasMany('App\Models\モデル名','外部キー名');
  }

外部キー 【削除】

  • dropForeignメソッド : 他のインデックスと似た命名規則。
  • テーブル名_カラム名_foreign
外部キーの削除
$table->dropForeign('posts_user_id_foreign');
$table->dropForeign(['user_id']);              // 配列で渡せば、命名規則に従った名前が使用される
  • 参)マイグレーション中の外部キー制約を一時的に、有効/無効化ができるメソッドも用意されてる。
    • 外部キー制約を無効にしてDBを操作し、終わったら有効に戻すような時に使う。
参)外部キー制約の有効/無効化の変更
Schema::enableForeignKeyConstraints();    //外部キー制約を有効化
Schema::disableForeignKeyConstraints();   //外部キー制約を一時的に無効化

マイグレーション関連のエラー

MySQL接続エラー: SQLSTATE[HY000][2002]No such file or directory

SQLSTATE[HY000] [2002] No such file or directory

  • DBに接続できていないエラー。
  • (原因例)
    • .envファイルとMySQL(DB)の、DB_DATABASE、DB_USERNAME、DB_PASSWORDが異なる。
    • .envファイルの修正後、キャッシュをクリアできてない。
    • rootユーザーのPW変更後に、MySQLを再起動してない。
    • MySQLソケットに関するファイル(mysql.sock)が読み込まれてない。 → database.phpで unix_socket を設定する。
ターミナルからunix_socketを確認
// MySQL
% show variables like '%sock%';
            :
 | socket | /Applications/MAMP/tmp/mysql/mysql.sock |
unix_socketの設定(database.php)
'mysql' => [
  'driver'      => 'mysql',
  'host'        => 'localhost',
          : 
  'unix_socket' => '/Applications/MAMP/tmp/mysql/mysql.sock', 
],

MySQL文字列長エラー: SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table 'users' add unique 'users_email_unique'('email'))

【原因】

公式キュメントによると、

DB中への「絵文字」をサポートのため、デフォルトでutf8mb4文字セットを使ってる。MySQLがバージョン5.7.7未満の場合、マイグレーション時に、インデックス用文字列長を明示的に設定する必要がある

実際に確認してみると、確かに、MySQLバージョン5.6を使っており、utf8mb4となってる。utf8mb4は、1文字 = 4バイトなので、最大長の767バイトを超えてしまい、エラーとなってる模様。

mySQLのバージョン確認
% mysql --version
mysql  Ver 14.14 Distrib 5.6.47, for osx10.15 (x86_64) using  EditLine wrapper
app/config/database.php
  'mysql' => [
         :
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
         :

【解決方法】

  • 公式ドキュメント通りに、varcharカラムの最大長を191バイトに指定し、767バイト以上の文字列が入らないようにする。(767 ÷ 4 = 191.75)。
    • ↓の記述で、default値を変更できる(カラムごとに指定するのはめんどい)。マイグレーションをやり直せばエラーが消えるハズ。
app/Providers/AppServiceProvider.php
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;     // 追記

class AppServiceProvider extends ServiceProvider
{
          :
  public function boot()
    {
      Schema::defaultStringLength(191);   // 最大長を191バイトに指定
    }
          :
}
  • 若しくは、支障がないなら、、MySQLのバージョンを最新にする。

テーブルがないよーエラー: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'アプリ名.指定したテーブル名' doesn't exist (SQL: alter table '指定したテーブル名' add '追加しようとしたカラム名' int not null)

  • 既存テーブルへのカラム追加時に、テーブル名が間違ってた。。
    → マイグレーションファイルのテーブル名を修正して、マイグレーション実行 で解決。

カラムの数と保存しようとしてるデータの数が合わない: SQLSTATE[21S01]: Insert value list does not match column list: 1136 Column count doesn't match value count at ...

  • seederでカラムにデータを保存時に発生。
  • 保存しないカラムはnullとしてあげることで解決。

■ カラム型 一覧

コマンド 概要
increments('id'); 符号なしの数値(INT)を使ったID(主キー)。0〜4294967295。
bigIncrements('id'); 符号なしのBIGINTを使ったID(主キー)。0〜18446744073709551615。※ 21億行以上にならないなら、↑ を使う。
tinyInteger('numbers'); 数値(TINYINT型)。1バイト。0~255
smallInteger('カラム名'); 数値(SMALLINT型)。2バイト。0~65,535
mediumInteger('カラム名'); 数値(MEDIUMINT型)。3バイト。0~16,777,215
integer('カラム名'); 数値(INT型)。4バイト。0~4,294,967,295
bigInteger('カラム名'); 数値(BIGINT型)。8バイト。 0~18,446,744,073,709,551,615
float('カラム名', ○, △); トータルで8桁、小数点以下2桁以内の小数
double('カラム名', ○, △); トータルで15桁、小数点以下8桁以内の小数
decimal('カラム名', ○, △); トータルで5桁、小数点以下2桁以内の小数
char('カラム名', 5); 固定長文字列(長さが決まってる文字列)
string('カラム名'); 可変長文字列。デフォルトの最大文字数:255文字
string('カラム名', 100); 最大文字数指定の可変長文字列
text('カラム名'); 文字列。65,535文字(約64Kバイト)
mediumText('カラム名'); 長い文字列。16,777,215文字(約16Mバイト)
longText('カラム名'); めっちゃ長い文字列。4,294,967,295文字(約4Gバイト)
date('カラム名'); 日付
year('カラム名');
time('カラム名'); 時間
timeTz('カラム名'); 時間(タイムゾーン付き)
dateTime('カラム名'); 日時
dateTimeTz('カラム名'); 日時(タイムゾーン付き)
boolean('カラム名')->default(false); 真偽値
enum('カラム名', ['定数', '定数']); enum型
binary('カラム名'); バイナリデータ
json('カラム名'); JSON
jsonb('カラム名'); JSONB
timestamp('カラム名'); タイムスタンプ
timestampTz('カラム名'); タイムスタンプ(タイムゾーン付き)
timestamps(); created_atとupdated_at
nullableTimestamps(); ↑と同じ(NULLを許可)
softDeletes(); NULL可能な deleted_at カラム。例:退会日時などの管理用途(nullでなければ、退会済)
ipAddress('カラム名'); IPアドレス
macAddress('カラム名'); MACアドレス
rememberToken(); Eloquent認証ドライバーを扱うためのカラム。remember_tokenVARCHAR(100)NULLとして追加
uuid('id'); UUID。重複の可能性が小さく、一意な識別子として扱えるID
morphs('カラム名'); 符号なし数値(taggable_id)と文字列(taggable_type)を追加

■ オプション(カラム修飾子)一覧

カラム修飾子 概要
->comment('コメント内容') コメント追加(MySQLのみ)
->nullable() NULL許可
->unsigned() 整数カラムを符号なしに設定(MySQLのみ)。外部キーでよく使う
->default('値')、->default($value) デフォルト値を設定
->first() テーブルの最初(first)に設置(MySQLのみ)
->after('カラム名') 指定カラムの次にカラム追加(MySQLのみ)
->autoIncrement() 整数カラムをID(主キー)に設定
->charset('utf8') キャラクタセットを指定(MySQLのみ)
->collation('utf8_unicode_ci') コロケーションを指定(MySQL/SQL Serverのみ)
->nullable($value = true)、->nullable()->default(null) デフォルトでNULL値を指定
->storedAs($expression) stored generatedカラムを生成(MySQLのみ)
->useCurrent() TIMESTAMPカラムのデフォルト値をCURRENT_TIMESTAMPに指定
->virtualAs($expression) virtual generatedカラムを生成(MySQLのみ)
->onDelete('cascade') CASCADE:親テーブルを更新すると、子テーブルも更新
SET NULL:親テーブルを更新/削除すると、子テーブルはNULLに更新
RESTRICTNO ACTION:エラー発生

例) json型で連想配列を管理する場合

1つのカラムで連想配列を管理したい
// マイグレーション
$table->json('json_column');

// モデル
protected $fillable = ['json_column',];
protected $casts = ['json_column' => 'json',];  // コレを指定しないと、stringと扱ってしまう

// コントローラー
// 作成
ModelName::create([
    'json_column' => ['test1' => ['a'=>1, 'b'=>2,], 'test2'=>'456', ],
]);
// 取得
$test = array('test' => json_decode($request->test));

// ビュー
{{ Form::hidden('expert_check[]', json_encode($expert_select,TRUE)) }}

■ データの作成

  • テーブルが作成できたら、データを作成してみる。

seederで初期値を作成する場合

  • モデルごとに、Seederファイルを作成し、それをDatabaseSeeder.php で読み込んでデータ作成を実行する。
例)CategoriesTableSeeder.php生成
% php artisan make:seeder CategoriesTableSeeder

// クラスが見つからないエラーが出たら、、
% composer dump-autoload       // autoloadされるクラスを再定義
seederに初期データを記述する
// database/seeds/CategoriesTableSeeder.php
use Illuminate\Support\Facades\DB;     // ← この記述が必要(DBファサード使用の宣言)。忘れると、エラー Class 'Database\Seeders\DB' not found
public function run() {
  DB::table('categories') -> insert([
    ['name' => '情報発信'],
    ['name' => 'つぶやき'],
    ['name' => '備忘録'],
  ]);
}

// database/seeds/DatabaseSeeder.php で呼び出す
public function run() {
  $this -> call([
    CategoriesTableSeeder::class,
  ]);
}

// ターミナルで、データ作成を実行
% php artisan migrate:refresh --seed

// クラスが見つからないエラー(Class UsersSeeder does not exist)が生じた時
% composer dump-autoload    // クラスを再定義してみる
  • 因みに、seederでPWデータを作成する場合は、注意が必要。
seederでPWデータを作成する場合
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;   // ← この記述が必要(Hashファサード使用の宣言)
public function run() {
    DB::table('admins') -> insert([
        [
            'name' => 'ユーザー1',
            'email' => 'testuser1@gmail.com',
            'password' => Hash::make('testuser1'),    // 暗号化
        ],
                  :

コンソール操作でレコードを作成する場合

ターミナル操作で作成する方法
% php artisan tinker               // コンソール起動
  // レコード作成( インスタンスを作成して、saveメソッドを使う )
  >>> $box = new App\Box();        // インスタンス作成(※ 名前空間がデフォルトでAppなので、App\モデル名(); )
  >>> $box -> title = 'タイトル1';
  >>> $box -> content = '詳細1';
  >>> $box -> save();              // 保存
  => true                         // レコード作成成功

  >>> App\Box::all();             // 作成したデータの確認
  >>> App\Box::all()->ToArray();  // 作成したデータの確認(配列で取得)
  >>> exit

■ リレーション

  • テーブルの関連付け。
  • Tinkerで確認できる。
関係 リレーションの種類
1対1 hasOne
1対多 hasMany
多→1 belongsTo
多対多 belongsToMany(中間テーブル)
例)テーブルの関連づけ(モデルに記述)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Shop extends Model {
  // ココに記述↓
  Public function profile(){
    // リレーションの書き方の基本
    return $this -> リレーションの種類'関連付けるモデルファイルのディレクトリ';

    // 1対1 の場合
    return $this->hasOne('App\Models\モデル名');
    return $this->hasOne('App\Models\モデル名', 'foreign_key');         // 外部キーの場合
    return $this->hasOne('App\Models\モデル名', 'foreign_key', 'local_key');

    // 1対多 の場合
    return $this->hasMany('App\Models\モデル名');
    return $this->hasMany('App\Models\モデル名', 'foreign_key');       // 外部キーの場合
    return $this->hasMany('App\Models\モデル名', 'foreign_key', 'local_key');

    // 多対1 の場合
    return $this->belongsTo('App\Models\モデル名');
    return $this->belongsTo('App\Models\モデル名', 'foreign_key');    // 外部キーの場合
    return $this->belongsTo('App\Models\モデル名', 'foreign_key', 'owner_key');

    // 多対多 の場合
    return $this->belongsToMany('App\Models\モデル名');
    return $this->belongsToMany('App\Models\モデル名', 'role_user');                         // 第2引数: 繋げたモデル名(中間テーブル名)
    return $this->belongsToMany('App\Models\モデル名', 'role_user', 'user_id', 'role_id');   // 第3引数: このモデルのid名(外部キー)、第4引数: 繋げたいテーブルのid名(外部キー)
    return $this->belongsToMany('App\Models\User')->using('App\Models\RoleUser');

    // 仲介テーブルを通した関連付け
    return $this->hasOneThrough('App\Models\モデル名1', 'App\Models\モデル名2');
  }
}
52
53
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
53