このブログを開始してから、もうすでに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のヘルパー関数です。