人気ブログランキング | 話題のタグを見る
(PHP)MVCモデルを使ったPHPアプリケーション
PHPのフレームワーク(CakePHP等)は、元からMVCモデルとなっているわけですが、機能も多く重量級です。
軽量のMVCフレームワークもあるのかもしれませんが、ほんとに小規模なPHPアプリケーションとかだと、案外自作にした方が効率がよいのかもしれません。

5年度前に、以前の勤務先で自作MVCモデルを使ってたんですが、しばらくPHP触らない間にすっかり忘れていました。
ということで、PHPのオブジェクト指向入門 | オブジェクト指向PHP.NETを参考に、軽量なMVCフレームワークを作ってみました。


MVCの概念は多くのサイトで解説されているので省略します。

今回作成するのは、大まかに下記のような構造になります。
(PHP)MVCモデルを使ったPHPアプリケーション_e0091163_145447.jpg



リクエス振り分け
まずは、リクエスト振り分けの部分です。
たいていは、全アクセスを index.php に集中させ、そこからURLの内容に基づきどのコントローラのどのアクションに処理を割り振るかを制御します。

利用するコントローラを指定する方法として、URLの index.php に対してGETパラメータを付ける方法と、/区切りでパラメータを付ける方法があります。

今回は / 区切りでパラータを指定する方法を取りました。
(http://hoge.jp/index.php?c=login → http://hoge.jp/login/ みたいな感じ)

参考先の設定ではサイト直下にMVCコンテンツを置くような設定になっています。
しかしイントラネットのサーバ等で1台のWEBサーバにWEBアプリが乱立していると、サイト直下に配置できないケースが出てくるんですよね。
(例えば、http://hoge.jp/eigyou/ とか http://hoge.jp/jousys/ とか部署のディレクトリの配下にコンテンツ置く必要がある場合など)

ということで、特定の階層以下からコントローラのパラータとみなすように手を加えてみました。
例として、http://hoge.jp/eigyou/ 配下にMVCモデルで作ったコンテンツを置くこととします。

まず、apacheのリライト機能の設定です。
コンテツを置く場所の .htaccess に以下のように設定しました。
RewriteRuleに QSA を指定するとこで、GETパラメータも引き継げます。

Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /eigyou/ [QSA,L]

※当然MVCアプリコンテンツのパスが変わると、mod_rewriteの設定は変わります。
例:http://hoge.jp/ → RewriteRule ^(.*)$ / [QSA,L]
例:http://hoge.jp/eigyou/app1/ → RewriteRule ^(.*)$ /eigyou/app1/ [QSA,L]



全てのアクセスが集中する index.php とそこから呼ばれる Dispatcher.php は下記のようになります。
(文字数制限のためハイライトはなしです)

index.php

<?php

require_once 'Dispatcher.php';
require_once 'util.php'
$dispatcher = new Dispatcher();

IncludePathSetting($dispatcher);

//apacheのドキュメントルートから何階層目のディレクトリにMVCアプリを配備するか。
//ルートを0とすると、eigyou/ 配下は1階層目に当たるので、1とする。
$dispatcher->setPramLevel(1);
$dispatcher->dispatch();

?>



util.php

<?php
/**
* PHPのinclude_pathを設定。
* 本番環境、テスト環境を切り分ける。
*/
function IncludePathSetting($dispatcher){
if ($_SERVER['SERVER_NAME']=='hoge.jp' ){
//本番環境
$path = '/home/hoge/smarty/libs/';
$path .= PATH_SEPARATOR . '/home/hoge/別にインクルードするディレクトリあれば指定/';
$dispatcher->setSystemRoot('/home/hoge/html/eigyou);
} else {
//開発環境
$path = 'D:/devlop/php/smarty/libs/';
$path .= PATH_SEPARATOR . 'D:/devlop/php/別にインクルードするディレクトリあれば指定/';
$dispatcher->setSystemRoot('D:/devlop/php/eigyou');
}
set_include_path(get_include_path() . PATH_SEPARATOR . $path);
}
?>



Dispatcher.php
このPHPファイルで、リクエストを振り分けます。
1つめのパラメータで、どのコントローラを使うか決めます。
具体的に、パラメータ名+Controller というクラスのインスタンスを作成します。
そして、2つめのパラメータでどのアクションメソッドを実行するか決めます。
こちらも、パラメータ名+Action というメソッドを実行します。

<?php
class Dispatcher
{
private $sysRoot;
//URLの階層設定。どの階層からをパラメータとして解釈するか。
// /直下に置くなら0, /hoge/以下に置くなら1, /hoge/piyo/以下に置くなら2
private $paramLevel=1;

public function setSystemRoot($path)
{
$this->sysRoot = rtrim($path, '/');
}

/**
* URLの階層設定。どの階層からをパラメータとして解釈するか。
* @param int $iLevel
*/
public function setPramLevel($iLevel) {
$this->paramLevel=$iLevel;
}

public function dispatch()
{
$params_tmp = array();
//?で分割。GETパラータを無視するため
$params_tmp = explode('?', $_SERVER['REQUEST_URI']);
// パラメーター取得(末尾,先頭の / は削除)
$params_tmp[0] = ereg_replace('/?$', '', $params_tmp[0]);
$params_tmp[0] = ereg_replace('^/*', '', $params_tmp[0]);
$params = array();
if ('' != $params_tmp[0]) {
// パラメーターを / で分割
$params = explode('/', $params_tmp[0]);
}

// 1番目のパラメーターをコントローラーとして取得
$controller = "index";
if ($this->paramLevel < count($params)) {
$controller = $params[$this->paramLevel];
}

// パラメータより取得したコントローラー名によりクラス振分け
$className = ucfirst(strtolower($controller)) . 'Controller';

// クラスファイル読込
require_once $this->sysRoot . '/controllers/' . $className . '.php';

$url ="/";
for ($i = 0; $i < $this->paramLevel; $i++) {
$url = $url . $params[$i] . "/";
}
// クラスインスタンス生成
$controllerInstance = new $className($url);

// 2番目のパラメーターをコントローラーとして取得
$action= 'index';

if ( ($this->paramLevel + 1) < count($params)) {
$action= $params[($this->paramLevel + 1)];
}
// アクションメソッドを実行
$actionMethod = $action . 'Action';
$controllerInstance->$actionMethod();

}
}

?>



xxxController.php
各コントローラのクラスファイルです。
ここで、モデル、ビューへの制御を行います。

<?php
require_once( "Request.php" );
require_once( 'model/HogeModel.php' );
require_once('Smarty.class.php');

class HogeController
{

//リクエスト
private $request;
//モデル
private $model;
//ビュー(Smartyのインスタンス)
private $view;
//ビューで表示するURL
private $url;

// コンストラクタ
public function __construct($url)
{
// リクエスト
$this->request = new Request();
//モデルをインスタンス化(コントローラと1:1のパターン)
$this->model = new HogeModel();
//ビューインスタンス化
$this->view = new Smarty();
//Smartyのディレクトリ設定(キャッシュやテンプレート置き場など)
$this->view->template_dir = "smarty/templates";
$this->view->compile_dir = "smarty/templates_c";
$this->view->cache_dir ="smarty/cache";
//Smartyテンプレートにセットするパス
$this->url = $url;
//HTMLから参照すべき外部ファイルのパス設定(URLを/区切りにするとパスが変わるため)
$this->view->assign("url",$this->url);
}

public function indexAction()
{

//http://hoge.jp/eigyou/hoge/ アクセス時の処理
//モデル($this->model )の任意のメソッドを実行。
//画面表示
$this->view->display("hoge_index.tpl");
}
public function dispguestAction(){
//http://hoge.jp/eigyou/hoge/dispguest/ アクセス時の処理
//モデル($this->model )の任意のメソッドを実行。
//画面表示
$this->view->display("hoge_disp.tpl");
}

}
?>



xxxModelクラス.php
各モデルのクラスファイルです。
このクラス内でDBにアクセスしたりと、メインロジックを書いて行きます。

<?php

class HogeModel
{
//各処理毎にメソッドを作成。
}
?>



リクエスト
ブラウザから送信されるリクエストですが、これもクラス化してやります。
詳しいコードは、(PHP)MVCモデルを使ったPHPアプリケーション(リクエスト部分)を参照にしてください。


コントローラからリクエストを取得するには下記のようにします。

$this->request->getPost("パラメータ名");
$this->request->getPost(); //とすると、全POST情報を取得できます。



ビュー
ビューは前述のとおり、テンプレートエンジンであるSmartyを使います。
コントローラにSmartyのインスタンスを作成し、テンプレートを指定して表示するという方法です。
Smartyについては大量に情報が出まわってますし、3流PGのメモ書きにもちょこっと書いてるのでそのあたりが参考になるかもです。


さて、こう考えると結構簡単にMVCモデルって作れるんですね。
オブジェクト指向的には、コントローラやモデルクラスでベースクラスに汎用処理埋め込み、それを継承して各クラス別の処理を書くのが理想なんでしょうが、今回はMVCの基本的な部分だけということなので、以上です。

参考先には、継承をつかった話も書いているので、見てみるといいかもしれません。
by Jehoshaphat | 2012-11-25 23:03 | PHP開発 | Comments(0)


<< (Linux)PXEサーバ(U... (JavaScript)IE環... >>