Programmer's Reference Guide
| リクエストオブジェクト |
標準のルータ
導入
Zend_Controller_Router_Rewrite は、標準のルータです。 ルーティングとは、URI (ベース URL から取得した URI の一部) を展開し、どのコントローラのどのアクションが リクエストを処理するのかを決める処理のことです。 モジュールやコントローラ、アクション、そしてその他のパラメータが Zend_Controller_Request_Http オブジェクトにまとめられます。 このオブジェクトを処理するのが Zend_Controller_Dispatcher_Standard です。 ルーティングが行われるのは一度だけ、すなわちリクエストを最初に受け取ってから 最初のコントローラに処理が渡される際だけです。
Zend_Controller_Router_Rewrite は、mod_rewrite 風の機能を PHP だけで実現できるように設計されています。 この処理は Ruby on Rails のルーティングを多少参考にしており、 ウェブサーバの URL 書き換えに関する前提知識を必要としません。 以下の単純な mod_rewrite ルール (のいずれか) で動作するように設計されています。
- RewriteEngine on
- RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php
あるいは (推奨)
- RewriteEngine On
- RewriteCond %{REQUEST_FILENAME} -s [OR]
- RewriteCond %{REQUEST_FILENAME} -l [OR]
- RewriteCond %{REQUEST_FILENAME} -d
- RewriteRule ^.*$ - [NC,L]
- RewriteRule ^.*$ index.php [NC,L]
Rewrite ルータを IIS ウェブサーバ (バージョン <= 7.0) で使用するには » Isapi_Rewrite を Isapi 拡張モジュールとしてインストールします。そして次のようなルールを記述します。
- RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]
注意: IIS Isapi_Rewrite
IIS を使用すると、$_SERVER['REQUEST_URI'] が存在しないか空の文字列に設定されます。このような場合、 Zend_Controller_Request_Http は $_SERVER['HTTP_X_REWRITE_URL'] の値を使用します。これは Isapi_Rewrite 拡張モジュールが設定します。
IIS 7.0 ではネイティブの URL リライトモジュールが登場しました。 次のように設定して使います。
- <?xml version="1.0" encoding="UTF-8"?>
- <configuration>
- <system.webServer>
- <rewrite>
- <rules>
- <rule name="Imported Rule 1" stopProcessing="true">
- <match url="^.*$" />
- <conditions logicalGrouping="MatchAny">
- <add input="{REQUEST_FILENAME}"
- matchType="IsFile" pattern=""
- ignoreCase="false" />
- <add input="{REQUEST_FILENAME}"
- matchType="IsDirectory"
- pattern="" ignoreCase="false" />
- </conditions>
- <action type="None" />
- </rule>
- <rule name="Imported Rule 2" stopProcessing="true">
- <match url="^.*$" />
- <action type="Rewrite" url="index.php" />
- </rule>
- </rules>
- </rewrite>
- </system.webServer>
- </configuration>
Lighttpd の場合は、次のようなルールを使用します。
- url.rewrite-once = (
- ".*\?(.*)$" => "/index.php?$1",
- ".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
- "" => "/index.php"
- )
ルータの使用法
Rewrite ルータを適切に使用するには、まずそのインスタンスを作成し、 次にユーザ定義のルーティングを追加し、それをコントローラに注入しなければなりません。 以下にコードの例を示します。
- // ルータを作成します
- $router = $ctrl->getRouter(); // デフォルトで rewrite ルータを返します
- $router->addRoute(
- 'user',
- new Zend_Controller_Router_Route('user/:username',
- 'action' => 'info'))
- );
基本的な RewriteRouter の操作法
RewriteRouter で最も重要なのが、ユーザ定義のルーティングです。 これは、RewriteRouter の addRoute メソッドをコールして追加します。 このメソッドに、Zend_Controller_Router_Route_Interface を実装したクラスの新しいインスタンスを渡します。
- $router->addRoute('user',
- new Zend_Controller_Router_Route('user/:username'));
Rewrite ルータには、6 種類の基本的なルーティング方式があります (そのうちのひとつは特別なものです)。
これらのルーティングは、チェインやユーザ定義のルーティング方式を作成する際に何度も使用します。 任意の設定でお好みの数のルーティングを使用することができますが、 Module ルートだけは例外です。これを使用するのは一度だけで、 もっとも汎用的なルート (デフォルト) として使用します。 個々のルーティング方式については、後ほど詳細に説明します。
addRoute への最初のパラメータはルートの名前です。 これを使用して、ルータがルートを処理します。 たとえば URL の生成などに使用します。 二番目のパラメータはルート自身となります。
注意: ルート名のもっとも一般的な使用例は、 Zend_View の url ヘルパーです。
これは
<a href= "<?php echo $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>user/martelへのリンクとなります。
ルーティング処理は、定義されたすべてのルートから リクエスト URI にマッチする定義を探すことによって行います。 マッチするものが見つかれば、ルートのインスタンスから変数の値が返され、 それを Zend_Controller_Request オブジェクトに注入します。 これを、後にディスパッチャやユーザが作成したコントローラで使用します。 マッチするものが見つからない場合は、チェイン内の次のルートを調べます。
どのルートがマッチしたかを知りたい場合は
getCurrentRouteName() メソッドを使用します。
これは、ルートをルータに登録する際に使用した識別子を返します。
ルートオブジェクトそのものを取得したい場合は
getCurrentRoute() を使用します。
注意: 定義の順番
一番最後にマッチしたルートが適用されるので、 汎用的なルートは最初に定義するようにしましょう。
注意: 返される値
ルーティングの結果返される値は、URL パラメータあるいは ユーザ定義のルータのデフォルト値です。これらの値は、後ほど Zend_Controller_Request::getParam() あるいは Zend_Controller_Action::_getParam() メソッドでアクセスできます。
ルートで使用される変数のうち、'module'、'controller' および 'action' の 3 つは特別な扱いとなります。これらの特殊変数は、Zend_Controller_Dispatcher がディスパッチ先のコントローラとアクションを決定するために使用されます。
注意: 特殊変数
これらの特殊変数の名前を変更することもできます。その場合は Zend_Controller_Request_Http のsetControllerKeyメソッドやsetActionKeyメソッドを使用します。
デフォルトのルート
Zend_Controller_Router_Rewrite がデフォルトのルートとして設定されています。
これは controller/action 形式の URI にマッチします。
さらに、パス要素の最初の部分にモジュール名を指定することができます。つまり
module/controller/action のような URI も可能です。
また、URI にパラメータを追加した形式、つまり
controller/action/var1/value1/var2/value2
のような URI にもデフォルトで対応しています。
ルータのマッチ処理についての例を示します。
- // 以下の設定を前提とします
- $ctrl->setControllerDirectory(
- 'default' => '/path/to/default/controllers',
- 'news' => '/path/to/news/controllers',
- 'blog' => '/path/to/blog/controllers'
- )
- );
- モジュールのみ
- http://example/news
- module == news
- 無効なモジュール名は、コントローラ名として扱われます
- http://example/foo
- controller == foo
- モジュール + コントローラ
- http://example/blog/archive
- module == blog
- controller == archive
- モジュール + コントローラ + アクション
- http://example/blog/archive/list
- module == blog
- controller == archive
- action == list
- モジュール + コントローラ + アクション + パラメータ
- http://example/blog/archive/list/sort/alpha/date/desc
- module == blog
- controller == archive
- action == list
- sort == alpha
- date == desc
デフォルトのルートは、Zend_Controller_Router_Route_Module オブジェクトを 'default' という名前 (インデックス) で RewriteRouter に保存したものです。 これは、以下のようにして作成します。
- $dispatcher,
- $request);
- $this->addRoute('default', $compat);
このデフォルトルートが不要な場合は、独自の 'デフォルト' ルートで上書きします
(つまり、'default' という名前で保存します)。
あるいは、removeDefaultRoutes()
で削除することもできます。
- // すべてのデフォルトルートを削除します
- $router->removeDefaultRoutes();
ベース URL およびサブディレクトリ
Rewrite ルータはサブディレクトリ
(例. http://domain.com/~user/application-root/)
内でも使用可能です。この場合、アプリケーションのベース URL
(/~user/application-root) の自動検出が
Zend_Controller_Request_Http によって行われ、適切に使用されます。
ベース URL の検出に失敗する場合は、
Zend_Controller_Request_Http のメソッド setBaseUrl()
を使用してベースパスを上書き指定することができます
(ベース URL およびサブディレクトリ を参照ください)。
- $request->setBaseUrl('/~user/application-root/');
グローバルパラメータ
グローバルパラメータをルータ内で設定することができます。
これは setGlobalParam
によってルートに自動的に適用されます。
グローバルパラメータが設定されているにもかかわらず
直接メソッドによっても設定された場合は、
ユーザが設定したパラメータのほうがグローバルパラメータより優先されます。
グローバルパラメータは、このように設定します。
- $router->setGlobalParam('lang', 'en');
ルートの型
Zend_Controller_Router_Route
Zend_Controller_Router_Route はフレームワークの標準のルートです。 簡単に利用でき、柔軟なルート定義が可能です。各ルートには、まず (静的および動的な) URL のマッピングが含まれ、 そしてデフォルト値および変数についての制限を指定して初期化します。
とある架空のアプリケーションで、コンテンツの作者情報のページが必要になったとしましょう。
ブラウザで http://domain.com/author/martel
にアクセスした際に、"martel" とかいう人についての情報を見たいわけです。
この機能を実現するためのルートは、次のようになります。
- $route = new Zend_Controller_Router_Route(
- 'author/:username',
- 'controller' => 'profile',
- 'action' => 'userinfo'
- )
- );
- $router->addRoute('user', $route);
Zend_Controller_Router_Route
のコンストラクタの最初のパラメータは、ルートの定義です。
これを URL にマッチさせます。ルート定義は静的な部分と動的な部分で構成され、
それをスラッシュ ('/') で連結します。
静的な部分は単なるテキスト (例. author) です。
動的な部分を変数と呼び、変数名の前にコロンをつけて
(例. :username) 表します。
注意: 文字の使用法
現在の実装では、(スラッシュ以外の) 任意の文字を変数名として使用できます。しかし、 PHP の変数名として使用できる文字だけを用いることを強く推奨します。 このようにしておくことで、 将来実装が変更されたときにバグを引き起こす可能性を抑えられます。
この例のルートは、ブラウザで
'http://domain.com/author/martel' を指した際にマッチします。
この場合、すべての変数の値が Zend_Controller_Request
オブジェクトに注入され、ProfileController からアクセスできるようになります。
この例が返す変数は、以下のようなキーと値のペアを持つ配列となります。
- 'username' => 'martel',
- 'controller' => 'profile',
- 'action' => 'userinfo'
- );
その後、Zend_Controller_Dispatcher は
(デフォルトモジュールの) ProfileController クラスにある
userinfoAction() メソッドを実行します。変数にアクセスするには、
Zend_Controller_Action::_getParam() あるいは
Zend_Controller_Request::getParam() メソッドを使用します。
- public function userinfoAction()
- {
- $request = $this->getRequest();
- $username = $request->getParam('username');
- $username = $this->_getParam('username');
- }
ルート定義には、特殊文字 (ワイルドカード) を含めることができます。これは '*' 記号で表します。 これを使用して、Module ルートと同様にパラメータを扱う (変数名 => 値 のペアを URI で定義する) ことができます。 次のルートは、Module ルートの挙動をまねたものです。
- $route = new Zend_Controller_Router_Route(
- ':module/:controller/:action/*',
- );
- $router->addRoute('default', $route);
変数のデフォルト
ルートで使用するすべての変数についてデフォルト値を指定することができます。 これは、 Zend_Controller_Router_Route のコンストラクタの 2 番目のパラメータで指定します。 このパラメータは、変数名をキーとする配列で、 対応する値にそのデフォルト値を指定します。
- $route = new Zend_Controller_Router_Route(
- 'archive/:year',
- );
- $router->addRoute('archive', $route);
上のルートは 'http://domain.com/archive/2005' および
'http://example.com/archive'
のような URL にマッチします。後者の場合、変数 year にはデフォルト値である
2006 が設定されます。
この例は、year 変数をリクエストオブジェクトに注入することになります。 そしてルーティング情報が存在しない (コントローラやアクションのパラメータが定義されていない) ので、 アプリケーションはデフォルトのコントローラのデフォルトアクションメソッド (ともに Zend_Controller_Dispatcher_Abstract で定義されています) にディスパッチします。より使いやすくするには、 ルートのデフォルトとしてコントローラとアクションを定義しておく必要があります。
- $route = new Zend_Controller_Router_Route(
- 'archive/:year',
- 'year' => 2006,
- 'controller' => 'archive',
- 'action' => 'show'
- )
- );
- $router->addRoute('archive', $route);
このルートは、ArchiveController の
showAction() を実行します。
変数の制約
Zend_Controller_Router_Route のコンストラクタの 三番目のパラメータで、変数の制約を指定することができます。 これは、正規表現で指定します。
上の例のルートでは、year 変数の値が数値データである場合にのみ
Rewrite ルータにマッチします。つまり
http://domain.com/archive/2345 はマッチしますが
http://example.com/archive/test はマッチしません。
この場合はチェイン内の次のルートに処理を移します。
翻訳済みセグメント
標準のルートは、翻訳済みセグメントをサポートします。この機能を使用するには、 次のいずれかの方法で翻訳器 (Zend_Translate のインスタンス) を定義しなければなりません。
-
レジストリに、キー Zend_Translate で格納する
-
静的メソッド Zend_Controller_Router_Route::setDefaultTranslator() で設定する
-
コンストラクタの 4 番目のパラメータとして渡す
デフォルトでは、Zend_Translate のインスタンスで指定したロケールを使用します。これを上書きするには、 (Zend_Locale のインスタンスあるいはロケール文字列で) 次のいずれかの方法で設定します。
-
レジストリに、キー Zend_Locale で格納する
-
静的メソッド Zend_Controller_Router_Route::setDefaultLocale() で設定する
-
コンストラクタの 5 番目のパラメータとして渡す
-
アセンブルメソッドのパラメータ
@localeとして渡す
翻訳済みセグメントはふたつの部分に分かれます。
固定セグメントの前には @ 記号がひとつつき、
アセンブル時に現在のロケールに翻訳され、
マッチングの際にはメッセージ ID に戻されます。
動的セグメントの前には :@ がつきます。
アセンブルの際に、指定したパラメータが翻訳され、
パラメータの位置に挿入されます。
マッチングの際には、URL の翻訳済みパラメータが
メッセージ ID に戻されます。
注意: メッセージ ID と分割された言語ファイル
ルートの中で使いたいメッセージ ID が、 ビュースクリプトやその他の部分ですでに使われていることもあるでしょう。 URL の安全性を確保するには、 ルートで使用するメッセージを別の言語ファイルに分割しなければなりません。
標準のルートで翻訳済みセグメントを使用するための準備として もっともシンプルな方法は、次のようになります。
- // 翻訳器を準備します
- 'year' => 'jahr',
- 'month' => 'monat',
- 'index' => 'uebersicht'),
- 'de');
- // 現在のロケールを翻訳器に設定します
- $translator->setLocale('en');
- // ルートのデフォルト翻訳器として設定します
- Zend_Controller_Router_Route::setDefaultTranslator($translator);
これは、静的セグメントを使用する例です。
動的セグメントを使用すると、 モジュールルートの翻訳済みバージョンを作ることができます。
- // ルートを作成します
- $route = new Zend_Controller_Router_Route(
- ':@controller/:@action/*',
- 'controller' => 'index',
- 'action' => 'index'
- )
- );
- $router->addRoute('archive', $route);
- // URL をデフォルトのロケールでアセンブルします: archive/index/foo/bar
- // URL をドイツ語でアセンブルします: archiv/uebersicht/foo/bar
静的セグメントと動的セグメントを同時に使用することもできます。
- // ルートを作成します
- $route = new Zend_Controller_Router_Route(
- '@archive/:@mode/:value',
- 'mode' => 'year'
- 'value' => 2005,
- 'controller' => 'archive',
- 'action' => 'show'
- ),
- 'value' => '\d+')
- );
- $router->addRoute('archive', $route);
- // URL をデフォルトのロケールでアセンブルします: archive/month/5
- // URL をドイツ語でアセンブルします: archiv/monat/5
Zend_Controller_Router_Route_Static
これまでの例では、すべて動的なルートを使用していました。 つまり、特定のパターンにマッチするものについてのルートです。 しかし、時には特定のルートを固定してしまい、 わざわざ正規表現エンジンを動かしたくない場合もあるでしょう。 そんなときには静的なルートを使用します。
- $route = new Zend_Controller_Router_Route_Static(
- 'login',
- );
- $router->addRoute('login', $route);
上のルートは http://domain.com/login という URL
にマッチし、AuthController::loginAction() にディスパッチされます。
注意: 警告: 静的なルートにはまともなデフォルトが必須
静的なルートは、URL の一部をリクエストオブジェクトへのパラメータとして渡すことはありません。 したがって、リクエストのディスパッチに必要なパラメータは すべてデフォルトでルートに渡すようにしておかなければなりません。 "controller" や "action" のデフォルト値を省略してしまうと予期せぬ結果を引き起こし、 リクエストがディスパッチ不能になってしまうでしょう。
一般に、以下のデフォルト値は常に渡すようにしておきましょう。
オプションで、起動時に "useDefaultControllerAlways" パラメータをフロントコントローラに渡すこともできます。
controller
action
module (デフォルト以外の場合)
しかし、これはあくまでも次善策であり、 デフォルトを明記しておくほうがおすすめです。
$front->setParam('useDefaultControllerAlways', true);
Zend_Controller_Router_Route_Regex
デフォルトのルートや静的なルートに加えて、正規表現によるルートも使用可能です。 このルートは他のものに比べてより強力で柔軟なものですが、 多少複雑になってしまいます。そして、より高速になります。
標準のルートと同様、このルートを初期化する際にはルートの定義とデフォルトを指定する必要があります。 サンプルとして、archive ルートを作成してみましょう。 これは先ほど定義したものとほぼ同じですが、今回は Regex ルートを使用しています。
- $route = new Zend_Controller_Router_Route_Regex(
- 'archive/(\d+)',
- 'controller' => 'archive',
- 'action' => 'show'
- )
- );
- $router->addRoute('archive', $route);
定義された正規表現のパターンが、リクエストオブジェクトに注入されます。
上の例では、http://domain.com/archive/2006
がマッチした後の結果の値は次のような配列になります。
- 1 => '2006',
- 'controller' => 'archive',
- 'action' => 'show'
- );
注意: ルータとのマッチングを行う前に、URL の先頭と最後のスラッシュは取り除かれます。 結果として、URL
http://domain.com/foo/bar/は正規表現foo/barにマッチすることになります。/foo/barにはマッチしません。
注意: 行頭と行末を表す文字 (それぞれ '^' および '$') が、すべての式の前後に自動的に付加されます。 したがって、これらは正規表現で指定する必要はありません。
注意: このルートクラスは、区切り文字として
#を使用します。 つまり、ルート定義の中にハッシュ文字 ('#') がある場合は、それをエスケープする必要があるということです。 スラッシュ ('/') をエスケープする必要はありません。 '#' (アンカー) は通常はウェブサーバに渡されることはないので、 エスケープが必要になることはまずないでしょう。
定義されたサブパターンの内容は、通常通りの方法で取得できます。
- public function showAction()
- {
- $request = $this->getRequest();
- $year = $request->getParam(1); // $year = '2006';
- }
注意: このキーは、文字列 ('1') ではなく数値の 1 であることに注意しましょう。
このルートは、標準のルートとまったく同様に動作するわけではありません。 'year' のデフォルトが設定されていないからです。 また、year のデフォルトを設定してこれをオプション扱いにしたとしても、 最後のスラッシュをどうするかという問題が残ります。 これを解決するには、year 部をスラッシュを含めてオプションにし、 その数値部のみを取得するようにします。
- $route = new Zend_Controller_Router_Route_Regex(
- 'archive(?:/(\d+))?',
- 1 => '2006',
- 'controller' => 'archive',
- 'action' => 'show'
- )
- );
- $router->addRoute('archive', $route);
まだ問題が残っていることにおそらくお気づきでしょう。 パラメータとして数値のキーを使用するのはなかなか難しく、 長い目で見れば問題を引き起こす可能性が高くなります。 そこで三番目のパラメータの登場です。 このパラメータは、正規表現サブパターンとパラメータ名のキーを関連付けます。 簡単な例を見てみましょう。
この結果は次のようになり、これがリクエストオブジェクトに格納されます。
- 'year' => '2006',
- 'controller' => 'archive',
- 'action' => 'show'
- );
関連付けは両方の方法で定義でき、任意の環境 (例. Zend_Config) で動作します。 キーには変数名あるいはサブパターン番号のいずれかを含めることができます。
注意: サブパターンのキーは整数値でなければなりません。
リクエストの値から数値キーが消え、代わりに名前がつけられたことに注目しましょう。 もちろん、お望みなら数値での指定と名前での指定を共用することもできます。
この結果、リクエスト内には数値キーと名前つきキーが共存することになります。
たとえば、URL http://domain.com/archive/2006/page/10
は次のような値になります。
- 'year' => '2006',
- 2 => 10,
- 'controller' => 'archive',
- 'action' => 'show'
- );
正規表現を簡単に反転させることはできないので、 url ヘルパーやこのクラスのメソッドを使用するには 逆の URL を準備しておく必要があります。 逆方向のパスは sprintf() 形式の文字列で表し、 コンストラクタの四番目のパラメータとして指定します。
これまで説明してきたことは、すべて標準のルートオブジェクトでも可能なことです。
それでは、Regex ルートを使用するメリットはいったい何なのでしょう?
これを使用すると、あらゆる形式の URL を制約なしに定義することができます。
仮に、あなたが blog を持っており
http://domain.com/blog/archive/01-Using_the_Regex_Router.html
のような URL を作成したいと考えたとしましょう。
このパスの最後の要素 01-Using_the_Regex_Router.html
から記事の ID とタイトル/説明 を取得するにはどうしたらいいでしょうか?
標準のルートでは不可能でしょう。Regex ルートを使用した場合は、
次のようにすることができます。
regex ルートは標準のルートよりはるかに柔軟性があるということが、 ここからもわかります。
Zend_Controller_Router_Route_Hostname
Zend_Controller_Router_Route_Hostname はホスト名によるルートです。標準のルートと同じように動作しますが、 パスではなくコールされた URL のホスト名に基づいて動作します。
標準のルートの例を使用して、
ホスト名に基づいた動作がどのようなものになるのかを見ていきましょう。
パスを利用してユーザをコールするのではなく、たとえば
http://martel.users.example.com
でユーザ "martel" の情報を見られるようにしたいものとします。
- $hostnameRoute = new Zend_Controller_Router_Route_Hostname(
- ':username.users.example.com',
- 'controller' => 'profile',
- 'action' => 'userinfo'
- )
- );
- $plainPathRoute = new Zend_Controller_Router_Route_Static('');
- $router->addRoute('user', $hostnameRoute->chain($plainPathRoute);
Zend_Controller_Router_Route_Hostname
のコンストラクタの最初のパラメータはルートの定義で、
これがホスト名にマッチします。
ルート定義には静的な部分と動的な部分があり、両者はドット
('.') で区切られています。動的な部分 (変数)
は、変数名の先頭にコロンをつけて :username
のように表します。静的な部分は、user
のように単純なテキストで表します。
hostname ルートを単独で使うこともできますが、決してしてはいけません。
その理由は、hostname ルートはそれ単体だと任意のパスにマッチすることになるからです。
hostname ルートの後には path ルートをつなげなければなりません。
例に示したように、$hostnameRoute->chain($pathRoute);
のようにコールすることになります。こうすると、
$hostnameRoute には何も変更は加えられませんが、新たなルート
(Zend_Controller_Router_Route_Chain) が返されます。
そして、これをルータに渡します。
Zend_Controller_Router_Route_Chain
Zend_Controller_Router_Route_Chain is a route which allows to chain multiple routes together. This allows you to chain hostname-routes and path routes, or multiple path routes for example. Chaining can be done either programatically or within a configuration file.
注意: Parameter Priority
When chaining routes together, the parameters of the outer route have a higher priority than the parameters of the inner route. Thus if you define a controller in the outer and in the inner route, the controller of the outer route will be selected.
When chaining programatically, there are two ways to achieve this. The
first one is to create a new
Zend_Controller_Router_Route_Chain instance and then
calling the chain method multiple times with all routes
which should be chained together. The other way is to take the first
route, e.g. a hostname route, and calling the chain
method on it with the route which should be appended to it. This
will not modify the hostname route, but return a new instance of
Zend_Controller_Router_Route_Chain, which then has both
routes chained together:
// Create two routes
$hostnameRoute = new Zend_Controller_Router_Route_Hostname(...);
$pathRoute = new Zend_Controller_Router_Route(...);
// First way, chain them via the chain route
$chainedRoute = new Zend_Controller_Router_Route_Chain();
$chainedRoute->chain($hostnameRoute)
->chain($pathRoute);
// Second way, chain them directly
$chainedRoute = $hostnameRoute->chain($pathRoute);
When chaining routes together, their default separator is a slash by default. There may be cases when you want to have a different separator:
// Create two routes
$firstRoute = new Zend_Controller_Router_Route('foo');
$secondRoute = new Zend_Controller_Router_Route('bar');
// Chain them together with a different separator
$chainedRoute = $firstRoute->chain($secondRoute, '-');
// Assemble the route: "foo-bar"
echo $chainedRoute->assemble();
Chain Routes via Zend_Config
To chain routes together in a config file, there are additional
parameters for the configuration of those. The simpler approach is
to use the chains parameters. This one is simply a list
of routes, which will be chained with the parent route. Neither the
parent- nor the child-route will be added directly to the router but
only the resulting chained route. The name of the chained route in
the router will be the parent route name and the child route name
concatenated with a dash (-). A simple config in XML would look
like this:
<routes>
<www type="Zend_Controller_Router_Route_Hostname">
<route>www.example.com</route>
<chains>
<language type="Zend_Controller_Router_Route">
<route>:language</route>
<reqs language="[a-z]{2}">
<chains>
<index type="Zend_Controller_Router_Route_Static">
<route></route>
<defaults module="default" controller="index" action="index" />
</index>
<imprint type="Zend_Controller_Router_Route_Static">
<route>imprint</route>
<defaults module="default" controller="index" action="index" />
</imprint>
</chains>
</language>
</chains>
</www>
<users type="Zend_Controller_Router_Route_Hostname">
<route>users.example.com</route>
<chains>
<profile type="Zend_Controller_Router_Route">
<route>:username</route>
<defaults module="users" controller="profile" action="index" />
</profile>
</chains>
</users>
<misc type="Zend_Controller_Router_Route_Static">
<route>misc</route>
</misc>
</routes>
This will result in the three routes www-language-index,
www-language-imprint and
users-language-profile which will only match based on
the hostname and the route misc, which will match with
any hostname.
The alternative way of creating a chained route is via the
chain parameter, which can only be used with the
chain-route type directly, and also just works in the root level:
<routes>
<www type="Zend_Controller_Router_Route_Chain">
<route>www.example.com</route>
</www>
<language type="Zend_Controller_Router_Route">
<route>:language</route>
<reqs language="[a-z]{2}">
</language>
<index type="Zend_Controller_Router_Route_Static">
<route></route>
<defaults module="default" controller="index" action="index" />
</index>
<imprint type="Zend_Controller_Router_Route_Static">
<route>imprint</route>
<defaults module="default" controller="index" action="index" />
</imprint>
<www-index type="Zend_Controller_Router_Route_Chain">
<chain>www, language, index</chain>
</www-index>
<www-imprint type="Zend_Controller_Router_Route_Chain">
<chain>www, language, imprint</chain>
</www-imprint>
</routes>
You can also give the chain parameter as array instead
of separating the routes with a comma:
<routes>
<www-index type="Zend_Controller_Router_Route_Chain">
<chain>www</chain>
<chain>language</chain>
<chain>index</chain>
</www-index>
<www-imprint type="Zend_Controller_Router_Route_Chain">
<chain>www</chain>
<chain>language</chain>
<chain>imprint</chain>
</www-imprint>
</routes>
RewriteRouter での Zend_Config の使用法
新しいルートを追加する際に、
いちいちコードを書き換えるのではなく設定ファイルの変更で対応できると便利でしょう。
そんなときには addConfig() メソッドを使用します。基本的な使用法は、
まず Zend_Config 互換の設定を作成し、それをコードに読み込み、
そして RewriteRouter に渡すことです。
例として、次のような INI ファイルを考えてみましょう。
- [production]
- routes.archive.route = "archive/:year/*"
- routes.archive.defaults.controller = archive
- routes.archive.defaults.action = show
- routes.archive.defaults.year = 2000
- routes.archive.reqs.year = "\d+"
- routes.news.type = "Zend_Controller_Router_Route_Static"
- routes.news.route = "news"
- routes.news.defaults.controller = "news"
- routes.news.defaults.action = "list"
- routes.archive.type = "Zend_Controller_Router_Route_Regex"
- routes.archive.route = "archive/(\d+)"
- routes.archive.defaults.controller = "archive"
- routes.archive.defaults.action = "show"
- routes.archive.map.1 = "year"
- ; あるいは: routes.archive.map.year = 1
上の INI ファイルを、次のようにして Zend_Config オブジェクトに読み込みます。
- $config = new Zend_Config_Ini('/path/to/config.ini', 'production');
- $router = new Zend_Controller_Router_Rewrite();
- $router->addConfig($config, 'routes');
上の例では、INI ファイルの 'routes' セクションを使用してルートを決めるよう、 ルータに指定しています。このセクションの第一レベルのキーがルート名に対応します。 上の例だと 'archive' と 'news' がこれにあたります。 ルートの各エントリには、最低限 'route' エントリとひとつ以上の 'defaults' エントリが必要となります。また、オプションでひとつ以上の 'reqs' ('required' の略) も指定できます。ここで指定したものが、それぞれ Zend_Controller_Router_Route_Interface オブジェクトに対する引数となります。オプションのキー 'type' を使用すると、 特定のルートで使用するルートクラスの型を指定できます。デフォルトでは、これは Zend_Controller_Router_Route となります。上の例では、 'news' ルートで Zend_Controller_Router_Route_Static を使用するようにしています。
ルータのサブクラスの作成
標準の rewrite ルータには、必要となるであろう機能のほとんどが組み込まれています。 もし新しいルータ型を作成する必要があるとすれば、 それは既存のルートに対して新しい機能を追加したり機能を変更したりしたい場合くらいでしょう。
どこかで、既存のものとはまったく異なるルーティング処理が必要となったとしましょう。 そんな場合には Zend_Controller_Router_Interface を使用します。これは、ルータとして最低限必要なひとつのメソッドのみを定義したインターフェイスです。 method.
- interface Zend_Controller_Router_Interface
- {
- /**
- * @param Zend_Controller_Request_Abstract $request
- * @throws Zend_Controller_Router_Exception
- * @return Zend_Controller_Request_Abstract
- */
- public function route(Zend_Controller_Request_Abstract $request);
- }
ルーティング処理は、システムが最初にリクエストを受け取った際に一度だけ行われます。 ルータの役割は、リクエストの内容に応じてコントローラやアクションとオプションパラメータを決定し、 それをリクエストに設定することです。 その後、リクエストオブジェクトがディスパッチャに渡されます。 ルートに対応するディスパッチトークンがない場合は、ルータは何も行いません。
| リクエストオブジェクト |
