今回は「マスアサインメントの保護を外す」という大胆なタイトルです。マスアサインメントの保護は、Eloquentにおけるモデルの定義においての$fillable$guardedを通しての設定のことです。その保護を外す、とは、これを指定しない、ということではなく(もちろん外すのは危険)、あるケースにおいて外す必要が出てきたときに、どう外すかということです。

$fillable & $guarded

復習として、Eloquentにおけるこれらの変数の説明から始めます。

Laravelのプロジェクトのデフォルト設定のDBのusersのテーブルは以下のような構造です。

mysql> describe users;
+-------------------+---------------------+------+-----+---------+----------------+
| Field             | Type                | Null | Key | Default | Extra          |
+-------------------+---------------------+------+-----+---------+----------------+
| id                | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| name              | varchar(255)        | NO   |     | NULL    |                |
| email             | varchar(255)        | NO   | UNI | NULL    |                |
| email_verified_at | timestamp           | YES  |     | NULL    |                |
| password          | varchar(255)        | NO   |     | NULL    |                |
| remember_token    | varchar(100)        | YES  |     | NULL    |                |
| created_at        | timestamp           | YES  |     | NULL    |                |
| updated_at        | timestamp           | YES  |     | NULL    |                |
+-------------------+---------------------+------+-----+---------+----------------+

そして、EloquentのモデルのUserでは、以下のように$fillableが定義されています。


namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

つまり、name, email, password以外の項目、例えば、idとかとともにマスアサインしても、以下のようにUserのインスタンスの作成時には弾かれる、ということです。

$ php artisan tinker
>>> $input = ['id' => 1, 'name' => 'test', 'email' => 'test@example.com'];
=> [
     "id" => 1,
     "name" => "test",
     "email" => "test@example.com",
   ]
>>> new User($input);
[!] Aliasing 'User' to 'App\User' for this Tinker session.
=> App\User {#2985
     name: "test",
     email: "test@example.com",
   }
>>> 

作成されたインスタンスにおいては、idの項目はありません。fill()を使用しても同じことです。

>>> $user = new User();
=> App\User {#2984}
>>> $user->fill($input);
=> App\User {#2984
     name: "test",
     email: "test@example.com",
   }
>>> 

そして、$fillableでなく、$guardedを使用しても結果は同じことです。

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

    protected $guarded = [
        'id', 'email_verified_at', 'remember_token', 'created_at', 'updated_at'
    ];
...

保護を外す

さて、上の$fillableの保護指定をそのままにして、同じUserのモデルでidなど、name, email, password以外の項目を指定してインスタンスを作成したいときは、どうするのでしょう?

そこで登場するのが、unguard()のメソッドです。

>>> User::unguard()
=> null
>>> new User($input);
=> App\User {#2985
     id: 1,
     name: "test",
     email: "test@example.com",
   }
>>> $user = new User();
=> App\User {#2986}
>>> $user->fill($input);
=> App\User {#2986
     id: 1,
     name: "test",
     email: "test@example.com",
   }

idが入るようになりましたね。

解除するには、unguard(false)です。

>>> User::unguard(false);
=> null
>>> new User($input);
=> App\User {#2987
     name: "test",
     email: "test@example.com",
   }
>>> 

以前のようにもうidは入りません。

unguard()はセッションで有効ですが、1つのインスタンスで保護を外すには、forceFill()を使用できます。

>>> $user = new User();
=> App\User {#2985}
>>> $user->forceFill($input);
=> App\User {#2985
     id: 1,
     name: "test",
     email: "test@example.com",
   }

By khino