Laravel 地味に進化していた $guarded 機能
前書き
Laravel の Eloquent モデルで複数代入をする際に $fillable か $guarded を設定しますが、その $guarded 機能が地味に進化していたので、それについて書きたいと思います。とは言え、進化したのは、Ver.6.18.35 の時なので、既にご存じの方には、「何を今更… ( ´,_ゝ`) プッ」と笑われてしまうかも知れません😅
(以降、動作確認は、Ver.9.29 で行っていますが、Ver.6.18.35 以降であれば、同じになるはずです)
本題
インストール仕立ての Laravel で、以下のように変更します。
// こちらはコメントアウト
// protected $fillable = [
// 'name',
// 'email',
// 'password',
// ];
// パターンA
protected $guarded = ['id'];
// パターンB
protected $guarded = [];
<?php
use App\Models\User;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
$user = new User();
$user->fill(['hoge' => 'bar']);
dd($user->toArray());
});
この時、パターンAとパターンBでどのような違いがあるかという話です。
情報の古い私は、「いやいや、同じでしょ!」なんて思っていましたが、これが違うのですね。
出力結果は下記の通りです。
パターンA
^ []
パターンB
^ array:1 [▼
"hoge" => "bar"
]
「は?」「パターンAで、$guarded はブラックリストだから、$guarded で列挙されたもの以外は、fill() されるのでは?」と思いましたが、Ver.6.18.35 の改良で、$guarded に列挙がある場合は、列挙されたのはもちろん、「DB から項目を調べ上げ、テーブルに実在する項目以外は fill されない」という風になりました。
[6.x] Verify column names are actual columns when using guarded #33777
確かにこのプルリク、見た記憶は3%位残っているのですが、ほぼ記憶の彼方に追いやられました。ちなみに、パターンAの時は、以下のような SQL 文が発行されています。
select column_name as `column_name` from information_schema.columns where table_schema = 'temp5' and table_name = 'users'
もちろん、明示的な fill() のみならず、Laravel 内部で fill() が呼ばれるもの(複数代入系)は同様となります。
この改良、本来であれば breaking change なのでマイナーやパッチリリースでは行われないはずですが、セキュリティ上の都合で急遽当てられたという事になりますね。
後書き
個人的には、パターンB派なので、この機能に出くわすことなく過ごしてきました。😪
ちなみにこの機能、Laravel v9.28.0 で実装された新機能に対するバグ報告を送った事で、気付く事となりましたが、次回余裕があれば、そちらの新機能もブログにしてみたいと思います。
間違い等ありましたら、コメント下さい。
Discussion