プロジェクトでLaravelを十分長く使用している場合、モデルはかなり大きくなる可能性があります。 時間が経つにつれて、維持することがますます難しくなります。 それらは新しい機能で大きくなりすぎています。 モデルが使用されるそれぞれの場合にコードを書くとき、誘惑はモデルが太くなるまで「“せる」ことです。
このような状況では、Decoratorパターンを使用できます。これにより、個別のクラスで各ケースに固有のコードを選択できます。 たとえば、デコレータを使用して、PDFドキュメント、CSV、またはAPI応答のプレゼンテーションを共有できます。
デコレータとは何ですか?プレゼンターとは何ですか?
デコレータは、機能を拡張するために別のオブジェクトをラップするオブジェクトです。 また、デコレータにない場合はラップされているオブジェクトにメソッド呼び出しを渡します。 デコレータは、継承に頼らずにクラスの動作を変更する必要がある場合に役立ちます。 これらを使用して、ロギング、アクセス制御などの追加機能をオブジェクトに追加できます。
プレゼンターは、オブジェクトを目的のビューに移動するために使用されるデコレーターの一種です(たとえば、BladeテンプレートまたはAPI応答用)。
ユーザーのコレクションをAPIレスポンスに取り込む
APIレスポンスで返す必要があるユーザーのコレクションがあるとします。 Laravelでは、コレクション自体を返すだけでこれを簡単に実装できます。コレクションはJSONに変換されます。 コントローラーでユーザーのモデルを取得しましょう:
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; class UsersController extends Controller { public function index() { return User::all(); } }
all
メソッドは、データベースからすべてのユーザーを返します。 ユーザーモデルには、テーブル内のすべてのフィールドが含まれます。 パスワードやその他の重要な情報もそこにあります。 さらに、Laravelは、出力時にall
メソッドの結果をJSONに自動的に変換します。
ただし、これは問題の最善の解決策ではありません。 たとえば、応答でユーザーパスワードハッシュを送信する必要はありません。
また、 created_at
とupdated_at
日付がどのように見えるかが気に入らないかもしれません。 または、 is_active
フィールドがtinyint
型の場合、それを文字列またはブール値に変換することもできます。
注:はい、はい、Eloquentを使用すると、モデルの$ hiddenプロパティを使用してJSONに変換するときにモデルフィールドを非表示にできることがわかります。 私と一緒に遊んでください。
Presenterパターンを使用します。
ユーザーモデルのコレクションができたので、デコレータでモデルをラップしてビューに渡す方法を理解する必要があります。 プレゼンターとして機能するクラスが必要になります。 この場合のUserPresenterクラスは次のようになります。
<?php namespace App\Users; class UserPresenter { protected $model; public function __construct(Model $model) { $this->model = $model; } public function __call($method, $args) { return call_user_func_array([$this->model, $method], $args); } public function __get($name) { return $this->model->{$name}; } public function fullName() { return trim($this->model->first_name . ' ' . $this->model->last_name); } }
プレゼンターにはこれらのプロパティがないため、プレゼンターはモデルのfirst_name
、 last_name
、およびcreated_at
プロパティを受け取ることに注意してください。
私は愚かな類推が大好きで、これもその1つです。デコレーターは、ブルース・ウェインのバットマンの衣装のようなものです。 また、バットマンにはさまざまな状況に合わせたさまざまな衣装があります。 バットマンのコスチュームと同様に、ユーザーモデルが必要なさまざまな状況でさまざまなデコレーターを使用できます。 デコレータの名前をより適切なもの、たとえばApiPresenterに変更して、Presentersフォルダに入れましょう。 また、別のPresenterクラスで再利用できるコードを強調します。
<?php namespace App\Presenter; abstract class Presenter { protected $model; public function __construct(Model $model) { $this->model = $model; } public function __call($method, $args) { return call_user_func_array([$this->model, $method], $args); } public function __get($name) { return $this->model->{$name}; } }
ApiPresenter
新しいメソッドを追加しましょう:
<?php namespace App\Users\Presenters; use App\Presenter\Presenter; class ApiPresenter extends Presenter { 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'); } }
Laravelミューテーターを使用して、日付を必要な形式に変換し、プレゼンターとのこの混乱をすべて回避できると考えるかもしれません。 これは、表示オプションが1つだけ必要な場合に可能です。
また、「 created_at
フィールドをそのままにして、さまざまな状況に応じて複数のpdfCreatedAt()
を使用することもできます。たとえば、 friendlyCreatedAt()
、 pdfCreatedAt()
およびcreatedAtAsYear()
。」と言うこともできます。 このアプローチに対する主な論点は、モデルが徐々に巨大になり、多くの懸念をもたらすことです。 この責任を別のクラスに移して、モデルを適切な種類にすることができます。
プレゼンターにいくつかのメソッドを追加しましょう。
<?php namespace App\Users\Presenters; class ApiPresenter { public function fullName() { return trim($this->model->full_name . ' ' . $this->model->last_name); } public function createdAt() { return $this->model->created_at->format('n/j/Y'); } public function isActive() { return (bool) $this->model->is_active; } public function role() { if ($this->model->is_admin) { return 'Admin'; } return 'User'; } }
ここではis_active
フィールドをtinyint
ではなくブール型にis_active
ます。 ユーザーロールのAPI文字列表現も提供します。
コントローラーに戻りましょう。 プレゼンターを使用して答えを作成できます。
<?php namespace App\Http\Controllers; use App\Users\Presenters\ApiPresenter; use App\Http\Controllers\Controller; 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(), ]); } }
これはすごい! これで、APIは必要な情報のみを返し、コードはよりきれいに見え始めました。 しかし、 ApiPresenter
にはないがUserモデルにある値を使用する場合は、 ApiPresenter
ように、モデルから動的に単純に返すことができます。
<?php return response()->json([ 'first_name' => $user->first_name, 'last_name' => $user->last_name, 'name' => $user->fullName(), 'role' => $user->role(), 'created_at' => $user->createdAt(), 'is_active' => $user->isActive(), ]);
ユーザーコレクションの装飾
デコレータはかなり強力なパターンであり、コードをきれいに整頓できます。 しかし、モデルのコレクションがあった最初の状況はどうでしょうか? ループして新しい配列を作成できます。
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Users\Presenters\ApiPresenter; class UsersController extends Controller { public function index() { $users = User::all(); $apiUsers = []; foreach ($users as $user) { $apiUser = new ApiPresenter($user); $apiUsers[] = [ 'first_name' => $apiUser->model->first_name, 'last_name' => $apiUser->model->last_name, 'name' => $apiUser->fullName(), 'role' => $apiUser->role(), 'created_at' => $apiUser->createdAt(), 'is_active' => $apiUser->isActive(), ]; } return response()->json($apiUsers); } }
すべてが正常ですが、それは非常に美しく見えません。 代わりに、 Collection
クラスで作成できるマクロを使用します。
<?php Collection::macro('present', function ($class) { return $this->map(function ($model) use ($class) { return new $class($model); }); });
このコードは、アプリケーションのサービスプロバイダーに配置できます。 次に、目的のプレゼンターを指定してマクロを呼び出します。
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Users\Presenters\ApiPresenter; class UsersController extends Controller { public function index() { $users = User::all() ->present(ApiPresenter::class) ->map(function ($user) { return [ 'first_name' => $user->first_name, 'last_name' => $user->last_name, 'name' => $user->fullName(), 'role' => $user->role(), 'created_at' => $user->createdAt(), 'is_active' => $user->isActive(), ]; }); return response()->json($users); } }
各モデルはプレゼンターになります。 必要に応じて、コレクションを別のデコレータに転送して、複数のオブジェクトを単一のJSONに結合できます。
したがって、デコレータとプレゼンターは、自由に使える強力なツールです。 記述しやすく、テストも簡単です。 理にかなっているときに使用してください。 リファクタリングのお手伝いをします。
しかし、それだけではありません。 別のモデルに対して現在のメソッドを呼び出すことができれば、クールです。 そして、プレゼンターでモデルをラップできるヘルパーがあれば。
さて、 ヘンプ/プレゼンターパッケージを紹介します。 彼は私たちが話したことすべてを行い、さらに私が話したことをすべて叶えます。 そして、これはすべてテストされています。 それを試して、あなたがそれについてどう思うか教えてください。 お楽しみください!
オリジナル: http : //davidhemphill.com/blog/2016/09/06/presenters-in-laravel