1. Home
  2. PHP
  3. Laravel
  4. Laravelのアクセサとミューテタを用いてEloquentモデルの属性フォーマットを定義する

Laravelのアクセサとミューテタを用いてEloquentモデルの属性フォーマットを定義する

  • 公開日
  • 更新日
  • カテゴリ:Laravel
  • タグ:PHP,Laravel,Mutators,Accessors
Laravelのアクセサとミューテタを用いてEloquentモデルの属性フォーマットを定義する

Laravel の Eloquent には「アクセサ」と「ミューテタ」というものがあります。これらを使うと、EloquentORM, つまりはモデルでのデータ操作を行う際に、予めデータの形式を定義しておく事が出来ます。

今回は Laravel のアクセサとミューテタを用いて Eloquent の属性フォーマットを定義していきます。

Contents

  1. 開発環境
  2. アクセサ
  3. ミューテタ
  4. 日付ミューテタ
    1. デフォルトの日付フォーマット
  5. 属性のキャスト

開発環境

今回の開発環境は以下の通りです。

  • MySQL 8.0
  • PHP 7.3
  • Laravel 5.8

Laravel プロジェクトのルートディレクトリを「laravel/」としています。

データ取得結果については、確認しやすいように適宜変換しています。

アクセサ

アクセサの定義を行うと、予め取得するカラムのフォーマットを定義できます。

例えば苗字(first_name)と名前(last_name)を収録したカラムがあるとして、これらについてアクセサを定義します。

アクセサの定義は、モデルクラスに行います。

laravel/app/Member.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Member extends Model
{
    public function getFirstNameAttribute($value)
    {
        return ucfirst($value); // 頭文字を大文字にする
    }

    public function getLastNameAttribute($value)
    {
        return ucfirst($value); // 頭文字を大文字にする
    }
}

これで何を定義しているのかというと、first_name カラムと last_name カラムへアクセスした際に取得できる「値のフォーマット」を定義しています。

尚、メソッド名には規則があり、
get[カラム名(パスカルケース)]Attribute
という命名規則になっています。

つまり、first_name カラムへの定義の場合は getFirstNameAttribute() のようになります。

両メソッドで ucfirst() メソッドを定義していますが、これは英字に対して頭文字を大文字に変換するものです。

ちなみに今回用いるサンプルのデータベースには英字の名前が全て小文字で収録されており、この定義の場合は、first_name ・ last_name 共に、頭文字が大文字になった状態で取得できるという事になります。

mysql> SELECT `id`, `first_name`, `last_name` FROM `members`;
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
|  1 | drew       | luettgen  |
|  2 | sienna     | deckow    |
|  3 | maci       | rohan     |
+----+------------+-----------+

コントローラから取得します。

laravel/app/Http/Controllers/SampleController.php
<?php

namespace App\Http\Controllers;

use App\Member;

class SampleController extends Controller
{
    public function index()
    {
        $members = Member::select(['id','first_name','last_name'])->get();

        // => Array
        //(
        //    [0] => Array
        //        (
        //            [id] => 1
        //            [first_name] => Drew
        //            [last_name] => Luettgen
        //        )
        //
        //    [1] => Array
        //        (
        //            [id] => 2
        //            [first_name] => Sienna
        //            [last_name] => Deckow
        //        )
        //
        //    [2] => Array
        //        (
        //            [id] => 3
        //            [first_name] => Maci
        //            [last_name] => Rohan
        //        )
        //
        //)
    }
}

上記の通り、モデルにアクセサを定義しておく事で、定義した通りのフォーマットで取得できるようになります。

また、既存のカラムを用いて、オリジナルの値を定義する事もできます。

laravel/app/Model/Member.php
<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Member extends Model
{
    public function getFullNameAttribute()
    {
        // full_name として返す
        return "{$this->first_name} {$this->last_name}";
    }
}

first_name カラムと last_name カラムの値を合わせて、full_name を作成しています。テーブルには full_name というカラムは存在していませんが、これで $this->full_name としてアクセスできるようになります。

コントローラから取得します。

laravel/app/Http/Controllers/SampleController.php
$members = Member::all();

$names = [];
foreach ($members as $member) {
    $names[] = $member->full_name;
}

// => Array
//(
//    [0] => Drew Luettgen
//    [1] => Sienna Deckow
//    [2] => Maci Rohan
//)

あたかも full_name カラムが存在しているかのように取得が行えました。

ちなみに前半で定義した頭文字を大文字にするアクセサをそのまま定義しておくとそれも適用されます。

ミューテタ

ミューテタはアクセサの逆のようなものです。アクセサでは取得の際の定義を行いましたが、ミューテタは値を代入する際のフォーマットを定義します。

今回のサンプルデータベースには英字の名前が収録されていますが、新たなレコードを挿入する際もやはりもれなく小文字で収録したいです。

そこで、モデルクラスにてミューテタを定義します。

laravel/app/Member.php
public function setFirstNameAttribute($value)
{
    // 全て小文字でセットする
    $this->attributes['first_name'] = strtolower($value);
}

public function setLastNameAttribute($value)
{
    // 全て小文字でセットする
    $this->attributes['last_name'] = strtolower($value);
}

アクセサは get プレフィックスだったのに対して、ミューテタは set プレフィックスです。命名規則はアクセサと同じです。

ここでは、strtolower() メソッドを用いて、全てを小文字にしています。

コントローラから値をセットし、保存してみます。

laravel/app/Http/Controllers/SampleController.php
$member = new Member();
$member->first_name = 'Taylor';
$member->last_name = 'Swift';
$member->save();

保存結果を見てみます。

mysql> SELECT `id`, `first_name`, `last_name` FROM `members` WHERE id = 4;
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
|  4 | taylor     | swift     |
+----+------------+-----------+

セット時には頭文字を大文字で代入しましたが、保存時にはミューテタの定義通り、小文字で保存されている事が確認できます。

日付ミューテタ

Laravel では、created_at(作成日)カラムと updated_at(更新日)カラムに対して、Eloquent での取得の際に自動的に Carbon オブジェクトにて返却されます。

Carbon は日時を操作する PHP のパッケージで、PHP の DateTime クラスを継承しています。(動作には PHP 5.4 以降が必要)

Carbon - A simple PHP API extension for DateTime.

この Carbon インスタンスを用いて入力時の日時フォーマットを定義できます。 created_at と updated_at 以外で Carbon オブジェクトとして扱いたいカラムがある場合は、モデルクラスに追加します。

laravel/app/Member.php
class Member extends Model
{
    protected $dates = [
        'added_on'
    ];

取得時の違いは以下のようになります。

$member = Member::find(1);

// print_r($member->added_on);

// 指定しない場合
// 2019-08-27 00:09:04

// 指定した場合
// Illuminate\Support\Carbon Object
//(
//    [date] => 2019-08-27 00:09:04.000000
//    [timezone_type] => 3
//    [timezone] => UTC

Carbon オブジェクトなので、形式を自在に形を変化させる事も可能です。

$member->added_on
$member->added_on->getTimestamp() // => UnixTimestamp
$member->added_on->subYear(10)    // 10 年前
$member->added_on->addYear(10)    // 10 年後
$member->added_on->subMonth(10)   // 10 ヵ月前
$member->added_on->addMonth(10)   // 10 ヵ月後
$member->added_on->subWeek(10)    // 10週間前
$member->added_on->addWeek(10)    // 10週間後
$member->added_on->subDays(10)    // 10 日前
$member->added_on->addDays(10)    // 10 日後
$member->added_on->subHours(10)   // 10 時間前
$member->added_on->addHours(10)   // 10 時間後

デフォルトの日付フォーマット

created_at/updated_at 並びにモデルで $dates として指定したカラムについて、その時に挿入する日時のフォーマットを変更する事が出来ます。

デフォルトのタイムスタンプのフォーマットは Datetime 型 Y-m-d H:i:s ですが、それを変更したい場合は以下のようにモデルに設定します。

laravel/app/Member.php
class Member extends Model
{
    protected $dateFormat = 'U'; // 日時フォーマットをUnixTimestampとする

メンバ変数 $dateFormat に任意のフォーマットを設定します。ちなみに上記は UnixTimestamp の設定です。ただしこの場合はカラムを数値型にする必要があります。

// $table->timestamp('added_on');
// 変更
$table->integer('added_on');

// $table->timestamps();
// 変更
$table->integer('updated_at');
$table->integer('created_at');

値をセットし、保存します。

laravel/app/Http/Controllers/SampleController.php
$member = new Member();
$member->first_name = 'Winston';
$member->last_name = 'Churchill';
$member->added_on = now();
$member->save();

結果は以下になります。

mysql> SELECT * FROM `members` WHERE id = 4;
+----+------------+-----------+------------+------------+------------+
| id | first_name | last_name | added_on   | updated_at | created_at |
+----+------------+-----------+------------+------------+------------+
|  4 | winston    | churchill | 1566865930 | 1566865930 | 1566865930 |
+----+------------+-----------+------------+------------+------------+

Unix Timestamp で登録できている事が確認できました。

次は、DATE フォーマットで試してみます。

protected $dateFormat = 'Y-m-d'; // 日時フォーマットを DATE とする

日時カラムを date 型で作成するので、マイグレーションを以下へ変更します。

$table->date('added_on');

$table->date('updated_at');
$table->date('created_at');

コントローラなどで値をセットし、保存します。記述は先ほどと同じです。そして、結果は以下になります。

mysql> SELECT * FROM `members` WHERE id = 4;
+----+------------+-----------+------------+------------+------------+
| id | first_name | last_name | added_on   | updated_at | created_at |
+----+------------+-----------+------------+------------+------------+
|  4 | winston    | churchill | 2019-08-27 | 2019-08-27 | 2019-08-27 |
+----+------------+-----------+------------+------------+------------+

date 型で登録できている事が確認できました。

属性のキャスト

モデルにメンバ変数 $casts を設定する事で、任意のカラムに対して属性のキャストを行う事が出来ます。ここで言う「キャスト」とは、データ取得の際にそのカラムのフォーマットを変更する事です。

例えば、datetime 型のカラムを date 型としてここに設定すれば、データ取得時に date 型で受け取る事が出来ます。

laravel/app/Member.php
class Member extends Model
{
    protected $casts = [
        'added_on' => 'date', // added_onはいつでもDATE型で取得する
    ];

上記の場合は、datetime 型の added_on カラムに対して date 型で取得するようにキャストしています。取得してみると結果は以下のようになります。

$member = Member::find(1);

// print_r($member->added_on);
// => Illuminate\Support\Carbon Object
//(
//    [date] => 2019-08-27 00:00:00.000000
//    [timezone_type] => 3
//    [timezone] => UTC
//)

時間の部分が丸まっているのが確認できます。

このようにして、 $casts プロパティでは以下へのキャストを行う事が出来ます。(変更するカラムに対してキャスト可能な型を指定)

  • integer
  • real
  • float
  • double
  • string
  • boolean
  • object
  • array
  • collection
  • date
  • datetime
  • timestamp

上記のリストを見てなんとなく気が付いたかもしれませんが、キャストはただカラムからの取得フォーマットを変えるだけではなく、オブジェクトやコレクション、配列と言った形にもキャストできます。

まとめ

ミューテタやアクセサを上手く使いこなすとロジックの中で値を変換しなくて済むのでソースコードも綺麗になります。手間も省けるので是非試してみてください。

サンプルコード

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級