URLパラメーター

MVCでのURL

MVCではURLの考え方が通常とは違うのはお分かりのことでしょう。
通常はURLはサーバー上のディレクトリのパスそのままに対応します。ある程度、サーバー上の階層が分かる状態ですよね。

しかしMVCでは言ってみればURLは偽装です。
URLに対応するディレクトリは実際には存在しません。
また通常であればファイルの拡張子に.htmlやら.phpやらが付きますが、MVCでは当然付きません。
これも一つのメリットです。
通常は.phpという拡張子を見れば、そのページが動的なものであることが推測できてしまいます。別にそんなこと問題でもないですが、隠せるなら隠すに越した事はありません。
そもそもURLとはWEB上のリソースを一意に指し示すものです。拡張子が付いていると、URLが表しているのはPHPファイルそのものということになり、アプリケーション上のリソースというイメージではありません。どちらかと言えばシステム便宜上作ったファイルそのものという感じです。なので拡張子は無い方が、本来のURLの意味に則しているとも言えます。

URLに含めるパラメーター

HTMLを使ってページからあるページへ何かパラメーターを渡すときはPOSTかGETしかありません。POSTならいいですがGETはURLにくっつけて送信するため、URLが汚くなりがちだし、そのページにはどんなパラメータが必要でどんな値を送信しているかがバレバレです。

例えばこんな感じです。

http://www.aiueo.com/bbs.php?mode=view&page=2

掲示板のURLで、modeとpageというパラメータを送信しています。
例ではmodeが"view"、つまり記事表示モードです。
そしてpageが"2"なので2ページ目を表示しようとしています。

これをMVC的なURLだと、

http://www.aiueo.com/bbs/view?page=2

こんな感じでしょうか。
"bbs"がコントローラー、"view"がアクションで、
モードごとにアクションメソッドを作るイメージです。
で、pageというパラメーターが必要だとするとやはりGET送信になります。
ここでどうしても"?"を使ってパラメータ送信する事になるので、
せっかくのMVC的な綺麗なURLが台無しです。

別に気にならないならいいんですが、やはりURLにこの"?"とか"&"とかが入るのを嫌がる人は実際多いのです。私も好きではありません。

なのでどうにかしてこれをなくそうと思います。
そもそもMVCのURL自体が、apacheのりライトにより、最終的にはドメイン部分以降をGETパラメーターとして処理します。
それを"/"で区切って1つ目をコントローラー、2つ目をアクション、という決め事の上に成り立つ仕組みです。

それでは更に"/"区切りを増やして3つ目以降が存在したら?ってなります。
ここまででご紹介した仕組みでは3つ目以降が存在しても何も処理していません。
これを生かすのです。
つまり、

http://www.aiueo.com/bbs/view/2

として3つ目のパラメーターをページ番号として取得すれば良い話です。
しかしこれは掲示板、つまりbbsコントローラーに限った話になるので、共通の仕組みとして3番目はページ番号、という事はできません。

なので、3つ目以降は全て配列に格納します。

URLパラメーターの仕組みの実装

まず、意味合い的にはPOSTやGETと同列のパラメーターとして扱いたいと思うので、
PostクラスやGetクラスと同じように一つクラスを作ります。

UrlParameter
<?php

class UrlParameter extends RequestVariables
{
    protected function setValues()
    {
        // パラメーター取得(末尾の / は削除)
        $param = ereg_replace('/?$', '', $_GET['param']);
        
        $params = array();
        if ('' != $param) {
            // パラメーターを / で分割
            $params = explode('', $param);
        }

        // 2番目以降のパラメーターを順に_valuesに格納
        $i = 0;
        if (2 < count($params)) {
            for ($i = 0; $i < count($params); $i++) {
                $this->_values[$i] = $params[$i + 2];
            }
        }
    }
}

?>

そしてPostやGetと同じようにRequestクラスのフィールドに含めます。

Request.php
<?php

class Request
{
    // POSTパラメータ
    private $_post;
    // GETパラメータ
    private $_query;
  // URLパラメータ
    private $_param;
    
    // コンストラクタ
    public function __construct()
    {
        $this->_post = new Post();
        $this->_query = new QueryString();
        $this->_param = new UrlParameter();
    }

           ・
           ・
           ・

    // URLパラメーター取得
    public function getParam($key = null)
    {
        if (null == $key) {
            return $this->_param->get();
        }
        if (false == $this->_param->has($key)) {
            return null;
        }
        return $this->_param->get($key);
    }
}

?>

これを使ってコントローラーでパラメーターを取得する例です。

BbsController.php
<?php

    // 記事表示
    public function threadsAction()
    {        
        // URLパラメータよりページ数取得
        $page = 1;
        if (null != $this->request->getParam('0')) {
            $page = $this->request->getParam('0');
        }
        // 記事データ取得
        $data = $this->model->getThreadsData($page);
        // 表示
        $this->view->assign('parent', $data);   
    }

?>

単純に3番目以降のパラメーターを前から順に配列に格納しただけのため、
汎用的な仕組みとするには数字をキーとするしかありません。

これでもいいのですが、params->get('0')って言われても何のパラメーターを取得しようとしているのか分かりにくいです。
しかもURLのパラメーターの順番を入れ替えることは絶対出来なくなります。もしそんな事をしたらプログラムの修正になります。

なんとか意味のあるキーでパラメーターを取得したいところですが、
当然、何とかして3番目のパラメーターは"page"であるという関連付けが必要です。

一つの方法はある規則に基づいてURLにキーを含めてしまいます。
例えば、

http://www.aiueo.com/bbs/view/page_2

みたいな感じで、3番目以降のパラメーターは必ず「キー_値」の形式という決め事をするのです。

UrlParameter.php
<?php

class UrlParameter extends RequestVariables
{
    protected function setValues()
    {
        // パラメーター取得(末尾の / は削除)
        $param = ereg_replace('/?$', '', $_GET['param']);
        
        $params = array();
        if ('' != $param) {
            // パラメーターを / で分割
            $params = explode('', $param);
        }

     if (2 < count($params)) {
            foreach ($params as $param) { 
                // "_"で分割
                $splited = explode('_', $param);
                if (2 == count($splited)) {
                    $key = $splited[0];
                    $val = $splited[1];
                    $this->_values[$key] = $val;
                }
            }
        }
    }
}

?>
BbsController.php
<?php

    // 記事表示
    public function threadsAction()
    {        
        // URLパラメータよりページ数取得
        $page = 1;
        if (null != $this->request->getParam('page')) {
            $page = $this->request->getParam('page');
        }
        // 記事データ取得
        $data = $this->model->getThreadsData($page);
        // 表示
        $this->view->assign('parent', $data);   
    }

?>

これで晴れてキーによるパラメーターが実現できました。
「_」で区切られていているパラメーターがなんとなく気持ち悪いのも事実ではありますが。

ちなみに例えばZendFrameworkの場合に同じようなことを実現する場合のURL指定は、

http://www.aiueo.com/bbs/view/page/2

という指定の仕方がデフォルトになっています。
つまり、奇数番目と偶数番目を対で考え、
3番目がキーでそれに対する値は4番目、
5番目がキーでそれに対する値は6番目、
ということになっています。

これも一つの決め事の上に成り立っています。


また、URLにはキーを含めずに、

http://www.aiueo.com/bbs/view/2

というURLで、コントローラー側では"page"というキーで取得する方法も一つあります。
テキストの設定ファイルを一つ用意し、関連付けの設定をするのです。
例えば、
コントローラーがbbsでアクションがviewのとき、3番目のパラメーターのキーは"page"である、という設定をするのです。

param.ini
[bbs]
view.3 = page

例えばこのような感じで、コントローラー・アクションに対してパラメーターの順番とキーを関連付ける何らかの設定を行うのです。

当然この場合、この設定ファイルを読み込み、コントローラーとアクションから該当する設定があればその値を取得するという仕組みの組み込みが必要になります。
ここではそこまでは触れませんが、そういう手段も考えられます。