【FuelPHP】ルーティングに正規表現(名前つきキャプチャ)を使うと捗る

投稿日:

ここにきて急に近所でも桜が咲き出しまして(と思ったらもう満開を通り越した感ある)。
こんな時、頭に流れるのは森山直太朗のさくらさくらいざ舞い上がれか、コブクロのさくらのはなびら散るたびにか、NumberGirlの桜のダンスをお前は見たかです。

さて、最近案件でFuelPHP(以下fuelと呼びます)を使う機会が多くなってきました。
こういうフレームワークで何が一番最初に便利かなって考えると自分はルーティングを思い浮かべるんですが、どうでしょうか。

基本的なルーティング

で、fuelもそりゃあルーティングが結構簡単に書けるわけなんですね。
ルーティング – 概要 – FuelPHP ドキュメント
あ、バージョンは1.7.2です。

この中でも、自分は名前付きパラメータが結構便利だなぁって思うんです。
上記のドキュメントにもある通り、fuel/app/config/routes.php

return array(
    'blog/:year/:month/:id' => 'blog/entry', // /blog/2010/11/entry_name が /blog/entry に経路付けられる
);

こう書くと

$this->param('year');  // => '2010'
$this->param('month'); // => '11'
$this->param('id');    // => 'entry_name'

こうアクセス出来るってわけです。
あら便利。

便利なんだけど、、

便利なんだけど、上の書き方だと year,month,id はどんな値でも受け取るんですよね。
でもでもでも少なくとも使う側にとってyearとmonthは数値(int)を期待しているはず。
まぁバリデーションすればいいんですが、数値である事くらいはルーティングで防げたらいいなぁと思いますよね?
そんな時、こう書くとイイんです。

名前つきキャプチャを使う

return array(
    'blog/(?P<year>\d+)/(?P<month>\d+)/:id' => 'blog/entry',
);

これで、yearとmonthに数値以外が入力されたら、HttpNotFoundExceptionを吐くし、数値が入れられていれば上記のように
$this->param('year');
でアクセス出来ます。 

これは正規表現の名前つきキャプチャというものを使っています。
※この記事が参考になりました php :: 正規表現 :: 名前つきキャプチャというのを習った :: ウェブデザイナーの日記
んで、ルーティングに名前つきキャプチャを使うと色々捗るんです。
※実際に捗った例を何点か記します

例えば、ページングのある画面でページ数をURIに含むとします。
http://hoge.test/blog/pXX/ な感じ
ほいで、pXXのXXの値をcontrollerとかで、$this->param('page')で取得したいっ!っていう時、以下の様に書けたりしちゃいます。

例1) page数を、1 〜 無制限で許容する場合

return array(
    'blog/p(?P<page>[1-9][0-9]*)' => 'welcome/hoge',
);

例2) page数を、1 〜 99まで許容する場合

return array(
    'blog/p(?P<page>[1-9][0-9]?)' => 'welcome/hoge',
);

例3) page数を、2 〜 無制限で許容する場合 (p1は出したくないケース)

return array(
    'blog(/p(?P<page>[2-9]|[1-9][0-9]+))?' => 'welcome/hoge',
);

例4) page数を、2 〜 999まで許容する場合 (p1は出したくないケース)

return array(
    'blog(/p(?P<page>[2-9]|[1-9][0-9]{2}))?' => 'welcome/hoge',
);

ほら、かなり便利ですよね。使わない手は無いですよね。
上記は単純なものしか書いていないですが、正規表現が得意な人はもっといい感じに書けると思います。※自分は苦手です

種明かし

でまぁ、ここまでドヤ感満載な感じで書いてるんですけど、実はこの辺、ドキュメントにヒントが書いてありまして。
Router – クラス – FuelPHP ドキュメント
の、get($name, $named_params = array())の説明に

ルートに名前付きパラメータ、正規表現、または両方の組み合わせを含めて、この様にもできます。
// ルートをこの様に定義した場合:
return array(
‘thread/(?P<thread_id>\d+?)/post’ => array(‘post’, ‘name’ => ‘post’),
);

// これらは ‘thread/1/post’ を返す:
echo Router::get(‘post’, array(‘thread_id’ => 1));
echo Router::get(‘post’, array(‘$1’ => 1));
echo Router::get(‘post’, array(1));

とあって、(?P<thread_id>\d+?)これなんぞ?なんぞこれ?って思ったんですね。
で、調べるうちに名前つきキャプチャっていう事が分かったというわけです。
あと、/core/classes/route.php

protected function compile()
{
    /*
     |------------------------------
     | 処理がある
     |------------------------------
     */
    
    return preg_replace('#(?<!\[\[):([a-z\_]+)(?!:\]\])#uD', '(?P<$1>.+?)', $search);
}

をダンプしてもどんな正規表現が使われるのかが分かったりします。

ちなみに、名前付きキャプチャは

return array(
    'thread/(?<thread_id>\d+?)/post' => array('post', 'name' => 'post'),
);

こんな感じでPを付けなくてもいけるんだけど、Pを付けないと上記ドキュメント(Routerクラス)内のRouter::get('post', array('thread_id' => 1));をしたい時に上手く置換してくれないケースがあるのでPは付けたほうがいい気がします。

public function action_hoge()
{
    var_dump(
        // /thread/1/post => OK
        Router::get('post', array('$1' => '1')),
        // /thread//post  => NG
        Router::get('post', array('thread_id' => '1')),
        // /thread/1/post => OK
        Router::get('post', array(1))
    );
}

まとめ

というわけで、ルーティングに名前付きキャプチャを使ったら捗ったという話でした。
もっと良い書き方があったりだとか、何か間違っていたらごめんなさい。

ちなみに、laravelだと

Route::get('user/{id}', function($id)
{
    //
})
->where('id', '[0-9]+');

みたいに書けるんだけれども、こっちのほうが直感的だなぁと思う今日この頃です。

投稿日:
カテゴリー: PHP タグ:

作成者: shimabox

Web系のプログラマをやっています。 なるべく楽しく生きていきたい。

1件のコメント

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください