Documentation

標準のルータ - Zend_Controller

標準のルータ

導入

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 ルール (のいずれか) で動作するように設計されています。

  1. RewriteEngine on
  2. RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php

あるいは (推奨)

  1. RewriteEngine On
  2. RewriteCond %{REQUEST_FILENAME} -s [OR]
  3. RewriteCond %{REQUEST_FILENAME} -l [OR]
  4. RewriteCond %{REQUEST_FILENAME} -d
  5. RewriteRule ^.*$ - [NC,L]
  6. RewriteRule ^.*$ index.php [NC,L]

Rewrite ルータを IIS ウェブサーバ (バージョン <= 7.0) で使用するには » Isapi_Rewrite を Isapi 拡張モジュールとしてインストールします。そして次のようなルールを記述します。

  1. RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]

Note: IIS Isapi_Rewrite
IIS を使用すると、$_SERVER['REQUEST_URI'] が存在しないか空の文字列に設定されます。このような場合、 Zend_Controller_Request_Http$_SERVER['HTTP_X_REWRITE_URL'] の値を使用します。これは Isapi_Rewrite 拡張モジュールが設定します。

IIS 7.0 ではネイティブの URL リライトモジュールが登場しました。 次のように設定して使います。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3.      <system.webServer>
  4.          <rewrite>
  5.              <rules>
  6.                  <rule name="Imported Rule 1" stopProcessing="true">
  7.                      <match url="^.*$" />
  8.                      <conditions logicalGrouping="MatchAny">
  9.                          <add input="{REQUEST_FILENAME}"
  10.                              matchType="IsFile" pattern=""
  11.                              ignoreCase="false" />
  12.                          <add input="{REQUEST_FILENAME}"
  13.                              matchType="IsDirectory"
  14.                              pattern="" ignoreCase="false" />
  15.                      </conditions>
  16.                      <action type="None" />
  17.                  </rule>
  18.                  <rule name="Imported Rule 2" stopProcessing="true">
  19.                      <match url="^.*$" />
  20.                      <action type="Rewrite" url="index.php" />
  21.                  </rule>
  22.              </rules>
  23.          </rewrite>
  24.      </system.webServer>
  25. </configuration>

Lighttpd の場合は、次のようなルールを使用します。

  1. url.rewrite-once = (
  2.     ".*\?(.*)$" => "/index.php?$1",
  3.     ".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
  4.     "" => "/index.php"
  5. )

ルータの使用法

Rewrite ルータを適切に使用するには、まずそのインスタンスを作成し、 次にユーザ定義のルーティングを追加し、それをコントローラに注入しなければなりません。 以下にコードの例を示します。

  1. // ルータを作成します
  2.  
  3. $router = $ctrl->getRouter(); // デフォルトで rewrite ルータを返します
  4. $router->addRoute(
  5.     'user',
  6.     new Zend_Controller_Router_Route('user/:username',
  7.                                      array('controller' => 'user',
  8.                                            'action' => 'info'))
  9. );

基本的な RewriteRouter の操作法

RewriteRouter で最も重要なのが、ユーザ定義のルーティングです。 これは、RewriteRouter の addRoute メソッドをコールして追加します。 このメソッドに、Zend_Controller_Router_Route_Interface を実装したクラスの新しいインスタンスを渡します。

  1. $router->addRoute('user',
  2.                   new Zend_Controller_Router_Route('user/:username'));

Rewrite ルータには、6 種類の基本的なルーティング方式があります (そのうちのひとつは特別なものです)。

これらのルーティングは、チェインやユーザ定義のルーティング方式を作成する際に何度も使用します。 任意の設定でお好みの数のルーティングを使用できますが、 Module ルートだけは例外です。これを使用するのは一度だけで、 もっとも汎用的なルート (デフォルト) として使用します。 個々のルーティング方式については、後ほど詳細に説明します。

addRoute への最初のパラメータはルートの名前です。 これを使用して、ルータがルートを処理します。 たとえば URL の生成などに使用します。 二番目のパラメータはルート自身となります。

Note: ルート名のもっとも一般的な使用例は、 Zend_View の url ヘルパーです。

  1. <a href=
  2. "<?php echo $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>
これは user/martel へのリンクとなります。

ルーティング処理は、定義されたすべてのルートから リクエスト URI にマッチする定義を探すことによって行います。 マッチするものが見つかれば、ルートのインスタンスから変数の値が返され、 それを Zend_Controller_Request オブジェクトに注入します。 これを、後にディスパッチャやユーザが作成したコントローラで使用します。 マッチするものが見つからない場合は、チェイン内の次のルートを調べます。

どのルートがマッチしたかを知りたい場合は getCurrentRouteName() メソッドを使用します。 これは、ルートをルータに登録する際に使用した識別子を返します。 ルートオブジェクトそのものを取得したい場合は getCurrentRoute() を使用します。

Note: 定義の順番
一番最後にマッチしたルートが適用されるので、 汎用的なルートは最初に定義するようにしましょう。

Note: 返される値
ルーティングの結果返される値は、URL パラメータあるいは ユーザ定義のルータのデフォルト値です。これらの値は、後ほど Zend_Controller_Request::getParam() あるいは Zend_Controller_Action::_getParam() メソッドでアクセスできます。

ルートで使用される変数のうち、'module'、'controller' および 'action' の 3 つは特別な扱いとなります。これらの特殊変数は、Zend_Controller_Dispatcher がディスパッチ先のコントローラとアクションを決定するために使用されます。

Note: 特殊変数
これらの特殊変数の名前を変更することもできます。その場合は Zend_Controller_Request_HttpsetControllerKey() メソッドや setActionKey() メソッドを使用します。

デフォルトのルート

Zend_Controller_Router_Rewrite がデフォルトのルートとして設定されています。 これは controller/action 形式の URI にマッチします。 さらに、パス要素の最初の部分にモジュール名を指定できます。つまり module/controller/action のような URI も可能です。 また、URI にパラメータを追加した形式、つまり controller/action/var1/value1/var2/value2 のような URI にもデフォルトで対応しています。

ルータのマッチ処理についての例を示します。

  1. // 以下の設定を前提とします
  2. $ctrl->setControllerDirectory(
  3.     array(
  4.         'default' => '/path/to/default/controllers',
  5.         'news'    => '/path/to/news/controllers',
  6.         'blog'    => '/path/to/blog/controllers'
  7.     )
  8. );
  9.  
  10. モジュールのみ
  11. http://example/news
  12.     module == news
  13.  
  14. 無効なモジュール名は、コントローラ名として扱われます
  15. http://example/foo
  16.     controller == foo
  17.  
  18. モジュール + コントローラ
  19. http://example/blog/archive
  20.     module     == blog
  21.     controller == archive
  22.  
  23. モジュール + コントローラ + アクション
  24. http://example/blog/archive/list
  25.     module     == blog
  26.     controller == archive
  27.     action     == list
  28.  
  29. モジュール + コントローラ + アクション + パラメータ
  30. http://example/blog/archive/list/sort/alpha/date/desc
  31.     module     == blog
  32.     controller == archive
  33.     action     == list
  34.     sort       == alpha
  35.     date       == desc

デフォルトのルートは、Zend_Controller_Router_Route_Module オブジェクトを 'default' という名前 (インデックス) で RewriteRouter に保存したものです。 これは、以下のようにして作成します。

  1. $compat = new Zend_Controller_Router_Route_Module(array(),
  2.                                                   $dispatcher,
  3.                                                   $request);
  4. $this->addRoute('default', $compat);

このデフォルトルートが不要な場合は、独自の 'デフォルト' ルートで上書きします (つまり、'default' という名前で保存します)。 あるいは、 removeDefaultRoutes() で削除することもできます。

  1. // すべてのデフォルトルートを削除します
  2. $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 およびサブディレクトリを参照ください)。

  1. $request->setBaseUrl('/~user/application-root/');

グローバルパラメータ

グローバルパラメータをルータ内で設定できます。 これは setGlobalParam() によってルートに自動的に適用されます。 グローバルパラメータが設定されているにもかかわらず 直接メソッドによっても設定された場合は、 ユーザが設定したパラメータのほうがグローバルパラメータより優先されます。 グローバルパラメータは、このように設定します。

  1. $router->setGlobalParam('lang', 'en');

ルートの型

Zend_Controller_Router_Route

Zend_Controller_Router_Route はフレームワークの標準のルートです。 簡単に利用でき、柔軟なルート定義が可能です。各ルートには、まず (静的および動的な) URL のマッピングが含まれ、 そしてデフォルト値および変数についての制限を指定して初期化します。

とある架空のアプリケーションで、コンテンツの作者情報のページが必要になったとしましょう。 ブラウザで http://domain.com/author/martel にアクセスした際に、"martel" とかいう人についての情報を見たいわけです。 この機能を実現するためのルートは、次のようになります。

  1. $route = new Zend_Controller_Router_Route(
  2.     'author/:username',
  3.     array(
  4.         'controller' => 'profile',
  5.         'action'     => 'userinfo'
  6.     )
  7. );
  8.  
  9. $router->addRoute('user', $route);

Zend_Controller_Router_Route のコンストラクタの最初のパラメータは、ルートの定義です。 これを URL にマッチさせます。ルート定義は静的な部分と動的な部分で構成され、 それをスラッシュ ('/') で連結します。 静的な部分は単なるテキスト (例. author) です。 動的な部分を変数と呼び、変数名の前にコロンをつけて (例. :username) 表します。

Note: 文字の使用法
現在の実装では、(スラッシュ以外の) 任意の文字を変数名として使用できます。しかし、 PHP の変数名として使用できる文字だけを用いることを強く推奨します。 このようにしておくことで、 将来実装が変更されたときにバグを引き起こす可能性を抑えられます。

この例のルートは、ブラウザで 'http://domain.com/author/martel' を指した際にマッチします。 この場合、すべての変数の値が Zend_Controller_Request オブジェクトに注入され、ProfileController からアクセスできるようになります。 この例が返す変数は、以下のようなキーと値のペアを持つ配列となります。

  1. $values = array(
  2.     'username'   => 'martel',
  3.     'controller' => 'profile',
  4.     'action'     => 'userinfo'
  5. );

その後、Zend_Controller_Dispatcher は (デフォルトモジュールの) ProfileController クラスにある userinfoAction() メソッドを実行します。変数にアクセスするには、 Zend_Controller_Action::_getParam() あるいは Zend_Controller_Request::getParam() メソッドを使用します。

  1. public function userinfoAction()
  2. {
  3.     $request = $this->getRequest();
  4.     $username = $request->getParam('username');
  5.  
  6.     $username = $this->_getParam('username');
  7. }

ルート定義には、特殊文字 (ワイルドカード) を含めることができます。これは '*' 記号で表します。 これを使用して、Module ルートと同様にパラメータを扱う (変数名 => 値 のペアを URI で定義する) ことができます。 次のルートは、Module ルートの挙動をまねたものです。

  1. $route = new Zend_Controller_Router_Route(
  2.     ':module/:controller/:action/*',
  3.     array('module' => 'default')
  4. );
  5. $router->addRoute('default', $route);

変数のデフォルト

ルートで使用するすべての変数についてデフォルト値を指定できます。 これは、 Zend_Controller_Router_Route のコンストラクタの 2 番目のパラメータで指定します。 このパラメータは、変数名をキーとする配列で、 対応する値にそのデフォルト値を指定します。

  1. $route = new Zend_Controller_Router_Route(
  2.     'archive/:year',
  3.     array('year' => 2006)
  4. );
  5. $router->addRoute('archive', $route);

上のルートは 'http://domain.com/archive/2005' および 'http://example.com/archive' のような URL にマッチします。後者の場合、変数 year にはデフォルト値である 2006 が設定されます。

この例は、year 変数をリクエストオブジェクトに注入することになります。 そしてルーティング情報が存在しない (コントローラやアクションのパラメータが定義されていない) ので、 アプリケーションはデフォルトのコントローラのデフォルトアクションメソッド (ともに Zend_Controller_Dispatcher_Abstract で定義されています) にディスパッチします。より使いやすくするには、 ルートのデフォルトとしてコントローラとアクションを定義しておく必要があります。

  1. $route = new Zend_Controller_Router_Route(
  2.     'archive/:year',
  3.     array(
  4.         'year'       => 2006,
  5.         'controller' => 'archive',
  6.         'action'     => 'show'
  7.     )
  8. );
  9. $router->addRoute('archive', $route);

このルートは、ArchiveControllershowAction() を実行します。

変数の制約

Zend_Controller_Router_Route のコンストラクタの 三番目のパラメータで、変数の制約を指定できます。 これは、正規表現で指定します。

  1. $route = new Zend_Controller_Router_Route(
  2.     'archive/:year',
  3.     array(
  4.         'year'       => 2006,
  5.         'controller' => 'archive',
  6.         'action'     => 'show'
  7.     ),
  8.     array('year' => '\d+')
  9. );
  10. $router->addRoute('archive', $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 に戻されます。

Note: メッセージ ID と分割された言語ファイル
ルートの中で使いたいメッセージ ID が、 ビュースクリプトやその他の部分ですでに使われていることもあるでしょう。 URL の安全性を確保するには、 ルートで使用するメッセージを別の言語ファイルに分割しなければなりません。

標準のルートで翻訳済みセグメントを使用するための準備として もっともシンプルな方法は、次のようになります。

  1. // 翻訳器を準備します
  2. $translator = new Zend_Translate(
  3.     array(
  4.         'adapter' => 'array',
  5.         'content' => array(),
  6.         'locale'  => 'en'
  7.     )
  8. );
  9. $translator->addTranslation(
  10.     array(
  11.         'content' =>
  12.             array(
  13.                 'archive' => 'archiv',
  14.                 'year'    => 'jahr',
  15.                 'month'   => 'monat',
  16.                 'index'   => 'uebersicht'
  17.             ),
  18.         'locale'  => 'de'
  19.     )
  20. );
  21.  
  22. // 現在のロケールを翻訳器に設定します
  23. $translator->setLocale('en');
  24.  
  25. // ルートのデフォルト翻訳器として設定します
  26. Zend_Controller_Router_Route::setDefaultTranslator($translator);

これは、静的セグメントを使用する例です。

  1. // ルートを作成します
  2. $route = new Zend_Controller_Router_Route(
  3.     '@archive',
  4.     array(
  5.         'controller' => 'archive',
  6.         'action'     => 'index'
  7.     )
  8. );
  9. $router->addRoute('archive', $route);
  10.  
  11. // URL をデフォルトのロケールでアセンブルします: archive
  12. $route->assemble(array());
  13.  
  14. // URL をドイツ語でアセンブルします: archiv
  15. $route->assemble(array());

動的セグメントを使用すると、 モジュールルートの翻訳済みバージョンを作ることができます。

  1. // ルートを作成します
  2. $route = new Zend_Controller_Router_Route(
  3.     ':@controller/:@action/*',
  4.     array(
  5.         'controller' => 'index',
  6.         'action'     => 'index'
  7.     )
  8. );
  9. $router->addRoute('archive', $route);
  10.  
  11. // URL をデフォルトのロケールでアセンブルします: archive/index/foo/bar
  12. $route->assemble(array('controller' => 'archive', 'foo' => 'bar'));
  13.  
  14. // URL をドイツ語でアセンブルします: archiv/uebersicht/foo/bar
  15. $route->assemble(array('controller' => 'archive', 'foo' => 'bar'));

静的セグメントと動的セグメントを同時に使用することもできます。

  1. // ルートを作成します
  2. $route = new Zend_Controller_Router_Route(
  3.     '@archive/:@mode/:value',
  4.     array(
  5.         'mode'       => 'year'
  6.         'value'      => 2005,
  7.         'controller' => 'archive',
  8.         'action'     => 'show'
  9.     ),
  10.     array('mode'  => '(month|year)'
  11.           'value' => '\d+')
  12. );
  13. $router->addRoute('archive', $route);
  14.  
  15. // URL をデフォルトのロケールでアセンブルします: archive/month/5
  16. $route->assemble(array('mode' => 'month', 'value' => '5'));
  17.  
  18. // URL をドイツ語でアセンブルします: archiv/monat/5
  19. $route->assemble(array('mode' => 'month', 'value' => '5', '@locale' => 'de'));

Zend_Controller_Router_Route_Static

これまでの例では、すべて動的なルートを使用していました。 つまり、特定のパターンにマッチするものについてのルートです。 しかし、時には特定のルートを固定してしまい、 わざわざ正規表現エンジンを動かしたくない場合もあるでしょう。 そんなときには静的なルートを使用します。

  1. $route = new Zend_Controller_Router_Route_Static(
  2.     'login',
  3.     array('controller' => 'auth', 'action' => 'login')
  4. );
  5. $router->addRoute('login', $route);

上のルートは http://domain.com/login という URL にマッチし、 AuthController::loginAction() にディスパッチされます。

Note: 警告: 静的なルートにはまともなデフォルトが必須
静的なルートは、URL の一部をリクエストオブジェクトへのパラメータとして渡すことはありません。 したがって、リクエストのディスパッチに必要なパラメータは すべてデフォルトでルートに渡すようにしておかなければなりません。 "controller" や "action" のデフォルト値を省略してしまうと予期せぬ結果を引き起こし、 リクエストがディスパッチ不能になってしまうでしょう。
一般に、以下のデフォルト値は常に渡すようにしておきましょう。

  • controller

  • action

  • module (デフォルト以外の場合)

オプションで、起動時に "useDefaultControllerAlways" パラメータをフロントコントローラに渡すこともできます。
  1. $front->setParam('useDefaultControllerAlways', true);
しかし、これはあくまでも次善策であり、 デフォルトを明記しておくほうがおすすめです。

Zend_Controller_Router_Route_Regex

デフォルトのルートや静的なルートに加えて、正規表現によるルートも使用可能です。 このルートは他のものに比べてより強力で柔軟なものですが、 多少複雑になってしまいます。そして、より高速になります。

標準のルートと同様、このルートを初期化する際にはルートの定義とデフォルトを指定する必要があります。 サンプルとして、archive ルートを作成してみましょう。 これは先ほど定義したものとほぼ同じですが、今回は Regex ルートを使用しています。

  1. $route = new Zend_Controller_Router_Route_Regex(
  2.     'archive/(\d+)',
  3.     array(
  4.         'controller' => 'archive',
  5.         'action'     => 'show'
  6.     )
  7. );
  8. $router->addRoute('archive', $route);

定義された正規表現のパターンが、リクエストオブジェクトに注入されます。 上の例では、http://domain.com/archive/2006 がマッチした後の結果の値は次のような配列になります。

  1. $values = array(
  2.     1            => '2006',
  3.     'controller' => 'archive',
  4.     'action'     => 'show'
  5. );

Note: ルータとのマッチングを行う前に、URL の先頭と最後のスラッシュは取り除かれます。 結果として、URL http://domain.com/foo/bar/ は正規表現 foo/bar にマッチすることになります。 /foo/bar にはマッチしません。

Note: 行頭と行末を表す文字 (それぞれ '^' および '$') が、すべての式の前後に自動的に付加されます。 したがって、これらは正規表現で指定する必要はありません。

Note: このルートクラスは、区切り文字として '#' を使用します。 つまり、ルート定義の中にハッシュ文字 ('#') がある場合は、それをエスケープする必要があるということです。 スラッシュ ('/') をエスケープする必要はありません。 '#' (アンカー) は通常はウェブサーバに渡されることはないので、 エスケープが必要になることはまずないでしょう。

定義されたサブパターンの内容は、通常通りの方法で取得できます。

  1. public function showAction()
  2. {
  3.     $request = $this->getRequest();
  4.     $year    = $request->getParam(1); // $year = '2006';
  5. }

Note: このキーは、文字列 ('1') ではなく数値の 1 であることに注意しましょう。

このルートは、標準のルートとまったく同様に動作するわけではありません。 'year' のデフォルトが設定されていないからです。 また、year のデフォルトを設定してこれをオプション扱いにしたとしても、 最後のスラッシュをどうするかという問題が残ります。 これを解決するには、year 部をスラッシュを含めてオプションにし、 その数値部のみを取得するようにします。

  1. $route = new Zend_Controller_Router_Route_Regex(
  2.     'archive(?:/(\d+))?',
  3.     array(
  4.         1            => '2006',
  5.         'controller' => 'archive',
  6.         'action'     => 'show'
  7.     )
  8. );
  9. $router->addRoute('archive', $route);

まだ問題が残っていることにおそらくお気づきでしょう。 パラメータとして数値のキーを使用するのはなかなか難しく、 長い目で見れば問題を引き起こす可能性が高くなります。 そこで三番目のパラメータの登場です。 このパラメータは、正規表現サブパターンとパラメータ名のキーを関連付けます。 簡単な例を見てみましょう。

  1. $route = new Zend_Controller_Router_Route_Regex(
  2.     'archive/(\d+)',
  3.     array(
  4.         'controller' => 'archive',
  5.         'action' => 'show'
  6.     ),
  7.     array(
  8.         1 => 'year'
  9.     )
  10. );
  11. $router->addRoute('archive', $route);

この結果は次のようになり、これがリクエストオブジェクトに格納されます。

  1. $values = array(
  2.     'year'       => '2006',
  3.     'controller' => 'archive',
  4.     'action'     => 'show'
  5. );

関連付けは両方の方法で定義でき、任意の環境 (例. Zend_Config) で動作します。 キーには変数名あるいはサブパターン番号のいずれかを含めることができます。

  1. $route = new Zend_Controller_Router_Route_Regex(
  2.     'archive/(\d+)',
  3.     array( ... ),
  4.     array(1 => 'year')
  5. );
  6.  
  7. // あるいは
  8.  
  9. $route = new Zend_Controller_Router_Route_Regex(
  10.     'archive/(\d+)',
  11.     array( ... ),
  12.     array('year' => 1)
  13. );

Note: サブパターンのキーは整数値でなければなりません。

リクエストの値から数値キーが消え、代わりに名前がつけられたことに注目しましょう。 もちろん、お望みなら数値での指定と名前での指定を共用することもできます。

  1. $route = new Zend_Controller_Router_Route_Regex(
  2.     'archive/(\d+)/page/(\d+)',
  3.     array( ... ),
  4.     array('year' => 1)
  5. );

この結果、リクエスト内には数値キーと名前つきキーが共存することになります。 たとえば、URL http://domain.com/archive/2006/page/10 は次のような値になります。

  1. $values = array(
  2.     'year'       => '2006',
  3.     2            => 10,
  4.     'controller' => 'archive',
  5.     'action'     => 'show'
  6. );

正規表現を簡単に反転させることはできないので、 URL ヘルパーやこのクラスのメソッドを使用するには 逆の URL を準備しておく必要があります。 逆方向のパスは sprintf() 形式の文字列で表し、 コンストラクタの四番目のパラメータとして指定します。

  1. $route = new Zend_Controller_Router_Route_Regex(
  2.     'archive/(\d+)',
  3.     array( ... ),
  4.     array('year' => 1),
  5.     'archive/%s'
  6. );

これまで説明してきたことは、すべて標準のルートオブジェクトでも可能なことです。 それでは、Regex ルートを使用するメリットはいったい何なのでしょう? これを使用すると、あらゆる形式の URL を制約なしに定義することができます。 仮に、あなたが blog を持っており http://domain.com/blog/archive/01-Using_the_Regex_Router.html のような URL を作成したいと考えたとしましょう。 このパスの最後の要素 01-Using_the_Regex_Router.html から記事の ID とタイトル/説明 を取得するにはどうしたらいいでしょうか? 標準のルートでは不可能でしょう。Regex ルートを使用した場合は、 次のようにすることができます。

  1. $route = new Zend_Controller_Router_Route_Regex(
  2.     'blog/archive/(\d+)-(.+)\.html',
  3.     array(
  4.         'controller' => 'blog',
  5.         'action'     => 'view'
  6.     ),
  7.     array(
  8.         1 => 'id',
  9.         2 => 'description'
  10.     ),
  11.     'blog/archive/%d-%s.html'
  12. );
  13. $router->addRoute('blogArchive', $route);

regex ルートは標準のルートよりはるかに柔軟性があるということが、 ここからもわかります。

Zend_Controller_Router_Route_Hostname

Zend_Controller_Router_Route_Hostname はホスト名によるルートです。標準のルートと同じように動作しますが、 パスではなくコールされた URL のホスト名に基づいて動作します。

標準のルートの例を使用して、 ホスト名に基づいた動作がどのようなものになるのかを見ていきましょう。 パスを利用してユーザをコールするのではなく、たとえば http://martel.users.example.com でユーザ "martel" の情報を見られるようにしたいものとします。

  1. $hostnameRoute = new Zend_Controller_Router_Route_Hostname(
  2.     ':username.users.example.com',
  3.     array(
  4.         'controller' => 'profile',
  5.         'action'     => 'userinfo'
  6.     )
  7. );
  8.  
  9. $plainPathRoute = new Zend_Controller_Router_Route_Static('');
  10.  
  11. $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は、 複数のルートを一緒にチェーンできるルートです。 これは、たとえばホスト名とルート、パスとルート、または複数のパスとルートをチェーンできます。 チェーンは、プログラム的に、または、構成ファイルの範囲内で行なえます。

Note: パラメータ優先度
ルートを一緒にチェーンするとき、 外側のルートのパラメータは内側のルートのパラメータより高い優先度を持ちます。 そういうわけで、外側のもので、そして、内側のルートでコントローラを定義するなら、 外側のルートのコントローラが選ばれます。

プログラム的にチェーンするとき、これを達成する2つの方法があります。 最初の1つは、Zend_Controller_Router_Route_Chain インスタンスを新規作成して、 そして、一緒にチェーンでつながなければならないルートすべてで chain()メソッドを複数回呼ぶことです。 他の方法は、最初のルート(例えばホスト名のルート)を受け取って、 それに付加されなければならないルートとともに、 そのルート上で chain()メソッドを呼ぶことです。 これはホスト名ルートを修正せずとも、 Zend_Controller_Router_Route_Chainの新規インスタンスを返します。 そして、両方のルートは一緒につながれます。

  1. //ルートを2つ作成
  2. $hostnameRoute = new Zend_Controller_Router_Route_Hostname(...);
  3. $pathRoute     = new Zend_Controller_Router_Route(...);
  4.  
  5. //最初の方法では、チェーン・ルートを通じてそれらをチェーンします。
  6. $chainedRoute = new Zend_Controller_Router_Route_Chain();
  7. $chainedRoute->chain($hostnameRoute)
  8.              ->chain($pathRoute);
  9.  
  10. //次の方法では、それらを直接チェーンします。
  11. $chainedRoute = $hostnameRoute->chain($pathRoute);

ルートを一緒にチェーンするとき、それらの分離記号はデフォルトでスラッシュです。 異なる分離記号にしたい場合があるかもしれません。

  1. //ルートを2つ作成
  2. $firstRoute  = new Zend_Controller_Router_Route('foo');
  3. $secondRoute = new Zend_Controller_Router_Route('bar');
  4.  
  5. //それらを異なる分離記号で一緒にチェーンします。
  6. $chainedRoute = $firstRoute->chain($secondRoute, '-');
  7.  
  8. //ルートを結合します: "foo-bar"
  9. echo $chainedRoute->assemble();

Zend_Configを介したルートのチェーン

構成ファイルでルートをチェーンするために、それらの構成のための付加パラメータがあります。 より単純なアプローチは、chainsパラメータを使うことです。 このものは単にルートの一覧です。そして、それは親ルートでチェーンされます。 親ルートも子供ルートも、結果として生じるチェーンされたルートにだけ直接追加され、 それ以外のルータには追加されません。 ルータでのチェーンされたルートの名前は、 デフォルトでダッシュで連結される親ルート名と子供ルート名です。 XMLでの単純な構成は、このように見えます。

  1. <routes>
  2.     <www type="Zend_Controller_Router_Route_Hostname">
  3.         <route>www.example.com</route>
  4.         <chains>
  5.             <language type="Zend_Controller_Router_Route">
  6.                 <route>:language</route>
  7.                 <reqs language="[a-z]{2}">
  8.                 <chains>
  9.                     <index type="Zend_Controller_Router_Route_Static">
  10.                         <route></route>
  11.                         <defaults module="default" controller="index"
  12.                                   action="index" />
  13.                     </index>
  14.                     <imprint type="Zend_Controller_Router_Route_Static">
  15.                         <route>imprint</route>
  16.                         <defaults module="default" controller="index"
  17.                                   action="index" />
  18.                     </imprint>
  19.                 </chains>
  20.             </language>
  21.         </chains>
  22.     </www>
  23.     <users type="Zend_Controller_Router_Route_Hostname">
  24.         <route>users.example.com</route>
  25.         <chains>
  26.             <profile type="Zend_Controller_Router_Route">
  27.                 <route>:username</route>
  28.                 <defaults module="users" controller="profile" action="index" />
  29.             </profile>
  30.         </chains>
  31.     </users>
  32.     <misc type="Zend_Controller_Router_Route_Static">
  33.         <route>misc</route>
  34.     </misc>
  35. </routes>

これは結果として、ホスト名及びルートmiscに基づいてマッチするだけで、 どんなホスト名ともマッチする3つのルート、 www-language-indexwww-language-imprint及び users-language-profileになります。

チェーンされたルートを作成する別な方法は、 chainパラメータを介することです。 それはチェーン・ルート型とともにのみ直接使うことができ、 さらに root レベルでのみ動作します。

  1. <routes>
  2.     <www type="Zend_Controller_Router_Route_Chain">
  3.         <route>www.example.com</route>
  4.     </www>
  5.     <language type="Zend_Controller_Router_Route">
  6.         <route>:language</route>
  7.         <reqs language="[a-z]{2}">
  8.     </language>
  9.     <index type="Zend_Controller_Router_Route_Static">
  10.         <route></route>
  11.         <defaults module="default" controller="index" action="index" />
  12.     </index>
  13.     <imprint type="Zend_Controller_Router_Route_Static">
  14.         <route>imprint</route>
  15.         <defaults module="default" controller="index" action="index" />
  16.     </imprint>
  17.  
  18.     <www-index type="Zend_Controller_Router_Route_Chain">
  19.         <chain>www, language, index</chain>
  20.     </www-index>
  21.     <www-imprint type="Zend_Controller_Router_Route_Chain">
  22.         <chain>www, language, imprint</chain>
  23.     </www-imprint>
  24. </routes>

コンマでルートを分離する代わりに、 配列としてchainパラメータを与えることもできます

  1. <routes>
  2.     <www-index type="Zend_Controller_Router_Route_Chain">
  3.         <chain>www</chain>
  4.         <chain>language</chain>
  5.         <chain>index</chain>
  6.     </www-index>
  7.     <www-imprint type="Zend_Controller_Router_Route_Chain">
  8.         <chain>www</chain>
  9.         <chain>language</chain>
  10.         <chain>imprint</chain>
  11.     </www-imprint>
  12. </routes>

Zend_Configでチェーン・ルートを構成して、 チェーン名の分離記号をダッシュ以外にしたい場合、 この分離記号を別途指定する必要があります。

  1. $config = new Zend_Config(array(
  2.     'chainName' => array(
  3.         'type'   => 'Zend_Controller_Router_Route_Static',
  4.         'route'  => 'foo',
  5.         'chains' => array(
  6.             'subRouteName' => array(
  7.                 'type'     => 'Zend_Controller_Router_Route_Static',
  8.                 'route'    => 'bar',
  9.                 'defaults' => array(
  10.                     'module'      => 'module',
  11.                      'controller' => 'controller',
  12.                      'action'     => 'action'
  13.                 )
  14.             )
  15.         )
  16.     )
  17. ));
  18.  
  19. //構成追加前にセパレータを設定
  20. $router->setChainNameSeparator('_separator_')
  21.  
  22. //構成を追加
  23. $router->addConfig($config);
  24.  
  25. //そしてルート名はこうなります: chainName_separator_subRouteName
  26. echo $this->_router->assemble(array(), 'chainName_separator_subRouteName');
  27.  
  28. //検証: /foo/bar をエコーします。

Zend_Rest_Route

Zend_Restコンポーネントは、 Zend_Controller_Router_RewriteのためにRESTfulなルートを含みます。 このルートは、HTTPメソッド及びURIをモジュール、 コントローラ及びアクションに変換することにより、 リクエストを割り振る標準化されたルーティング機構を提供します。 下表では、リクエスト・メソッドとURIを割り振る方法の概要を提示します。

Zend_Rest_Route Behavior
メソッド URI Module_Controller::action
GET /product/ratings/ Product_RatingsController::indexAction()
GET /product/ratings/:id Product_RatingsController::getAction()
POST /product/ratings Product_RatingsController::postAction()
PUT /product/ratings/:id Product_RatingsController::putAction()
DELETE /product/ratings/:id Product_RatingsController::deleteAction()
POST /product/ratings/:id?_method=PUT Product_RatingsController::putAction()
POST /product/ratings/:id?_method=DELETE Product_RatingsController::deleteAction()

Zend_Rest_Route 利用法

Zend_Rest_Routeをアプリケーション全てで有効にするには、 構成パラメータ無しで構築して、フロントコントローラにデフォルトのルートとして追加してください。

  1. $front     = Zend_Controller_Front::getInstance();
  2. $restRoute = new Zend_Rest_Route($front);
  3. $front->getRouter()->addRoute('default', $restRoute);

Note: もしZend_Rest_Routeが有効なモジュール、 コントローラまたはアクションにマッチできなければ、FALSEを返します。 そして、ルータはルータのなかの次のルートを使ってマッチを試みます。

特定のモジュールでZend_Rest_Routeを有効にするには、 コンストラクタの3番目の引数としてモジュール名の配列を使って構成します。

  1. $front     = Zend_Controller_Front::getInstance();
  2. $restRoute = new Zend_Rest_Route($front, array(), array('product'));
  3. $front->getRouter()->addRoute('rest', $restRoute);

特定のコントローラでZend_Rest_Routeを有効にするには、 コントローラ名の配列を各モジュールの配列の要素の値として追加します。

  1. $front     = Zend_Controller_Front::getInstance();
  2. $restRoute = new Zend_Rest_Route($front, array(), array(
  3.     'product' => array('ratings')
  4. ));
  5. $front->getRouter()->addRoute('rest', $restRoute);

Zend_Rest_Route with Zend_Config_Ini

To use Zend_Rest_Route from an INI config file, use a route type parameter and set the config options:

  1. routes.rest.type = Zend_Rest_Route
  2. routes.rest.defaults.controller = object
  3. routes.rest.mod = project,user

The 'type' option designates the RESTful routing config type. The 'defaults' option is used to specify custom default module, controller, and/or actions for the route. All other options in the config group are treated as RESTful module names, and their values are RESTful controller names. The example config defines Mod_ProjectController and Mod_UserController as RESTful controllers.

Then use the addConfig() method of the Rewrite router object:

  1. $config = new Zend_Config_Ini('path/to/routes.ini');
  2. $router = new Zend_Controller_Router_Rewrite();
  3. $router->addConfig($config, 'routes');

Zend_Rest_Controller

Zend_Rest_Routeを使う コントローラの開発を助けるか誘導するためには、 Zend_Rest_Controllerからコントローラを拡張してください。 Zend_Rest_Controllerでは、 RESTfulなリソースのために5つの最も一般的に必要とされる操作を 抽象的なアクション・メソッドの形で定義します。

  • indexAction() - リソースのインデックスを取得して、それをビューに割り当てます。

  • getAction() - URIで識別される単一のリソースを取得して、それをビューに割り当てます。

  • postAction() - 単一の新しいリソースを受け取って、その状態を持続します。

  • putAction() - URIで識別される単一のリソースを受け取って、その状態を持続します。

  • deleteAction() - URIで識別される単一のリソースを削除します。

RewriteRouter での Zend_Config の使用法

新しいルートを追加する際に、 いちいちコードを書き換えるのではなく設定ファイルの変更で対応できると便利でしょう。 そんなときには addConfig() メソッドを使用します。基本的な使用法は、 まず Zend_Config 互換の設定を作成し、それをコードに読み込み、 そして RewriteRouter に渡すことです。

例として、次のような INI ファイルを考えてみましょう。

  1. [production]
  2. routes.archive.route = "archive/:year/*"
  3. routes.archive.defaults.controller = archive
  4. routes.archive.defaults.action = show
  5. routes.archive.defaults.year = 2000
  6. routes.archive.reqs.year = "\d+"
  7.  
  8. routes.news.type = "Zend_Controller_Router_Route_Static"
  9. routes.news.route = "news"
  10. routes.news.defaults.controller = "news"
  11. routes.news.defaults.action = "list"
  12.  
  13. routes.archive.type = "Zend_Controller_Router_Route_Regex"
  14. routes.archive.route = "archive/(\d+)"
  15. routes.archive.defaults.controller = "archive"
  16. routes.archive.defaults.action = "show"
  17. routes.archive.map.1 = "year"
  18. ; あるいは: routes.archive.map.year = 1

上の INI ファイルを、次のようにして Zend_Config オブジェクトに読み込みます。

  1. $config = new Zend_Config_Ini('/path/to/config.ini', 'production');
  2. $router = new Zend_Controller_Router_Rewrite();
  3. $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 を使用します。これは、ルータとして最低限必要なひとつのメソッドのみを定義したインターフェイスです。

  1. interface Zend_Controller_Router_Interface
  2. {
  3.   /**
  4.    * @param  Zend_Controller_Request_Abstract $request
  5.    * @throws Zend_Controller_Router_Exception
  6.    * @return Zend_Controller_Request_Abstract
  7.    */
  8.   public function route(Zend_Controller_Request_Abstract $request);
  9. }

ルーティング処理は、システムが最初にリクエストを受け取った際に一度だけ行われます。 ルータの役割は、リクエストの内容に応じてコントローラやアクションとオプションパラメータを決定し、 それをリクエストに設定することです。 その後、リクエストオブジェクトがディスパッチャに渡されます。 ルートに対応するディスパッチトークンがない場合は、ルータは何も行いません。

Copyright

© 2006-2021 by Zend by Perforce. Made with by awesome contributors.

This website is built using zend-expressive and it runs on PHP 7.

Contacts