🔐

Laravel 地味に進化していた $guarded 機能

2022/09/13に公開

前書き

Laravel の Eloquent モデルで複数代入をする際に $fillable か $guarded を設定しますが、その $guarded 機能が地味に進化していたので、それについて書きたいと思います。とは言え、進化したのは、Ver.6.18.35 の時なので、既にご存じの方には、「何を今更… ( ´,_ゝ`) プッ」と笑われてしまうかも知れません😅

(以降、動作確認は、Ver.9.29 で行っていますが、Ver.6.18.35 以降であれば、同じになるはずです)

本題

インストール仕立ての Laravel で、以下のように変更します。

User.php

    // こちらはコメントアウト
    // protected $fillable = [
    //     'name',
    //     'email',
    //     'password',
    // ];

    // パターンA
    protected $guarded = ['id'];

    // パターンB
    protected $guarded = [];

web.php
<?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