【Larvel】アクセサとミューテタ ~DBのデータ加工・独自プロパティの定義を行う~

## はじめに

Laravelのアクセサ・ミューテタを使うと、データベースからデータを取得 / 保存する際に、毎回実行したいデータ加工の処理を定義することができる。

今回はこれらの使用方法を備忘録としてまとめておく。

## アクセサ・ミューテタとは？

### アクセサ

DBからデータを取得する時に、特定の処理を行う機能。

【使用例】

（１）DBからユーザーの名前を取得する際に、大文字小文字の変換を行う。

（２）DBからユーザーデータを取得する際に、既存カラムを使って作成した新しい属性値も含める。

### ミューテタ

データベースへデータを保存する時に、特定の処理を行う機能。

【使用例】

データベースに郵便番号を保存する際に、ハイフンを取り除いて半角に変換した上で保存する。

これらアクセサとミューテタの使用方法を、Readoubleに記載の例を引用して説明する。

[Laravel 8.x Eloquent：ミューテタ／キャスト](https://readouble.com/laravel/8.x/ja/eloquent-mutators.html#:~:text=%E3%82%82%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82-,%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B5%EF%BC%8F%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%82%BF,-%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B5%E3%81%AE%E5%AE%9A%E7%BE%A9)

## アクセサの使用方法

モデルに`get◯◯Attribute`という名前でメソッドを作成すると、Laravelが自動的にアクセサだと判断してくれる。

この際、メソッド名の`◯◯`の部分に**加工したいカラム名**をアッパーキャメルケースで指定する。

アクセサはDBからのデータ取得時にデータを加工するための機能だが、以下の2通りの使い方ができる。

① DBにあるカラム名と同じカラム名でデータを返す場合

② DBにあるカラムを使って、新しいカラム名でデータを返す（DBのテーブルに実際には存在しないカラムを、独自に作ることができるイメージ）

それぞれの使い方を説明する。

### 使用方法①：****DBにあるカラム名と同じカラム名でデータを返す場合****

以下は、DBのusersテーブルに実際に存在する `first_name` というカラムの値を加工する例。

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザーの名前の取得
     *
     * @param  string  $value
     * @return string
     */
    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }
}
```

これにより、カラムの元の値がアクセサに `$value` として渡され、PHPのucfirst関数により最初の文字を大文字に変換した結果の値を返す。

よって例えば、 `User::find(1);` のようにデータを取得すると、DBのデータが「hanako」だった場合、レスポンスでは「Hanako」に変換されているということになる。

### 使用方法②：DBにあるカラムを使って、新しいカラム名でデータを返す場合

アクセサは既存のカラムを使って、新しい属性を作り出してレスポンスに含めることもできる。

以下は、DBのusersテーブルに既存の `first_name`　と `last_name` というカラムを使用して、 `full_name` という新しい属性を作成して返す例。

 `full_name` という属性を作るので、メソッド名は `getFullNameAttribute()` とする。

```php
/**
 * ユーザーのフルネームの取得
 *
 * @return string
 */
public function getFullNameAttribute()
{
    return "{$this->first_name} {$this->last_name}";
}
```

例えば、 `User::f``ind(1);` のようにデータを取得すると、DBには `full_name`　というカラムは存在しなくても、あたかもDBに `full_name` というカラムが存在するかのように、返り値には `full_name` という属性が含まれている。

以下のように記述することで、DBに存在するカラムと同じように `full_name` の値にアクセスすることができる。

```php
$user = App\User::find(1);
$fullName = $user->full_name;
```

### 使用方法②の注意点

DBから取得したデータをJSON形式でフロント側にレスポンスとして返す、、というような場面もあるだろう。

モデルのデータを配列やJSONに変換する時に、使用方法②で作成したDBにはカラムが無い属性も含めたい場合は、アクセサの使用に加えてモデルの `appends` プロパティに属性を設定する必要がある。

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * モデルの配列フォームに追加するアクセサ
     *
     * @var array
     */
    protected $appends = ['full_name'];
}
```

属性を`appends`リストへ追加すると、配列やJSON形式のレスポンスにも含まれるようになる。

また`appends` プロパティに設定した属性は、モデルで設定された`visible`および`hidden`の設定も尊重する。

## ミューテタの使用方法

ミューテタを使用すると、DBへのデータ保存時にデータを加工することができる。

モデルに`set◯◯Attribute`という名前でメソッドを作成すると、Laravelが自動的にミューテタだと判断してくれる。

この際、メソッド名の`◯◯`の部分に**加工したいカラム名**をアッパーキャメルケースで指定する。

以下は、usersテーブルの `first_name` というカラムへデータを保存する際に、PHPの`strtolower`メソッドを使用して、受け取った文字列を全て小文字に変換する例。

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザーの名前を設定
     *
     * @param  string  $value
     * @return void
     */
    public function setFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = strtolower($value);
    }
}
```

ミューテタは入力された値を `$value` として受け取る。

`$this->attributes['first_name']` でEloquentモデルの `first_name` へアクセスできるので、ここに整形後の値を設定する。

## さいごに

業務でアクセサが使った記述があったのだが、ルールに沿ったメソッド名で定義すればLaravelが自動的にアクセサとして扱ってくれるというものなので、アクセサを知らずにコードを読んでいると中々仕組みに気付けなかった。

だが知っていれば非常に便利なフレームワークの機能なので、今後はこの機能を自分の引き出しの一つとして、有効な場面で使えるように覚えておきたい。

## 参考記事
https://readouble.com/laravel/8.x/ja/eloquent-mutators.html#:~:text=%E3%82%82%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82-,%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B5%EF%BC%8F%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%82%BF,-%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B5%E3%81%AE%E5%AE%9A%E7%BE%A9

https://qiita.com/hitochan/items/9cd9f2cbbbdd35916a96

https://zenn.dev/naoki_oshiumi/articles/6497b8e078b9c5#%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%82%BF%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B

https://wingdoor.co.jp/blog/%E3%80%90laravel%E3%80%91%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B5%E3%81%A8%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%82%BF%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/