Laravelでのモデル作成とマイグレーションファイル編集に関する備忘録。
■ モデル作成
- Laravel公式リファレンス
- make:model は、Eloquentモデルの生成コマンド。
モデル作成(※モデル名は、大文字から始まる単数形)
% 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に更新 ・ RESTRICT、NO 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');
}
}