私は定期的に「Eloquentでリポジトリテンプレートを使用する方法」というスタイルの記事を見ています(そのうちの1つは最近のPHPダイジェストに入りました )。 通常のコンテンツ:インターフェースPostRepositoryInterface 、 EloquentPostRepositoryクラスを作成し、それらを依存関係コンテナーにうまくバインドし、標準のEkoventメソッドの代わりにsaveとfindを使用しましょう。
なぜこのテンプレートが必要なのか、時にはまったく書かない(「これはテンプレートです!十分ではないのですか?」) そのようなテンプレートをそのような記事の通常のLaravelプロジェクトに導入することの利点は理解するのが困難です。
何が何であるかを理解してみましょうか? リポジトリテンプレートを使用すると、特定のストレージシステム(通常はデータベースとして使用)から抽象化して、エンティティのコレクションの抽象概念を提供できます。
Eloquent Repositoryの例は、2つのタイプに分けられます。
- デュアルEloquentアレイのバリエーション
- 純然たるリポジトリ
デュアルEloquentアレイのバリエーション
最初の例(ランダムな記事から引用):
<?php interface FaqRepository { public function all($columns = array('*')); public function newInstance(array $attributes = array()); public function paginate($perPage = 15, $columns = array('*')); public function create(array $attributes); public function find($id, $columns = array('*')); public function updateWithIdAndInput($id, array $input); public function destroy($id); } class FaqRepositoryEloquent implements FaqRepository { protected $faqModel; public function __construct(Faq $faqModel) { $this->faqModel = $faqModel; } public function newInstance(array $attributes = array()) { if (!isset($attributes['rank'])) { $attributes['rank'] = 0; } return $this->faqModel->newInstance($attributes); } public function paginate($perPage = 0, $columns = array('*')) { $perPage = $perPage ?: Config::get('pagination.length'); return $this->faqModel ->rankedWhere('answered', 1) ->paginate($perPage, $columns); } public function all($columns = array('*')) { return $this->faqModel->rankedAll($columns); } public function create(array $attributes) { return $this->faqModel->create($attributes); } public function find($id, $columns = array('*')) { return $this->faqModel->findOrFail($id, $columns); } public function updateWithIdAndInput($id, array $input) { $faq = $this->faqModel->find($id); return $faq->update($input); } public function destroy($id) { return $this->faqModel->destroy($id); } }
all 、 find 、 paginateメソッドは Eloquentオブジェクトを返しますが、 create 、 updateWithIdAndInputは配列を待っています。
updateWithIdAndInputという名前自体は 、この「リポジトリ」がCRUD操作にのみ使用されることを意味します。
通常のビジネスロジックは想定されていませんが、最も単純なものを実装しようとします。
<?php class FaqController extends Controller { public function publish($id, FaqRepository $repository) { $faq = $repository->find($id); //...- $faq->... $faq->published = true; $repository->updateWithIdAndInput($id, $faq->toArray()); } }
そして、リポジトリがない場合:
<?php class FaqController extends Controller { public function publish($id) { $faq = Faq::findOrFail($id); //...- $faq->... $faq->published = true; $faq->save(); } }
2倍簡単。
なぜ複雑にするだけの抽象化をプロジェクトに導入するのですか?
- ユニットテスト?
通常のLaravel CRUDプロジェクトは100%強のユニットテストでカバーされていることは誰もが知っています。
ただし、ユニットテストについては後ほど説明します。 - データベースを変更できるようにするためですか?
しかし、Eloquentはすでにいくつかのデータベースオプションを提供しています。
CRUDロジックのみを含むアプリケーションのサポートされていないベースにEloquentエンティティを使用すると、苦痛と時間の無駄になります。
この場合、純粋なPHP配列を返し、配列のみを受け入れるリポジトリは、より自然に見えます。
Eloquentを削除することにより、データウェアハウスから実際の抽象化を取得します。
純然たるリポジトリ
Eloquentのみで動作するリポジトリの例(1つの記事にもあります):
<?php interface PostRepositoryInterface { public function get($id); public function all(); public function delete($id); public function save(Post $post); } class PostRepository implements PostRepositoryInterface { public function get($id) { return Post::find($id); } public function all() { return Post::all(); } public function delete($id) { Post::destroy($id); } public function save(Post $post) { $post->save(); } }
この記事では、不要な接尾辞Interfaceを scりません。
この実装は、テンプレートの説明の内容に少し似ています。
最も単純なロジックの実装は、もう少し自然に見えます。
<?php class FaqController extends Controller { public function publish($id, PostRepositoryInterface $repository) { $post = $repository->find($id); //...- $post->... $post->published = true; $repository->save($post); } }
ただし、最も簡単なブログ投稿用のリポジトリを実装することは、子供たちが楽しむためのおもちゃです。
もっと複雑なものを試してみましょう。
サブエンティティを持つ単純なエンティティ。 たとえば、可能な回答を含む調査(サイトまたはチャットでの定期的な投票)。
そのような調査の対象を作成する場合。 2つのオプション:
- PollRepositoryとPollOptionRepositoryを作成し、 両方を使用します。
このオプションの問題は、抽象化がうまくいかなかったことです。
回答が可能な調査は1つのエンティティであり、データベース内のそのストレージは1つのPollRepositoryクラスによって実装されている必要があります。
PollOptionRepository :: deleteは簡単ではありません。なぜなら、この回答オプションを削除できるかどうかを知るためにPollオブジェクトが必要になるからです(世論調査に選択肢が1つしかない場合は世論調査ではありません)。
また、リポジトリテンプレートは、リポジトリ内のビジネスロジックの実装を意味するものではありません。 - PollRepository内にsaveOption メソッドとdeleteOptionメソッドを追加します 。
問題はほとんど同じです。 ストレージからの抽象化は、ある種のスカムバッグであることが判明します...答えのオプションは個別に取る必要があります。
しかし、本質がさらに複雑な場合はどうでしょうか? 他のサブエンティティがたくさんありますか?
同じ疑問が生じます。なぜこれがすべてなのでしょうか?
Eloquentが提供するよりも多くの抽象化をストレージシステムから取得します-動作しません。
ユニットテスト?
これが私の本からの可能な単体テストの例です-https://gist.github.com/adelf/a53ce49b22b32914879801113cf79043
単純な操作のためにこのような巨大な単体テストを行うことは、ほとんどの人にはわからないでしょう。
私は、プロジェクト内のそのようなテストが中止されることをほぼ確信しています。
誰も彼らをサポートしたくありません。 私はそのようなテストのあるプロジェクトに参加していました。
機能テストに集中する方がはるかに簡単で正確です。
特にAPIプロジェクトの場合。
ビジネスロジックが非常に複雑で、テストでカバーしたい場合は、 Doctrineのようなデータマッパーライブラリを使用して、ビジネスロジックを他のアプリケーションから完全に分離することをお勧めします。 単体テストは10倍簡単になります。
あなたのEloquentプロジェクトで、デザインパターンに夢中になりたい場合は、次の記事でリポジトリテンプレートを部分的に適用し、それから利益を得る方法を示します。