はじめに
LaravelでModelといえばDBと一対一になってることが多いと思うんですが、たまにDBのテーブルのカラムにはないものを出力したいときってありますよね。
例えば住所とか顕著ですかね。
かんたんに一例としてこんな感じの users
テーブルがあったとします。
物理名 | 論理名 | 型 |
---|---|---|
id | ID | bigint(auto_increment) |
name | 名前 | varchar |
zip_code | 郵便番号 | varchar |
prefecture | 県名 | varchar |
city | 市町村名 | varchar |
address | 番地 | varchar |
それに対応する class はこんな感じだったりするわけじゃないですか。
namespace App\Models;
class User extends Authenticatable
{
}
そこで、住所を全部表示させるときにbladeなんかにこういう書き方するじゃないですか。
<p>住所: 〒{{ $user->zip_code }}{{ $user->prefecture }}{{ $user->city }}{{ $user->address }}</p>
これが1箇所ならまあ別にいいかなって思うんですが、複数出てくると指がつったりしますよね。
そんなときに accessor という便利テクをLaravelではよく使うことがあります。
DBにはないカラムをさもあるかのように出力させるやつですね。
これはModelに get{架空のカラム名}Attribute()
っていうメソッドを追加してやることで実現します。
今回は住所全部なので getFullAddressAttribute()
っていうメソッドを作ることにします。
namespace App\Models;
class User extends Authenticatable
{
public function getFullAddressAttribute(): string
{
return $this->zip_code . $this->prefecture . $this->city . $this->address;
}
}
で、実際bladeに記述するときに
<p>住所: 〒{{ $user->full_address }}</p>
ってやるとうまくいくってやつです。みんなよく知っていると思います。
ただこれ、vueコンポーネントのpropsに
<div id="app">
<User
users="@json($model)"
/>
</div>
みたいに雑に渡したいときとかにうまくいきません。まあそりゃ存在しないカラムですもんね。
ただ、accessorもさも普通にあるカラムのように渡してーなーって思うのが人情ってやつですよね。
力技でなんとかするパターン
bladeテンプレートに渡す前にModelをループしてねじ込むという方法があります。
$users = User::all();
if($users->count() > 0){
foreach($users as $k => $u){
$users[$key]->full_address = $u->full_address;
}
}
これでできはするけど……んーーーー、もやもやしますね。
toArrayメソッドをオーバーライドしてどうにかするパターン
いにしえのQiita記事にはこんなのありました。
こちらはModel側のtoArray()メソッドをオーバーライドする一工夫入れるやつです。
namespace App\Models;
class User extends Authenticatable
{
public function getFullAddressAttribute(): string
{
return $this->zip_code . $this->prefecture . $this->city . $this->address;
}
public function toArray()
{
return array_merge(parent::toArray(), ['full_address' => $this->full_address]);
}
}
なるほど……とはいえ、こんないかにも普段遣いしたいような機構をFW側で実装してないなんてないよなあ……って気持ちにもなります。
ドキュメントをちゃんと読め
まあ、ドキュメントちゃんと読むと書いてあるんですよね。
https://readouble.com/laravel/8.x/ja/eloquent-serialization.html#appending-values-to-json
アクセサを作成したら、モデルのappendsプロパティに属性名を追加します。
appends......!?
namespace App\Models;
class User extends Authenticatable
{
$appends = [
'full_address'
];
public function getFullAddressAttribute(): string
{
return $this->zip_code . $this->prefecture . $this->city . $this->address;
}
}
なんとこれだけでいいのです!
どうもこれ、ドキュメント遡るとLaravel5.1の頃からあった機能のようです。
くっそー、知らんかった! このページ穴が空くほど呼んでたつもりだったのに!
おわりに
まー、Laravelにはまだまだ知らない(気づいてない?)機能が色々ありそうですね!
なんかまた思いついたり見つけたりしたら投稿します!