🦺

Laravel 新機能、fillable に列挙されてない項目が投入されたら、エラーとなる機能を試してみる

2022/09/16に公開

前書き

Laravel Ver.9.28 で、タイトルに記載した機能が実装されましたので、その試した内容を記事にしたいと思います。この機能は、機能をオンにすることで、実現できます。

ちなみに、そのプルリクのタイトルや中身を見ると、
https://github.com/laravel/framework/pull/43893

fillable の時のみ有効で、guarded は関係無いと思ってしまうのですが、guarded を使った時もこの機能の対象になります。

しかも(トリビアですが)、当初の Ver.9.28 ではバグっていて、fillable の時は全く機能してないんですね。guarded の時だけ機能しているという…(プルリクのタイトルと違うじゃん…💦)。その後、Ver.9.30 で修正されました。

私がブログネタ探しでこの機能をいじっている時、このバグを見つけてしまったので、やむなく😅バグ報告を送りました。

まぁ、犬も歩けば棒に当たるとは、よく言ったものです。(^3^)ぷっ

https://github.com/laravel/framework/issues/44094

(バグ報告送った時は、guarded にも効くとは思っていなかったので、バグ報告の半分は、バグでは無かったのですが)

本文

復習

という事で、まずは今までの Laravel の挙動の復習から。
インストール仕立ての Laravel で、以下のようにします。

User.php
    // コメントアウト
    // protected $fillable = [
    //     'name',
    //     'email',
    //     'password',
    // ];
web.php
use App\Models\User;
use Illuminate\Support\Facades\Route;

Route::get('', function () {
    $user = new User;
    $user->fill(['hoge' => 'bar']);

    dd($user->toArray());
});

このように、fillable / guarded の設定を全くしないで、一括代入すると、MassAssignmentException の例外が出て、エラーとなるのでした。

ですが、fillable のコメントアウトを外して、有効にした場合、

User.php
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

['hoge' => 'bar'] とか代入しているのにも関わらず、特にエラーにならずにスルーされるのですね。
結果、以下のように出力されます。

^ []

ただこれだと、fillable に追記を忘れた際や、綴りを間違えた時に、それに気付かずバグとなってしまう恐れもあるのですね。それを防ぐのが、この機能の目的なんですね。

ということで、今回の新機能を使って、エラーにしてみたいと思います。

fillable の場合

それでは、fillable は有効にしたままで、AppServiceProvider などに以下のように追加します。この記述で、今回の機能が有効になります。

AppServiceProvider
use Illuminate\Database\Eloquent\Model;

    public function boot()
    {
        Model::preventSilentlyDiscardingAttributes();
    }

この状態でアクセスすると、今度は、MassAssignmentException の例外が出て、エラーとなりました。いい感じですね。

で、上記の微妙な罠を紹介させていただきますと、

// 正解
Model::preventSilentlyDiscardingAttributes();

// 不正解(prevents と最後に s がある。こちらは設定状況を返す。ややこしー)
Model::preventsSilentlyDiscardingAttributes();

どちらもメソッドは存在しますので、この罠に掛からないようご注意下さい。掛かってしまうと、設定したつもりが、実は設定されていないという事になってしまいます。

ちなみに、開発環境のみで有効にしたい場合は、下記の感じ

Model::preventSilentlyDiscardingAttributes(! app()->isProduction());

// AppServiceProvider の中であれば app() → $this->app でもOK

guarded の場合

guarded の場合も例外が出るようになっており、2パターンがあります。

パターン1

fillable はコメントアウトして、guarded を下記のようにします。

User.php
    // コメントアウト
    // protected $fillable = [
    //     'name',
    //     'email',
    //     'password',
    // ];

    protected $guarded = ['id'];

web.php は以下のように変えます。

web.php
    $user = new User;
    $user->fill(['id' => 123]);

guarded の対象になっている id を複数代入しようとしたので、この時点で、MassAssignmentException の例外が出て、エラーとなります。

パターン2

実はもう1パターンあります。下記みたいのでも同様に MassAssignmentException の例外になります。

web.php
    $user = new User;
    $user->fill(['hoge' => 'bar']);

これは、前回のブログ記事と関連するのですが、DBに実際に存在しない項目を指定すると、このようになります。

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

ちなみに、パターン1、パターン2とも、

protected $guarded = [];

のように空の配列で指定した際は、共に例外は起きず、今まで通りスルーされる形になります。

後書き

新規案件で、開発環境ではバグを防ぐ為に有効にしておくといいかも知れませんね。

ちなみに、プルリクの最初の本文は、実際にマージされた時には、話が異なっている(古くなっている)事が結構あります。実際には存在しないメソッドや機能が書かれたままの事があります。(本文を更新してくれれば良いのですが…)

間違い等ありましたら、コメント下さい。

Discussion