◀ 2.

LaravelのMassAssignmentException|fillableの原因と安全な対処

▶
この記事の要点
  • Laravel の Add [field] to fillable property to allow mass assignment
  • 原因: 一括代入のガードに拒否された。完全ガードと、許可外キーの破棄・厳格モードを区別する
  • 対処: 入力と権限を確認し、一括代入を許可してよいカラムだけ $fillable に指定
  • セキュリティ上重要: 想定外のカラムへの不正代入を防ぐための仕組み

 

エラー内容

入力検証、許可キーへの限定、モデルのfillableを別々に確認する図

Add [~] to fillable property to allow mass assignment on [App\~].

Laravelで Eloquent モデル経由でレコードを作成・更新するとき、Model::create([...]) や Model::fill([...]) のような マスアサインメント を行った際に出るエラーです。Eloquentの一括代入ガードに関する例外です。許可方式にはfillableとguardedがあり、対象版・モデル設定・厳格化の有無によって、未許可キーが例外になる場合と破棄される場合があります。

発生条件

  • Model::create(['name' => '...', 'email' => '...']) のように、配列で複数カラムを渡して保存している
  • 完全ガードのモデルに属性を渡した場合、または未許可属性の破棄を例外にする設定が有効な場合を確認する
  • fill() やモデルインスタンスの update() など、一括代入を通る処理でも設定に応じて発生する。検索・保存の全経路が同じ条件とは限らない

対処法

1. モデルに $fillable を追加(推奨)

マスアサインメントを許可するカラムをモデルに明記します。

// app/Models/User.php
class User extends Model
{
    protected $fillable = [
        'name',
        'email',
        'password',
    ];
}

エラーメッセージに出た名前は拒否された属性です。追加すべきとは限りません。綴り間違い・不要な入力・権限を伴う項目を確認し、その操作で許可してよい属性だけを指定します。passwordを扱う元の例では、実アプリのハッシュ化処理と入力検証も別途必要です。

2. $guarded を使う書き方

逆に「これだけは一括代入を禁止する」スタイルもあります。空配列にすると全カラム許可になります。

protected $guarded = ['id'];
// または全カラムを許可(非推奨)
protected $guarded = [];

3. 1カラムずつ代入する場合の注意

直接代入はfillableの制限を通りません。ただし入力検証・認可・型変換・DB制約は別です。以下は元の例を残したもので、例外回避のために未検証の入力をそのまま保存する方法として使わないでください。

$user = new User();
$user->name = 'Taro';
$user->email = 'taro@example.com';
$user->save();

$fillable と $guarded の使い分け

プロパティ意味適している場面
$fillable許可するカラムのホワイトリストセキュリティ重視。基本これが推奨
$guarded許可しないカラムのブラックリストカラム数が多くて列挙が現実的でない場合

注意点

  • $guarded = [] は一括代入の制限を開放する設定。create($request->all()) は避け、検証済み値から操作ごとの許可キーに絞る。入力の検証と認可は別に行う
  • 新規カラムでも、ユーザーによる一括代入を許可する必要がある場合だけ $fillable を更新する
  • id、created_at、updated_at は通常 $fillable に含めない

Laravel 13で確認した例外と破棄の違い

Laravel 13.34.0のfill()をDB接続なしで実行した結果です。モデルを新規作成して属性を確認し、create・save・実DBへの更新は行っていません。

モデルと設定渡す属性結果
初期状態の完全ガードnameMassAssignmentException
fillableにname・email、厳格化なしname・is_adminnameだけ代入。is_adminは破棄
同じfillable、破棄の例外化ありname・is_admin未許可キーで例外
guardedが空配列is_admin代入されるため危険な開放になり得る
属性へ直接代入is_adminfillableで止まらない

例外が出なかったから全項目が保存された、とは判断できません。逆に例外発生前に許可属性がモデルへ入る場合もあるため、失敗したモデルをそのまま保存せず処理を中断します。エラーメッセージの形は条件や版で異なります。

開発時に破棄を検出する設定

AppServiceProviderのbootメソッドへ加える断片です。既存bootの処理を置換せず、開発環境での検出に使います。

Illuminate\Database\Eloquent\Model::preventSilentlyDiscardingAttributes($this->app->isLocal());

これは入力検証やアクセス制御を代行しません。設定を有効にした後は、任意の余分なキーで処理が失敗する経路もテストしてください。

検証済み入力から必要なキーだけ渡す

プロフィール更新でnameとemailだけを受け付ける独立例です。まずコントローラで入力を検証します。必須項目や制約は実際の新規作成・部分更新の仕様に合わせて変更してください。

$validated = $request->validate([
    'name' => 'required|string|max:100',
    'email' => 'required|email',
]);

説明用モデル ProfileExample.php

<?php

use Illuminate\Database\Eloquent\Model;

class ProfileExample extends Model
{
    protected $fillable = ['name', 'email'];
}

検証済み配列を受け取る input.php

<?php

use Illuminate\Support\Arr;

function fillProfile(ProfileExample $profile, array $validated): void
{
    $profile->fill(Arr::only($validated, ['name', 'email']));
}

モデルと関数を読み込んだ後、fillProfile($profile, $validated)へ検証済み値を渡します。Arr::onlyはキーの選択で、値の正しさは検証しません。関数単体へ未検証値を渡す使い方は禁止です。この例はfillまででDBへ保存しません。実処理では対象レコードを変更できる権限を確認し、保存・失敗時の処理を別途設計します。

直らない・値が反映されないとき

  1. 実際に呼ばれたモデルクラス、属性名、fillable/guardedを確認する。同名クラスや綴り違いも見る。
  2. 完全ガード・厳格化・unguard・独自の例外コールバックを確認する。全体のガード解除を対処にしない。
  3. 検証済み配列に必要なキーがあるか確認する。余分な入力や秘密情報をログへまとめて出さない。
  4. 代入後の属性と保存後の結果を区別する。今回のテストは属性代入までで、DB制約・イベント・実HTTP処理は未検証。

公式資料: Laravel 13の一括代入、入力検証(2026年10月確認)。

子ページ

子ページはありません

同階層のページ
  1. SQLSTATE[HY000] [1045] Access denied for user 'homestead'@'localhost'
  2. Add [~] to fillable property to allow mass assignment on [App\~].
  3. PHP Parse error: syntax error, unexpected 'class' (T_CLASS), expecting identifier (T_STRING) or variable (T_VARIABLE) or '{' or '$' in ~
  4. Changing columns for table "~" requires Doctrine DBAL; install "doctrine/dbal"
  5. MethodNotAllowedHttpException No message
  6. Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found
  7. production.ERROR: No application encryption key has been specified.
  8. Dotenv values containing spaces must be surrounded by quotes.
  9. Laravel \ Socialite \ Two \ InvalidStateException
  10. The page has expired due to inactivity. Please refresh and try again.
  11. Failed to clone https://github.com/symfony/thanks.git via https, ssh protocol
  12. Illegal offset type
  13. Cannot access protected property Illuminate\Http\Request::$...
  14. Emitted value instead of an instance of Error
  15. 画像保存時にInternal Server Error
  16. Failed to authenticate on SMTP server with username ...
  17. PostTooLargeException
  18. Database hosts array is empty.
  19. Invalid request (Unsupported SSL request)
  20. does not comply with psr-4 autoloading standard. Skipping.
  21. MySQLのSTR_TO_DATE関数を使用するとnullが返却される問題