JavaScriptって使えば使うほど意外と奥が深いかも…と思い始める.
今作っているのは、「JavaScriptでクライアントアプリケーションみたいな感じのUIをどこまで再現できるのさ」と言う物.
…ダサダサ orz
素直にこういうの使った方がいいのかなあ、と思い始めてます.
-> RAIALT http://rialto.application-servers.com/wiki/
-> Echo2 http://www.nextapp.com/platform/echo2/echo/
-> qooxdoo http://qooxdoo.oss.schlund.de/
話は戻って本題へ.
| [htdocs(apache document root)] +- index.php(フロントコントローラ) [Framework(apacheが読み込めるDirectory. Document root下でも可)] +-[application directory] +-[model] +-[template] +-[view] +-[classes] +-[各class] +-[controller] +- controller.php(コントローラ) |
前回の記録で
・ControllerはModel/Viewを呼び出す
・Modelは処理を実行する
・Viewは描画処理を実行する
という役割分担が見えてきた訳ですが、それをディレクトリ構成とシンクロしてみます.
ちょっと前回と異なります.
[より現実的なディレクトリ構成]
| [htdocs(apache document root)] +- index.php(フロントコントローラ) [Framework] +-[application directory] +-[class] … Application毎のClass +-[conf] … Application毎のFramework用設定 +- Framework.config.php +- Framework.stringtable.php +-[model] … Application毎のModel +-[common] +- index.model.php +-[template] … 描画用Template +-[view] … 描画用View +-[common] +- index.view.php +-[tmp] … Applicationで使用するTmp +-[classes] +-[各class] +-[model] +- Model.class.php +-[view] +- View.class.php +-[conf] +- Framework.config.php +- Framework.header.php +- Framework.stringtable.php +-[controller] +- Controller.php |
…実際の画像を見た方が早いか orz
※図はEclipseのWindow画面です.
不要なディレクトリも多々ありますが、基本的に理解すべきディレクトリは、赤く囲った場所だと思います.
この構成と、前回の覚書を照らし合わせてみます.
※あくまで理解する為にやっているので、分かり易い名前にしています.
例えばindex処理のModelの場合は「index.model.php」w
でも、正直こんな風に名前を付けた方が分かり易いです.
[それぞれの役割(必要な物のみ)]
| [htdocs] +- index.php … フロントコントローラ (ユーザーがアクセスするファイル) [KtFramework] +-[application] … Applicationディレクトリ | +-[example] … Application毎のディレクトリ | | (例では「example」と言うApplication) | +-[model] … 各Model処理用ディレクトリ | | +-[common] | | +- index.model.php … index処理用Model | +-[view] … 各View処理用ディレクトリ | +-[common] | +- index.view.php … index処理用View +-[class] … Framework用Classディレクトリ | +-[KtModel] | | +- _KtModel.class.php … Model雛形 | +-[KtView] | +- _KtView.class.php … View雛形 | ※KtView.error.phpはエラー時用. 正直不要w +-[controller] … Framework用Controllerディレクトリ +- KtController.php … FrameworkContorller ※KtController.php5.php等はKIMAGUREが気まぐれで作った各 PHPバージョン用ファイル. |
以降、このディレクトリ構成を基準として話を進めます.
※ここで示されている構成、処理等々はKIMAGUREが自分で認識している範囲の内容、および自分の好みの書き方で書いています. 余り参考にしないで下さいw
ユーザーにアクセスしてもらうのは
/htdocs/index.php
です(フロントコントローラ).
「/htdocs/index.php」の中身は次の通り.
[/htdosc/index.php]
| require_once('../KtFramework/controller/KtController.php'); $KtController = new KtController('example', 'common/index_Action'); |
「/htdocs/index.php」はFrameworkのControllerを呼び出すためのフロントコントローラと位置づけていますので(例の場合ですが)、当然Framework用ControllerファイルをInclude(require)し、続いてControllerクラスを宣言しています.
/** ---------------------------------------------------------
* @param string ApplicationName [Application名]
* @param string ActionName [Action名(※前回記録参照)]
* @param string CallbackViewName [強制処理View]
*/
$KtController = new KtController('example', 'common/index_Action');
この時点で処理はFramework用Controller、今回の例では「/Framework/controller/KtController.php」に移ります.
※自分で作っておいて何ですが、「*.php5.php」等は話が複雑になるの
で省きます. 中身も必要な部分のみ抜粋.
[/Framework/controller/KtFramework.php]
/** =========================================================== * Framework Constructor. * * @internal Framework用Controller * @param string $szAppName * @param string $szActionName * @param string $szCallbackViewName */ function KtController($szAppName, $szActionName, $szCallbackViewName = '') { /** --------------------------------------------------------- * Application名セット */ $this->_KTF_APPLICATION = $szAppName; /** --------------------------------------------------------- * Include classes. * * @todo エラー処理未実装 */ if ($this->doClassInclude() == false) { require_once($this->_KTF_ERROR_VIEW); exit; } /** --------------------------------------------------------- * Set request action. * * @internal Constructor引数よりREQUESTを優先 */ $this->setRequestAction($szActionName, $szCallbackViewName); /** --------------------------------------------------------- * Execute trigger. */ $this->doTrigger($szActionName, $szCallbackViewName); } [かなり省略] /** =========================================================== * doTriggerWWW(※2) * * @access private * @param string $szActionName * @param string $szCallbackViewName * @return mixed 成功 = True / エラー = KT_ERROR_* */ function doTriggerWWW($szActionName, $szCallbackViewName) { /** --------------------------------------------------------- * Local valiable. */ $isSuccess = false; /** --------------------------------------------------------- * Include model. * * @todo Model処理後の成否判定処理があやふや */ $isSuccess = $this->doModel($szActionName); if ($this->isModelSuccess) $isSuccess = true; /** --------------------------------------------------------- * Switch view. */ $this->forwardView($isSuccess, $szActionName, $szCallbackViewName); } [省略] /** =========================================================== * doModel * * @internal Model処理を実行 * @access private * @param string $szActionName * @return mixed 成功 = true / エラー = KT_ERROR_* */ function doModel($szActionName) { /** --------------------------------------------------------- * Local valiable. */ $isSuccess = false; $szForwarPath = ''; $szClassName = ''; /** --------------------------------------------------------- * Set model path. * * @internal 処理の仕方が手抜き…もとい分かり易くw * 単純にしているので、Action名を置換処理+加工してPATH * としてセットしています. * ※補足)index_Actionをindex.model.phpに置換し、 * Applicationまでのパスを追加しています. */ $szForwarPath = $this->_KTF_BASEDIRECTORY . '/' . $this->_KTF_DIRECTORY['application'] . '/' . $this->_KTF_APPLICATION . '/' . $this->_KTF_DIRECTORY['model'] . '/' . str_replace('_Action', '.model.php', $szActionName); /** --------------------------------------------------------- * Set class name. * * @internal 単純にAction名とModelのクラス名を類似させて * index_Action->index_Model(Class名)に変換. */ $szClassName = basename(str_replace('_Action', '_Model', $szActionName)); /** --------------------------------------------------------- * Check file exist and declare class. */ if ((is_file($szForwarPath))&&(is_readable($szForwarPath))) { require_once($szForwarPath); //-> Class宣言 if (class_exists($szClassName)) { $this->_KTF_MODEL =& new $szClassName($this); $isSuccess = true; } } /** --------------------------------------------------------- * Check I/O error. */ if ($isSuccess == false) { require_once($this->_KTF_ERROR_VIEW); exit; } /** --------------------------------------------------------- * Set return value. */ return $isSuccess; } [省略] /** =========================================================== * forwardView * * @internal View処理を実行 * @access private * @param boolean $isSuccess * @param string $szActionName * @param string $szCallbackViewName * @return mixed 成功 = True / エラー = KT_ERROR_* */ function forwardView($isSuccess, $szActionName, $szCallbackViewName) { /** -------------------------------------------------------- * Local valiable. */ $szForwardName = $szCallbackViewName; $szForwarPath = ''; $szClassName = ''; /** --------------------------------------------------------- * Set view path. * * @internal 処理の仕方が手抜き…もといModelと同じ. * 単純にしているので、Action名を置換処理+加工してPATH * としてセットしています. * ※補足)index_Actionをindex.view.phpに置換し、 * Applicationまでのパスを追加しています. */ if (strlen($szCallbackViewName) == 0) { $szForwardName = str_replace('_Action', '.view', $szActionName); } //-> エラー描画の場合「.error」へ置換 if ($isSuccess == false) { $szForwardName = str_replace('.view', '.error', $szForwardName); } //-> フルパス代入 $szForwarPath = $this->_KTF_BASEDIRECTORY . '/' . $this->_KTF_DIRECTORY['application'] . '/' . $this->_KTF_APPLICATION . '/' . $this->_KTF_DIRECTORY['view'] . '/' . $szForwardName . '.php'; /** --------------------------------------------------------- * Set class name. */ $szClassName = basename(str_replace('_Action', '_View', $szActionName)); /** --------------------------------------------------------- * Check file exist and declare class. */ $isSuccess = false; if ((is_file($szForwarPath))&&(is_readable($szForwarPath))) { require_once($szForwarPath); //-> Class宣言 if (class_exists($szClassName)) { $this->_KTF_VIEW =& new $szClassName($this); $isSuccess = true; } } /** --------------------------------------------------------- * Check I/O error. */ if ($isSuccess == false) { require_once($this->_KTF_ERROR_VIEW); exit; } } |
うっ…行数多くなってきた(汗
「$this->doClassInclude()」の部分に於いて、Controller内で全てのFramework用付属クラスをIncludeしてしまいます.
実際にユーザーコードから呼び出す場合は
KtController->Kt****->[FunctionName]
と成ります(ClassLibrary等と同じ).
次に「$this->setRequestAction()」でREQUESTからActionを渡されていた場合に、今処理で実行すべきActionとしてREQUESTからAction名をセットします.
後々書きますが、Model->Viewと処理した際、ViewでTemplateに「次に処理すべきAction」をINPUT(要するにREQUESTで渡される何かしら)で指定します.
「$this->setRequestAction()」はこの指定されたActionを取得する為に実行する様にしました.
現時点では「単純にindex.php(フロントコントローラ)から呼び出されただけ」ですので、初期値の「common/index_Action」がAction名になります.
※注)この「common/index_Action」は非常に変な書き方ですが、パス構成を自分で分かり易くする為にこの様な特殊な書き方をしています. この様な書き方は他では殆ど見かけません.
次に「$this->doTrigger」ですが、これは多少細かく処理を分けた結果出来た(と言うよりEthnaをパクったりしてw)Functionです.
「$this->doTrigger」から呼び出された後の不要な処理を省いて、実際に何をやっているのか抽出.
[doTriggerWWW参照]
要するに、細かい処理(Model呼び出し前/後の処理等々)を省いた上述を見ると
1. Modelを呼び出す.
2. 必要であれば何らかの方法でModel処理の結果を取得
※例では分かり易く「$isSuccess」に戻る様に表記しています.
3. Modelの処理結果を踏まえてViewを呼び出す.
と成ります.
ようやくModelに処理を移行します.
[doModel参照]
基本的にView移行処理と同じ流れです.
今回の例ではAction名を直接パス名に変更し、そのファイルをIncludeして直後にClass宣言しています.
呼び出されたModelでは、
・REQUEST処理(Validate(入力内容が正当か)等)
・データ処理(DBアクセス等含む)
・データ加工/整形
※サニタイズはControllerから予めまとめて処理した方が良いと個
人的には思うのですが、Smarty等ではTemplateへ値をセットする
際に処理するのでお好みで.
処理結果として、値を戻すかControllerの変数(またはGLOBALS)に戻します.
ま、Viewで判定してエラー用の描画をするのでも良いと思います.
最後にView.
Modelの処理結果、データ処理後の値を描画する為の処理を行います.
Modelで一通り必要な処理をしてあれば、Template Engineへ値をセットするだけで終了します(それが本来の姿?).
一通りザックリと流れを見てみた訳ですが(省きすぎて分からないと言う話も有りw)、基本的には
1. フロントコントローラをユーザーに読み込んでもらう
2. フロントコントローラからControllerを読み込む
3. ControllerでActionを取得し、事前処理を行う
4. ControllerからActionに対応したModelを呼び出す
5. Modelで処理を行う
6. ModelからControllerに戻る
7. ControllerからActionに対応したViewを呼び出す
8. Viewで処理して描画出力
と言う流れですね.
そして前回備忘した様に、ControllerからModel/Viewを参照し、Model/ViewからControllerを参照する事で、Modelで処理されたデータをViewでセットする事が可能となります.
[Controller]
KtController->DATA;
[Model]
$this->Controller =& KtController;
KtModel->Excecute() {
$this->Controller->Data = 1;
}
[View]
$this->Controller =& KtController;
KtView->Excecute() {
描画 = $this->Controller->Data;
}
で、MVC型Frameworkを使う理由は?
…何となくw
では無く、次の利点があります.
1. 基本機能が統一的に提供される(Framework全般に言える)
処理の流れやFrameworkに内包されたClass機能等.
2. ロジックと描画を分離出来る為、ロジックコードを書くプログラマ
とデザインするデザイナの作業分担が楽.
3. メンテナンス製に優れている.
変更点やバグFIX時の切り分け等が分かり易い.
4. 再利用性の向上.
確実に部品化されますので、再利用には向いています.
極端な話、View/Temlateは同じでModelだけ変更するとかもありw
5. Model/Viewが雛形から派生されるClassである為、書いたコードに
統一性がある(メンテナンス時は機能の統一性よりもこちらの方が
大事かもしれません)
6. 書いたコードが気付かない内にオブジェクト指向(っぽく)になる.
正直、KIMAGUREの様な底辺中の底辺の人間には、この恩恵は計り知
れません. 書いている時は苦痛でも、後々非常にスマートなコード
である事を実感するはずです.
さて、次回は実際のコードで最終的な流れの確認です.
話は変わりますが、最近急激にコードの書き方が変わってきていますw
勉強すればする程、自分の底辺さ加減が分かり、且つ以前のコードが如何に無駄の多いコードであったかに気付き、且つ再利用性に乏しいコードであったかに気付かされます orz
ま、後数ヵ月後には今書いているコードも同じ様に「底辺は底辺. 底辺で試行錯誤してたな」と思うんでしょうがw
取り敢えず一気に書いたので不足、間違いが多々ありそうな悪寒.
後日修正ですな.

