Laravelのアクセサとミューテタを用いてEloquentモデルの属性フォーマットを定義する
- 公開:
- 更新:
- カテゴリ: PHP Laravel
- タグ: PHP,Laravel,EloquentORM,Model,Mutators,Accessors
LaravelのEloquentには「アクセサ」と「ミューテタ」というものがあります。これらを使うと、EloquentORM、つまりはモデルでのデータ操作を行う際に、予めデータの形式を定義しておく事が出来ます。
今回はLaravelのアクセサとミューテタを用いてEloquentの属性フォーマットを定義していきます。
開発環境
今回の開発環境は以下の通りです。
- Linux CentOS 7
- Apache 2.4
- 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.
https://carbon.nesbot.com/
この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
上記のリストを見てなんとなく気が付いたかもしれませんが、キャストはただカラムからの取得フォーマットを変えるだけではなく、オブジェクトやコレクション、配列と言った形にもキャストできます。
まとめ
ミューテタやアクセサを上手く使いこなすとロジックの中で値を変換しなくて済むのでソースコードも綺麗になります。手間も省けるので是非試してみてください。