set…Attribute() のメソッドについて調べものをしていたら、自分が全然理解できていなかったことに色々と気づけたのでメモです。
要点だけまとめておくと、
・モデルインスタンスのattributesには現在の値(編集された)が入っている
・originalには保存された値(元の)が入っている
・ミューテタはプロパティに値を入れた時点で動いている(saveしなくても)
・originalにアクセスするには、getRawOriginal()を使う
・アクセサを介さないattributesの値にアクセスするには、getAttributes()を使う
前置き
laravelのミューテタ・アクセサを利用して、値を保存・取得したい。
こんなコードがあったとします。
/** * nameカラムを取得時、.getを付与する */ public function getNameAttribute($val): string { return $val . '.get'; } /** * nameカラムの値は大文字に変換する */ public function setNameAttribute($val): string { return $this->attributes['name'] = strtoupper($val); }
上記のメソッドにより、
$user->name = 'test' // TEST $user->name // TEST.get
という挙動になります。
そして、アクセサを介さない素のnameカラムの値も欲しいなぁと思ったとします。以下で色々検証。
new しただけのインスタンス
ひとまずインスタンスを作成します。
$u = new User
の結果が以下の通りです。
Modelに設定されているカラムはあるけど、値は空です。
attributesもoriginalも空の配列になっています。
newして、name値を入れたインスタンス
newしただけのインスタンスはattributesもoriginも空ということが分かったので、
次は値を入れてみます。
$u->name = 'test'
そして、インスタンスがどうなったかdumpしてみましょう。
attributesに “name” => “TEST” とちゃんと入っていました!
この段階でちゃんとミューテタが効いているのでtestがTESTと大文字になって保存されています。
※ミューテタは、save()時ではなくインスタンスに値を設定した段階で動く
値を取得
当たり前ですが、この段階で$u->getRawOriginal() としても空の配列が返ります。
また、$u->getAttributes() とすると、アクセサを介さないattributesの値が取得できます
そして、tinkerで$u->name とすると、
ちゃんとアクセサも働いているのがわかります。
DBに保存されたインスタンス
new しただけのインスタンスはoriginal要素が無いことがわかったので、DBに保存してみましょう。
先ほどのインスタンスをそのままsave()で保存、
もう一度dumpすると…
保存されたインスタンスにはattributesもoriginalも、どちらもセットされています!
値を取得
こうなると、$u->getRawOriginal() で値が取得できました。
$u->getAttributes() とすると、
こちらも取得できます。(アクセサを介さない)
では、
ここでまた同じインスタンスに値を入れてみると
$u->name = 'efg'
だけでsave()はしていないので、attributesの値だけが新しい値で上書きされています!
attributesには加工された値(現在の値)が、
originalには元のままの値が入っているということが分かりました。
isDirty()
attributesとoriginalの違いがわかったところで、とても便利なメソッドがありました。
$u->isDirty()
とすると、現在の値(attributes)と元の値(original)を比較してくれるようです。
$u->name = ‘efg’ のままsave()していない状態でメソッドを使ってみると
trueが返りました!
とても便利ですね。
tinkerではdd()ではなくdump()
インスタンスの詳細を見たくてddするとtinkerが終了してしまい不便なので、
dump()を使用すると良いようです。
参考サイト