何時間もの調査と思考の結果、私は理解を見つけ、それを再び疑問視するようになりました。私は、具体的な何かを見つけ、実用的な意味で私のコードに適用できるようになるのに苦労しています。
プレゼンテーションロジックとドメインモデルを参照していると思います。私は Anemic Domain Model などの記事を多数読んだことがありますが、正しい解決策が何であるかまだ完全には理解していません。私を困惑させている実際のシナリオの1つから始めます。
10の異なるモデルからのデータを表示する必要があるWebフォームがあるとします。各モデルから、特定のデータセットを取得する必要があります。この特定のビューにのみ役立ちます。たとえば、特定のオプションが含まれている多数のドロップダウンメニューなどです。
1。コントローラーに任せる
疑似コード:
/**
* Controller action
*
* Process a request for the specific view.
*/
public function myView()
{
$options1 = $model1->query(
// Get fields A, B, C from the database
// Order the data by a certain field
// Exclude data which does not meet conditions
// Process the data some more
);
$options2 = $model2->query(
// Get fields X, Y, Z from the database
// Order the data by a certain field
// Exclude data which does not meet conditions
// Process the data some more
);
// ...repeat this sort of thing 8 more times
$this->sendToView($options1, $options2, ...);
}
したがって、コントローラーは、ビューが必要とする形式とまったく同じ形式で、ビューが必要とするデータをさまざまなモデルエンティティに要求します。
ある意味では、モデルレイヤーはビューを認識しておらず、コントローラーがビュー間のギャップを埋める責任を負うため、これは私には良いように見えます。ビューに表示されるものに関するすべてのロジックは、そのビューに直接結合されているアプリケーションの唯一のメソッドによって処理されます。
ただし、これは私のコントローラーでは簡単に200行になる可能性があり、これはone固有のビューの場合のみです。間もなく、すべてのコントローラーは、各ビューの特定のデータに対するモデルへの数十回の呼び出しの巨大な乱雑な混乱になるでしょう。異なる呼び出しをコントローラー上の他のメソッド(プライベートなど)に分割して、モジュール化/管理しやすくすることができますが、何が重要なのでしょうか。
結論:コントローラーを薄くして、他の場所に委任してください。
2。モデルレイヤーにそれをさせましょう
疑似コード:
/**
* Controller action
*
* Process a request for the specific view.
*/
public function myView()
{
$options1 = $model1->getOptionsForAbc();
$options2 = $model2->getOptionsForXyz();
// ...repeat for 8 more models
}
/**
* Model 1
*/
public function getOptionsForAbc()
{
$options = $this->query(
// Get fields A, B, C from an associated table
// Order the data by a certain field
// Exclude data which does not meet conditions
);
return $options;
}
// Each model has similar methods which retrieve specific sets of data
そのため、コントローラーはすっきりしていて、どのロジックにも関係していません。モデルに必要なものを要求するだけで、モデルはそれを取得する方法を知っています。
ここでは「モデル」という用語を大まかに使用しています。モデルは多くのものからなるレイヤーであることはよく理解しています。
これの問題は、モデルが最終的に多くの機能を持つことになり、おそらく1つの特定のビューにのみ役立つでしょう。その1つのビューだけがその特定のデータのサブセットを必要とするため、他の場所では使用されません。これは私には正しくないようです。
3。サービス/ビジネスロジックレイヤーの実装
すべてのビジネスロジックが実際にモデルに含まれるべきだと思います。計算、検証、データに発生するその他の通常のビジネスロジックについては、これを理解しています。
しかし、特定のデータを必要とするビューのこの特定の例では何が起こるでしょうか?ビューに必要なすべてのデータを正しい形式で配信できるフォームを表すサービスクラスを作成する必要がありますか?それは正しくないようです。
最後の質問:では、どこにすべきかこの種類のデータ関連のプレゼンテーションロジックのみを保存します。ビューのために?
ビューにフォーマットを実行させます。それが目的です。コントローラの仕事はそれに任意のデータを供給することです。 MVCはパターンであり、具体的なものではありません。ビューが「スマート」であることには何の問題もありません。
検証はモデルに属します。コントローラーはモデルでユーザーデータを直接押し出し、無効なものを拒否する(たとえば、例外をスローする)のはモデルの仕事です。
書式設定コードをコピー貼り付けしている場合は、再利用可能な静的メソッドを使用してヘルパークラスを作成するか、継承されたメソッドを使用してビュークラス階層を作成するか、特性を使用します。
MVCの良い点は、さまざまな実装が無数にあるということです。あなたが十分に一見したなら、あなたがたどり着くあらゆるアプローチに一致するものを見つけることができると私は確信しています。私にとって、Webアプリケーションが要求/応答パターンに従っているという認識で、MVCを気にすることの専制から解放されました。 http://symfony.com/doc/current/introduction/http_fundamentals.html
あなたのコントローラーは本当にフォームコントロールの選択オプションを気にしていますか?もちろん違います。そのため、コントローラーがオプションを取得してビューに渡すだけの理由はありません。ビューに任せてください。ビューが必要とするオプションを決定する際に、いくつかのビジネスロジックはありますか?承知しました。したがって、あなたは本当にあなたのビューに骨の折れるロジックを埋め込むことを望んでいません。
だから次のようなもの:
// Inside of the view
$this->gameOfficialChoices = array_merge(
[null => 'Select Game Official'],
$this->assignorFinder->findGameOfficialChoices($game)
);
上記のコードでは、特定のゲームを担当する資格があるゲーム担当者を知る必要があります。実際にはかなり多くのビジネスロジックが実行されているため、実際のルックアップをAssignorFinderサービスに移動します。
この種のアプローチは私にとってはうまくいくようです。 MVCですか?分からない。気にしないで。
ビジネス層をサーバー側のMVCアプリケーションとして扱うことができ、プレゼンテーション層全体をクライアント側の別のMVCアプリケーションにすることができます(必須ではありません)。クライアント側のアプリケーションは、非同期でコントローラーにリクエストを送信し、コントローラーはリクエストをモデルに中継し、結果をプレゼンテーションレイヤーに転送します。これにより、クライアントが有効になります
個人的には、トランザクションなどのシナリオは別として、クライアント側で集約することを好みます。すべてのコンポーネントが軽量でシンプルになり、懸念が適切に分離および委任されているかのように感じます。