PHPではDB(データベース)に格納された値をwebページ上にいい感じに表示する、あるいはAPIとしていい感じのフォーマットの値を返す、目的のコードがよく書かれます。こういった時、DB上の値がDB上の時点で既にいい感じになっていないことはよくあります。日時に回りは特にこれに当てはまり、特に日本語の日時表記は案件毎にフォーマットが変わると言っても過言ではありません。
必然、DB中のテーブルと密結合させたORM(オブジェクト関係マッピング)モデルにDB中の値からwebに投げるための値に変換する処理を記述します。例はモデルですが、どこかしら処理を一か所にまとめて同じメソッドないし関数を呼び出すようにしない場合、一貫した表記の実現がとても手間になり誤りも起きやすくなります。一か所にまとめないとまずいのですが、値の変換処理を片っ端からモデルに詰め込むと度々メソッド数が膨れ上がりコードの見通しが悪くなります。これを解決するためにプレゼンターがあります。以下のリンクがとてもとても詳しいです。
View Presenters in Laravel – David Hemphill
プレゼンターはDI(依存性注入)を使用することによってextendsすることなく、モデルの持つ値を参照、変換します。上記リンクで紹介されているプレゼンターの抽象クラスは以下です。
<?php
namespace App\Presenter;
/**
* 参照に関しての継承もどきをextendsすることなく実現させる
*/
abstract class Presenter
{
protected $model;
/**
* 自身のプロパティからModelを参照できるようにする。
*/
public function __construct(Model $model)
{
$this->model = $model;
}
/**
* 自身の持たないメソッドが呼び出された場合、プロパティに持つModelにそのメソッドを実行させる
*/
public function __call($method, $args)
{
return call_user_func_array([$this->model, $method], $args);
}
/**
* 自身の持たないプロパティが呼び出された場合、プロパティに持つModelにそのプロパティを返させる
*/
public function __get($name)
{
return $this->model->$name;
}
}
こうすると次のように
<?php
namespace App\Users\Presenters;
use App\Presenter\Presenter;
/**
* 変換の定義クラス。マジックメソッドを知らないメンバーでも問題なくコーディングできる。
*/
class ApiPresenter extends Presenter
{
// プロパティに型のヒントをつけるとIDEでのコーディングがはかどる
/** @var User $model */
protected $model;
public function fullName()
{
return trim($this->model->first_name . ' ' . $this->model->last_name);
}
public function createdAt()
{
return $this->model->created_at->format('n/j/Y');
}
}
<?php
namespace App\Http\Controllers;
use App\Users\Presenters\ApiPresenter;
use App\Http\Controllers\Controller;
/**
* 使用例。プレゼンターをかませた$userは多彩な表現メソッドを得る。
*/
class UsersController extends Controller
{
public function show($id)
{
$user = new ApiPresenter(User::findOrFail($id));
return response()->json([
'name' => $user->fullName(),
'role' => $user->role(),
'created_at' => $user->createdAt(),
'is_active' => $user->isActive()
]);
}
}