Laravelの標準では、「app/Models/」にLaravelのORマッパーである Eloquent(エロクアント) のモデルを配置します。 要はデータベースのテーブルと対になるオブジェクトです…
リポジトリとは、データアクセス機能を一元管理することで保守性を向上させ、ドメインとデータアクセスを切り離すことを目的にしたクラスです。
切り離すことでドメインが簡潔になり、ユニットテストもデータソースへのアクセスが別れるので行いやすくなります。
連載
本記事は複数記事の連載記事の1つです。
この記事に関連するコミット
VSCodeにLaravelを入力補完して貰う
Laravelのファサードやモデルは、そのままではVSCodeは入力補完ができません。
「User::」まで入力したら、コールできるメソッド群を表示して貰いたいですが、表示されません。
そこで、「barryvdh/laravel-ide-helper」パッケージをcomposerから入れます。
このパッケージはFacadeや存在しているモデルやデータベースコネクションを使って入力補完用の定義がぎっしり詰まった定義ファイルを作ってくれます。
このファイルが存在しているだけで、VSCodeは入力補完を行えるようになります。
インストール
composer require --dev barryvdh/laravel-ide-helper
ファサードの定義ファイルを作成する
artisanコマンドに追加されるので、以下のコマンドを実行してルートディレクトリに「_ide_helper.php」が作成されればOKです。
php artisan ide-helper:generate
モデルの定義ファイルを作成する
モデルは以下のコマンドを実行して「_ide_helper_models.php」を作成します。
このモデルファイルは、現時点で存在している状態
で作成されます。
マイグレーションの追加や変更をした場合は再実行する必要があります。
php artisan ide-helper:model --nowrite
<?php namespace Packages\Infrastructures\Persistance\Orders; use App\Models\OrderModel; use Illuminate\Support\LazyCollection; use Packages\Domains\Orders\Order; class OrderRepository { public function find(int $orderId): Order { return OrderModel::find($orderId)->toEntity(); } /** * @return LazyCollection<Order> */ public function fetchAll(): LazyCollection { return OrderModel::cursor() ->map(fn (OrderModel $m) => $m->toEntity()) ; } /** * @return int オートインクリメント採番されたID */ public function insert(Order $order): int { return $order->toModel()->create()->id; } /** * @return boolean 更新成功:true / 失敗:false */ public function update(Order $order): bool { return $order->toModel()->save(); } }
Eloquentでデータアクセスするため、OrderModelを呼び出します。
当然、データベースから取得されたデータはOrderModelにマッピングされて返されますが、これをそのまま呼び出し元のレイヤーへ返してはいけません。
モデルの記事で述べていたように、EloquentのModelsを継承したOrderModelをアプリケーション層へ返すと何でも出来てしまいます。
そのため、OrderRepositoryへ引数で渡すときはOrder
クラス、返却するときもOrder
クラスとします。
※DDD本来はレイヤー間を横断しているので、インターフェースに依存させるためにクラスではなくインターフェースを指定しますが、今回は指定していません。
PHPはJavaのJARやC#のDLLといったパッケージ相当の機能が弱く、スコープのinternal
なども存在しません。
Composerでレイヤーごとにパッケージを分けられなくはないですが、書き換えるたびに更新が必要で現実的ではなく、internalスコープもないので細かく制御することも出来ないため、PHPらしく緩い感じになっています。
サンプルメソッドなので必ずこれを揃える必要はなく、要件で必要なものだけでOKです。
find
IDを指定して1件を取得します。
ポイントはtoEntity
メソッドでModelからEntityに変換して返却しています。
fetchAll
全件を取得します。
Eloquentではcursor
メソッドで取得することでクエリが遅延実行されます。
奥が深いのでここでは説明を省きますが、大量件数を取得する場合はget
ではなくcursor
で取得するとメモリ使用量が節約できます。
また、PHPDocに@return LazyCollection<Order>
のようにコレクションと内包する型を記載することで、VSCodeの補完が効きます。
insert
新しいレコードを追加します。
永続化したいデータは必ずOrderクラス
を引数にして、そのまま永続化します。
今回はIDをオートインクリメントで振っており、データを追加した時点で初めて採番されるのでIDを返却します。
update
既存のレコードを更新します。
insertと同様にOrderクラスを引数にして、更新します。
実案件ではもっと複雑なクエリが必要なんだけど?
複雑なクエリが必要になるケースは主に参照系がほとんどと言われています。
様々な条件で検索したり、複数のテーブルを結合して一覧を生成するなどが挙げられます。
これらはデータストアの読み取りと更新を分離するCQRSパターンで別途対応します。
コメントを書く