MVC のベストプラクティス ================= モデル・ビュー・コントローラ (MVC) は、ほとんど全てのウェブ開発者が知っている言葉ですが、実際のアプリケーション開発において MVC を正しく使う方法を知らない人がいまだに沢山います。 MVC の背後にある中心的なアイデアは、**コードの再利用性と関心事の分離** です。 この節では、Yii アプリケーションを開発するときにより良く MVC を守るための一般的なガイドラインをいくつか説明します。 ガイドラインをより良く説明するために、ウェブアプリケーションは以下のようないくつかのサブアプリケーションから構成されるものと仮定します。 * フロントエンド: 一般のエンドユーザのための公衆に向けられたウェブサイト。 * バックエンド: アプリケーションを管理するための機能を提供するウェブサイト。これは、通常、管理スタッフにのみアクセスを許すものです。 * コンソール: アプリケーション全体をサポートするために、ターミナルウィンドウで走らせたり、スケジュールされたジョブとして実行したりする一群のコンソールコマンドから構成されるアプリケーション。 * ウェブ API: アプリケーションとの統合を可能にするためにサードパーティに向けて提供するインタフェース。 サブアプリケーションは、[モジュール](/doc/guide/basics.module) の形式で実装されることもあれば、または、他のサブアプリケーションと何らかのコードを共有する Yii アプリケーションとして実装されることもあります。 モデル ----- [モデル](/doc/guide/basics.model) は、ウェブアプリケーションの基礎になるデータ構造を表します。例えば、`LoginForm` モデルは、アプリケーションのフロントエンドとバックエンドの両方で使われるでしょう。`News` モデルは、アプリケーションのコンソールコマンド、ウェブ API、フロントエンド、バックエンドで使われるでしょう。従って、 * モデルは特定のデータを表現するプロパティを持たなければなりません。 * モデルは、モデルが表しているデータが設計の要求を満たすことを保証するために、ビジネスロジック (例えば、検証ルール) を持たなければなりません。 * モデルはデータを操作するためのコードを持つことが出来ます。例えば、`SearchForm` モデルは、検索入力データを表現する他に、実際の検索を実装するために `search` メソッドを持つことが出来ます。 時として、上記の最後のルールに従ったために、単一のクラスに多くのコードを詰めすぎて、モデルが肥大化することがあります。 また、目的の違うコードを含んでいる場合も、モデルの保守が困難になり得ます。 例えば、`News` モデルは、フロントエンドでのみ使用される `getLatestNews` というメソッドを持つ一方で、同時に、バックエンドでのみ使用される `getDeletedNews` というメソッドも持つことが有り得ます。 小規模から中規模のアプリケーションでは、これでも構わないでしょう。 大規模なアプリケーションでは、モデルの保守性を高めるために、以下の方式を採用することが出来ます。 * `NewsBase` というモデルクラスを定義して、異なるサブアプリケーション (例えば、フロントエンドとバックエンド) で共有されるコードだけを含ませます。 * 各サブアプリケーションで、`NewsBase` を継承する `News` モデルを定義します。サブアプリケーションに特有のコードはすべてこの `News` モデルに置きます。 ですから、上記の例に対してこの方式を採用するとなると、フロントエンドのアプリケーションには `getLatestNews` メソッドだけを持つ `News` モデルを追加し、バックエンドのアプリケーションには `getDeletedNews` メソッドだけを持つ別の `News` モデルを追加することになります。 一般に、モデルはエンドユーザと直接に関係するロジックを持つべきではありません。もっと具体的に述べると、 * モデルは、`$_GET`、`$_POST`、その他、エンドユーザのリクエストと直接に結びついた同様の変数を使用してはいけません。 モデルを使用するサブアプリケーションには、このようなユーザのリクエストを表す変数を使用しないもの (例えば、ユニットテストやウェブ API) もあり得ることを憶えておいて下さい。 ユーザのリクエストに付属するこれらの変数は、コントローラで取り扱うべきです。 * モデルに HTML や他の表示用のコードを埋め込むことは避けるべきです。 表示用のコードはエンドユーザの要求によって異なります (例えば、フロントエンドとバックエンドでは、全く違う形式でニュースの詳細を表示することが有り得ます) から、 ビューによって取り扱う方が適切です。 ビュー ---- [ビュー](/doc/guide/basics.view) は、エンドユーザが望む形でモデルを表示する役割を持ちます。一般的に、 * ビューは、主として表示用のコードを持ちます。すなわち、HTML や、データを取得・書式化・表示する簡単な PHP コード等です。 * ビューでは、明示的に DB クエリを実行するコードは避けるべきです。そのようなコードはモデルに置くのがより適切です。 * ビューは、`$_GET`、`$_POST`、その他、エンドユーザのリクエストを表す同様の変数に直接アクセスしてはいけません。それはコントローラの仕事です。 ビューは、コントローラまたはモデルによって提供されたデータの表示とレイアウトに専念すべきであり、リクエストの変数やデータベースに対して直接のアクセスを試みてはいけません。 * ビューはコントローラやモデルのプロパティやメソッドに直接アクセスすることが出来ます。ただし、それは、表示が目的である場合に限らなければなりません。 ビューはさまざまな方法で再利用が可能です。 * レイアウト: 共通の表示領域 (例えば、ページのヘッダやフッタ) は、レイアウトビューに入れることが出来ます。 * 部分ビュー: 表示用のコード断片を再利用するために、部分ビュー (レイアウトで装飾されないビュー) を使用して下さい。例えば、モデルの入力フォームを表示する `_form.php` という部分ビューがありますが、これはモデルの新規作成と更新の両方のページで使われています。 * ウィジェット: 部分ビューを提供するのに多くのロジックが必要とされる場合は、部分ビューをウィジェットに転換して、そのクラスファイルにロジックを格納するのが適切です。大量の HTML マークアップを生成するウィジェットでは、マークアップを格納するためにウィジェット固有のビューファイルを使用するのが適切です。 * ヘルパクラス: ビューにおいては、データの書式化や HTML タグの生成など、ちょっとした仕事をするコード小片が必要になることがよくあります。 こういうコードを直接にビューファイルに書くのでなく、これらのコード小片をすべてビューのヘルパクラスに格納しておく方が良いでしょう。そして、ビューファイルでヘルパクラスを使用するのです。 Yii がこのアプローチの例を提供しています。Yii は共通に使われる HTML コードを生成するために、強力な [CHtml] ヘルパクラスを持っています。 ヘルパクラスは、明示的にクラスをインクルードしなくても使うことが出来るように、[オートロード可能なディレクトリ](/doc/guide/basics.namespace) に格納することが出来ます。 コントローラ ---------- [コントローラ](/doc/guide/basics.controller) は、モデル、ビュー、その他のコンポーネントを結合して実行可能なアプリケーションを作るための接着剤です。 コントローラはエンドユーザのリクエストを直接に扱う役割を持ちます。従って、 * コントローラは、`$_GET`、`$_POST`、その他ユーザのリクエストを表す PHP 変数にアクセスすることが出来ます。 * コントローラはモデルのインスタンスを作成して、そのライフサイクルを管理することが出来ます。例えば、モデルを更新する典型的なアクションでは、コントローラは最初にモデルのインスタンスを作成し、次に `$_POST` から取得したユーザ入力をそのモデルに代入し、モデルの保存に成功した後、ユーザのブラウザをモデルの詳細ページにリダイレクトします。 モデルの保存を実装するコードは、コントローラではなく、モデルに格納すべきであることに注意して下さい。 * コントローラは、埋め込みの SQL 文を含まないようにしなければなりません。SQL はモデルに置くのが適切です。 * コントローラは HTML やその他の表示用のマークアップを含まないようにしなければなりません。それらはビューに置くのが適切です。 設計の良い MVC アプリケーションでは、コントローラは非常に軽い、数十行のコードしか持たないものになる場合がしばしばあります。 その一方で、モデルは、データの表現と操作に関与するコードの大部分を抱えた、非常に重いものになります。 これは、モデルによって表現されるデータ構造とビジネスロジックがたいていはアプリケーション特有のものであり、アプリケーション固有の要求に応じるためにカスタマイズしなければならない所が多いことが原因です。 これに対してコントローラのロジックは多くのアプリケーションで共通のパターンに従うことが多く、従って、基礎になるフレームワークや基本クラスによって、かなり単純化することが出来ます。