このブログを開始してから、もうすでに1年以上。RawのSQLを書いてコードに埋め込む日常から、Eloquentを使用したORMのコードへと日常へと移行しています。Eloquentに関しても、ブログを書き始めた頃からは理解が深まり、洗練されたLaravelのコードを書けるようになってきたこの頃です。
1年前に書いた「マスアサインメントで一括取り込み」のトピックで、EloquentのModelのクラスの属性fillableとguardedの話、1年の経験で学んだことを含めてここでもう一度説明します。
まず、話のお膳立てを。
DBテーブルmemberにおいて以下の項目があるとします。
+----------------+------------------+------+-----+---------------------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------------+------------------+------+-----+---------------------+----------------+ | member_id | int(10) unsigned | NO | PRI | NULL | auto_increment | | active_flag | char(1) | NO | | NULL | | | name | varchar(255) | NO | | NULL | | | email | varchar(255) | NO | UNI | NULL | | | password | varchar(60) | NO | | NULL | | | memo | varchar(100) | YES | | NULL | | | created_at | timestamp | NO | | 0000-00-00 00:00:00 | | | updated_at | timestamp | NO | | 0000-00-00 00:00:00 | | +----------------+------------------+------+-----+---------------------+----------------+
MemberのModelは以下のような定義になります。
...
class Member extends Model
{
protected $table = 'member';
protected $primaryKey = 'member_id';
protected $fillable = ['name', 'email'];
...
}
$timestampsや$incrementingの定義は必要ないです。両方ともデフォルトでtrueなので。
入力フォームは、

で、email, password, nameの項目を入力できます。
以下のコントローラで、入力フォームから入ってきた値は以下のコードでDBに保存できます。
...
class MemberController extends Controller
{
...
public store(Request $request)
{
$member = new Member;
$member->fill($request->all())->save();
...
}
...
}
しかし、先ほどの$fillable定義により、DBに保存されるのは、emailとnameのみです。実行されるSQLは以下で、他の入力された値は無視されるので、passwordはDBはデフォルトの空のままです。つまり、$fillableは、DBに入力したい値をリストするホワイトリストです。ちなみに、created_at, updated_atの項目は、Eloquentにより自動的に保存時の日時を記録します。
逆に、DBに入れたくない項目をリストするなら、つまりブラックリストを定義したいなら、$fillableの代わりに、$guardedを使用します。
...
class Member extends Model
{
protected $table = 'member';
protected $primaryKey = 'member_id';
protected $guarded = ['member_id', 'active_flag', 'password'];
...
}
以上がマスアサイメントの使用の仕方で、意図的あるいは間違って入力フォームから、DBへ保存されるのを防いでくれます。
$fillableや$guardedの目的を理解したところで、2つ問題。
まず、
active_flagやpasswordなどマスアサインメントで相手にしない項目の値はどうやってDBに保存するのでしょう?
これは、通常のオブジェクトの値のアサインメントで行います。
...
public store(Request $request)
{
$member = new Member;
$member->active_flag = 'Y'; //デフォルト
$member->password = bcrypt($request->password); //ハッシュ値に変換
$member->fill($request->all())->save();
...
}
...
次に、
入力画面によりDBに入れたい項目が違う場合は、どう$fillableを設定?
例えば、管理画面で会員の情報を編集する画面。そこでは、会員が有効か無効のフラッグ(active_flag)、そして会員に関するノート(memo)も付加情報としてDBに保存したいです。もちろん、裏で使用するのは、同じMemberのクラスなので、同じ$fillableは使えないですね。
1つは、先の値のアサイメント使用する方法。
$member->active_flag = $request->active_flag;
$member->memo = $request->memo;
$member->fill($request->all())->save();
しかし、これではもっと項目が増えたら面倒です。
$fillableを使用するのではなく、先のように$guardedをMemberで定義して、以下のようにコントローラにおいて、独自の$fillableを使用します。
...
public store(Request $request)
{
$fillable = ['email', 'name'];
$input = array_only($request->all(), $fillable);
$member = new Member;
$member->active_flag = 'Y';
$member->password = Hash::make($request->password);
$member->fill($input)->save();
...
}
public edit(Member $member, Request $request)
{
$fillable = ['email', 'name', 'active_flag', 'memo'];
$input = array_only($request->all(), $fillable);
$member->fill($input)->save();
...
}
...
array_onlyの関数は、Laravelのヘルパー関数です。
