ルーティング:部分一致ルートと再帰構造ルーティング

http://d.hatena.ne.jp/noopable/20090215/1234653729
ここでも書いたが、Zend_Controller_Router_Rewriteの実装は、すべてのルートをグローバル的に保存し、順にチェックする。
それでもいいが、ルーティングのルールを組み合わせたり、遅延登録をさせたいケースがあったので、実装してみた。
着手当初は1日で出来るだろうなぁと思っていたが、やってみると意外に時間がかかった。
本質的な問題は階層化されたルートとの情報共有だけと思ってたんだけど、router::assembleとroute::assembleの仕様に微妙な誤差があってinterfaceの不一致を招くのでrouterをrouteとして登録して連鎖させることができない。compositeパターンで設計してあればこういうところはインターフェースを共通にするので問題は生じない。惜しいなぁ・・・と思う。

名前ベースの階層表現

ルート名として、foo_barという風に指定すると、fooという再帰ルーターが定義されてその子ルートとしてbarがfooに登録されるという仕様にした。部分一致や再帰ルートの場合、一致したあとに一致した名前をresponseに含めるような仕様変更を書ければスマートなんだけど、とりあえず現状のルーターを利用するので、再帰対応ルートはgetCurrentNameで一致ルート名を返す仕様にした。そのためルーター側がルートが再帰対応ルートかどうかを調べてgetCurrentNameを使うか、一致した配列のキー名を使うか(デフォルト)を判定するコードが必要になる。
ルーターをこんな風に登録する

<?php
$router->addRoute('hoge', $firstRoute);
$router->addRoute('hoge_test', $subRoute);

ヘルパーなどから名前付きルートでURLを取得するときは、階層を意識しながらルート名を使うことになる。実際にはどんな名前にすべきかを理解する機能もしくは仕様を用意する必要がある。
ZFのルーティングにはもともとchainルートが定義されているんだけど、階層ルートではこれもサポート利用している。階層下へルートするかどうかを判定できるルートを複数設定できるようにしてある。

部分一致をルーティング可能に

階層ルーティングができたら、上位ルートで大枠のルートを決定させて、その後細かいルートを読み込むような部分一致ルートを可能にした。標準のRouter_Routeの*の仕様に似せて、+を指定すると残存パスを次のルートに引き渡す仕様。受け取った側はそれを通常パスと同様に処理をする。
これのメリットは、モジュール内の各コントローラー毎に形式的なルーティングルールセットがあるとき、変更するのがモジュールへのルートだけであれば、モジュールへのルートをセットすることで、コントローラーへのルーティングは相対パス的に作っておいて再利用できるということ。

<?php
$router->addRoute('hoge', new Route_Partial('path/to/seg/+' , array('module' => 'hoge')));
$router->addRoute('hoge_test', new Route_Partial(':action/*' ,array('controller' => 'test')));

これで、path/to/seg/xxxxというURLはhogeモジュール、testコントローラー、xxxxアクションが呼ばれる。ポイントは二つ目に指定したルートでは:action/*という具合に、モジュール毎の部分一致ルールセットを作っておけば、部分一致ルートの相対パスだけを受け取るのでパス部分を変更しなくて済む点。一つのモジュールを複数の場所で再利用するときに効果を発揮する。

DOMでやりたい

時間があれば、DOMDocumentを利用して、全く違うルーティングを作りたい。一段落したら考えよう。