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');
}
}