【Laravel】シーダーの中で Eloquent の fill メソッドによるフィルターが機能しない問題と解決方法

  • 2021年12月20日
  • 2021年12月20日
  • Laravel

 この記事で扱う Laravel のバージョンは 8.75.0 です。実際に動作確認していませんが GitHub を見るに 5.2.0 以降で共通な動作です。

 Laravel にはシーダーというデータベースに値を入れるためのコードの置き場があります。これはphp artisan db:seedで実行されテストデータ、初期データを作ります。
データベース:シーディング 8.x Laravel
 このシーダーですが次の様な fill メソッドを用いたコードを書くとエラーを起こします。

-- テーブル定義(PostgreSQL)
create table table_name
(
    id   serial
        constraint table_name_pk
            primary key,
    name varchar(255)
);
/** モデル */
class TestTable extends Model
{
    protected $table = 'table_name';

    protected $fillable = [
        'name',
    ];
}
/** シーダー */
(new TestTable)->fill([
   'name' => '浜松太郎',
   'name_kana' => 'はままつたろう',
])->save();
/*
エラー内容
SQLSTATE[42703]: Undefined column: 7 ERROR:  column "name_kana" of relation "table_name" does not exist                                                                                                         
  LINE 1: insert into "table_name" ("name", "name_kana", "updated_at",...                                                                                                                                         
                                            ^ (SQL: insert into "table_name" ("name", "name_kana", "updated_at", "created_at") values (浜松太郎, はままつたろう, 2021-12-20 10:32:43, 2021-12-20 10:32:43) returning "id")
*/

 存在しないカラムである name_kana を INSERT しようとしてエラーが発生します。fill メソッドと fillable プロパティが本来の通りに動作するならば fill を実行した時点で name_kana は落とされ、問題なく INSERT が成功します。
Eloquentの準備 8.x Laravel#複数代入
 しかし、実際にはエラーが起こります。fill メソッドが動作していません。

 この挙動は次の db:seed で実行されるコマンドクラス中にある次のコードが原因です。
framework/SeedCommand.php at 8.x · laravel/framework#SeedCommand.php#L65

    public function handle()
    {
// 略
        Model::unguarded(function () {
            $this->getSeeder()->__invoke();
        });

 Model::unguarded は渡したコールバックの処理中 Eloquent の基底クラスの unguarded プロパティを true にするメソッドです。unguarded プロパティを true にして何が起こるかというと fillable, guarded プロパティによる複数代入のガード機能である代入するキーの絞り込みが動作しなくなります。これによりシーダークラス実行中は fill による絞り込みが効かず、未定義カラムを INSERT しようとしてエラーが発生しました。

 これを対策するならば次の様になります。

        \Illuminate\Database\Eloquent\Model::reguard();
       (new TestTable)->fill([
           'name' => '浜松太郎',
           'name_kana' => 'はままつたろう',
       ])->save();

 reguard メソッドで unguarded を false にしてfill を有効化します。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG