3.4.5 Routesの設定
Routingは、URLをコントローラのアクションに結びつける(map) する機能です。URL の見ばえをよくし(pretty URL)、より柔軟に設定できるよう、CakePHP に備わっています。routes を使うために Apache の mod_rewrite が必ず必要というわけではありませんが、mod_rewrite を使用すると、アドレスバーに表示される URL が整ったものになります。
後に説明するように、CakePHP 1.2 の Routes は機能が拡張され、非常にパワフルです。
3.4.5.1 デフォルトのルーティング
独自のルートを設定する前に、CakePHP のデフォルトのルートについて知っておく必要があります。CakePHP のデフォルトルーティングによって、どんなアプリケーションでもうまく動くようになっています。URL の中に、アクション名を置くことで、アクションに直接アクセスすることができます。コントローラのアクションに URL を使うことでパラメータを渡すこともできます。
デフォルトのroutesのURLパターン:
http://example.com/コントローラ名/アクション名/パラメータ1/パラメータ2/パラメータ3
/posts/view という URL は、PostsController の view() アクションにマップされます。 /products/view_clearance は、ProductsController の viewClearance() アクションにマップされます。URL でアクション名が指定されていない場合には、index() メソッドが用いられます。
デフォルトのルーティングでは、URLを使ってアクションにパラメータを渡すこともできます。例えば、/posts/view/25 というリクエストは、PostsController 上で view(25) として呼ぶのと同じことになります。
3.4.5.2 名前付きのパラメータ
CakePHP 1.2 の新機能には、パラメータに名前を付けられる、というものがあります。URL を使ってパラメータに名前を付けて、その値を渡すことができます。/posts/view/title:first+post/category:generalというリクエストでは、PostsController の view() アクションが呼ばれます。このアクションでは、title と category というパラメータの値を、それぞれ、$this->passedArgs[‘title’] と $this->passedArgs[‘category’] として受け取ることができます。
参考例としていくつか挙げてみます。
デフォルトのルーティングを使用した、URL からコントローラへのアクションマッピング:
URL: /monkeys/jump
Mapping: MonkeysController->jump();
URL: /products
Mapping: ProductsController->index();
URL: /tasks/view/45
Mapping: TasksController->view(45);
URL: /donations/view/recent/2001
Mapping: DonationsController->view('recent', '2001');
URL: /contents/view/chapter:models/section:associations
Mapping: ContentsController->view();
$this->passedArgs['chapter'] = 'models';
$this->passedArgs['section'] = 'associations';
3.4.5.3 Routes の定義
独自ルートの定義により、アプリケーションを指定した URL で動作させることができるようになります。独自のルーティングは、/app/config/routes.php ファイルで Router::connect() メソッドを使用して定義します。
connect() メソッドは3つのパラメータからなっています。対応させたい URL、独自のルート要素のデフォルトの値、要素と対応させるための正規表現ルールです。
ルート定義の基本的な形式は次のようになります。:
Router::connect(
'URL',
array('paramName' => 'デフォルト値'),
array('paramName' => '正規表現')
)
Router::connect('URL',array('paramName' => 'デフォルト値'),array('paramName' => '正規表現'))
最初のパラメータで、制御する URL をルータに伝えます。URL は、通常のスラッシュで分けられた文字列です。しかし、ワイルドカード(*)や、独自の route 要素(コロンで始まる変数名)を含むことができます。ワイルドカードで対応させたい URL を指定し、route 要素の指定で、コントローラアクションのためのパラメータを集めることができます。
URLを指定した後、connect() の次の2つのパラメータを使って、リクエストが対応した場合に何を行うのかを CakePHP に伝えます。2番目のパラメータは連想配列です。配列のキーは、URL の route 要素にちなんだものか、デフォルト要素(:controller, :action, :plugin)にしてください。配列内の値は、それらのキーに対するデフォルト値です。connect() の3番目のパラメータを考える前に、基本的な例をいくつか見てみましょう。
Router::connect(
'/pages/*',
array('controller' => 'pages', 'action' => 'display')
);
Router::connect('/pages/*',array('controller' => 'pages', 'action' => 'display'));
CakePHP の routes.php ファイルの中(40行目)に、このルートが書かれています。このルートは、/pages/ ではじまるすべての URL にマッチし、それを PagesController(); の display() メソッドに渡します。例えば、 /pages/products というリクエストは、PagesController->display('products') にマップされます。
Router::connect(
'/government',
array('controller' => 'products', 'action' => 'display', 5)
);
Router::connect('/government',array('controller' => 'products', 'action' => 'display', 5));
この2つ目の例は、connect() の2番目のパラメータでデフォルトパラメータを定義する方法を示しています。それぞれのお客様向けに異なる製品がある場合などに route を作成することを考えることができるでしょう。この例では、/products/display/5 としなくても、/government という URL でアクセスできるようになります。
一般的な Router の他の使い方は、コントローラの別名(alias)を定義することです。通常の /users/someAction/5 という URL の代わりに、/cooks/someAction/5 でアクセスさせたいとしましょう。このようなルートの設定は、次のようにすることで簡単に実現できます。
Router::connect(
'/cooks/:action/*', array('controller' => 'users', 'action' => 'index')
);
Router::connect('/cooks/:action/*', array('controller' => 'users', 'action' => 'index'));
この設定は、 Router が /cooks/ で始まる URL を、全て users コントローラに送ることを表しています。
URL が生成されるときも、ルートの設定が適用されます。上述の例にあるルートが最初にマッチした場合、URL として array('controller' => 'users', 'action' => 'someAction', 5) のように指定すると、/cooks/someAction/5 というURL が生成されます。
ルートの中で独自の名前を持つパラメータを使うつもりならば、Router::connectNamed を用いて Router にそれを伝える必要があります。上述の例にあるルートに /cooks/someAction/type:chef のようなURLをマッチさせたい場合は、次のようにします。
Router::connectNamed(array('type'));
Router::connect(
'/cooks/:action/*', array('controller' => 'users', 'action' => 'index')
);
Router::connectNamed(array('type'));Router::connect('/cooks/:action/*', array('controller' => 'users', 'action' => 'index'));
さらに柔軟な追加機能として、カスタム route 要素を指定できます。この機能により、コントローラアクション用のパラメータが、URL のどこにあるのかを定義できるようになります。リクエストがあると、このカスタム route 要素は、コントローラの $this->params の中に入ります。これは、パラメータに名前を付けるのとは異なる仕方で扱われます。違いに注意してください。パラメータに名前を付けた(/コントローラ/アクション/名前:値)ものは、$this->passedArgs の中に入りますが、カスタム route 要素のデータは、$this->params の中に入ります。カスタム route 要素を定義する際には、正規表現によって、URL が正しく作られているか、作られていないかを CakePHP が判断できるようにします。
Router::connect(
'/:controller/:id',
array('action' => 'view'),
array('id' => '[0-9]+')
);
Router::connect('/:controller/:id',array('action' => 'view'),array('id' => '[0-9]+'));
これは、/コントローラ名/id という形で、どんなコントローラからでもモデルを表示(view)できるようにする例です。connect() に指定されている URL には2つの route 要素があります。controller と :id です。:controller 要素は、CakePHP のデフォルト route 要素です。ルータは URL 内のコントローラ名を見つけることができます。:id 要素はカスタム route 要素なので、connect() の3番目のパラメータ内の正規表現で、どうやってマッチさせるのかを明示する必要があります。こうすることで、CakePHP はアクション名ではないと区別でき、URL 内の ID を見つけることができます。
この route が定義されると、/apples/5 というリクエストは、/apples/view/5 としたのと同じことになります。どちらも、ApplesController の view() メソッドを呼びます。view() メソッド内では、$this->params['id'] を使って渡された ID を知ることができます。
この例も知れば、routing の達人です。
Router::connect(
'/:controller/:year/:month/:day',
array('action' => 'index', 'day' => null),
array(
'year' => '[12][0-9]{3}',
'month' => '0[1-9]|1[012]',
'day' => '0[1-9]|[12][0-9]|3[01]'
)
);
Router::connect('/:controller/:year/:month/:day',array('action' => 'index', 'day' => null),array('year' => '[12][0-9]{3}','month' => '0[1-9]|1[012]','day' => '0[1-9]|[12][0-9]|3[01]'));
ややこしいものですが、route が非常に強力であることを示す例です。この URL では、4つの要素があります。最初のものはよく知っているもので、まずコントローラ名が来ることを CakePHP に伝えます。
次に、いくつかのデフォルト値を指定します。コントローラ名に関係なく、index() アクションが呼ばれるようにします。そして、day パラメーター(URL の4つ目の要素)のデフォルト値を null として、これがオプションであることを設定します。
最後に、年(year)、月(month)、日(day)の数値にマッチする正規表現を指定します。
設定すると、この route は、/articles/2007/02/01、/posts/2004/11/16、/products/2001/05 (day パラメータは、オプション)といった URL にマッチするようになります。対応するコントローラの index() アクションに渡し、$this->params の中にはカスタム指定した日付パラメータを入れます。
3.4.5.4 アクションにパラメータを渡す
アクション(action)が次のように定義されていて、$this->params['id'] の代わりに $articleID を使用して引数を受け取りたいと仮定します。ただ、Router::connect() の第3引数に特別な配列(array)を追加するだけです。
// some_controller.php
function view($articleID = null, $slug = null) {
// ここに何かしらコードを書く
}
// routes.php
Router::connect(
// E.g. /blog/3-CakePHP_Rocks
'/blog/:id-:slug',
array('controller' => 'blog', 'action' => 'view'),
array(
// これは単にアクション(action)で ":id" を $articleID にマッピングするため、順番は重要です。
'pass' => array('id', 'slug'),
'id' => '[0-9]+'
)
)
// some_controller.phpfunction view($articleID = null, $slug = null) {// ここに何かしらコードを書く}// routes.phpRouter::connect(// E.g. /blog/3-CakePHP_Rocks'/blog/:id-:slug',array('controller' => 'blog', 'action' => 'view'),array(// これは単にアクション(action)で ":id" を $articleID にマッピングするため、順番は重要です。'pass' => array('id', 'slug'),'id' => '[0-9]+'))
これで逆のルーティング機能が追加されたので、以下のように url 配列に渡すことができます。ルートで定義されているので、 Cake は URL をどのように作成したらよいかがわかっています。
// view.ctp
// これは /blog/3-CakePHP_Rocks へのリンクを返すでしょう
<?= $html->link('CakePHP Rocks', array(
'controller' => 'blog',
'action' => 'view',
'id' => 3,
'slug' => Inflector::slug('CakePHP Rocks')
)) ?>
// view.ctp// これは /blog/3-CakePHP_Rocks へのリンクを返すでしょう<?= $html->link('CakePHP Rocks', array('controller' => 'blog','action' => 'view','id' => 3,'slug' => Inflector::slug('CakePHP Rocks'))) ?>
3.4.5.5 プレフィックスルーティング(Prefix Routing)
たいていのアプリケーションには、権限のあるユーザだけが情報を変更できる管理画面が必要です。そのために /admin/users/edit/5 のような特別な URL を準備することがよくあります。CakePHP では、コア設定ファイル内の Routing.admin に管理パスを設定して admin ルーティングを有効にできます。
Configure::write('Routing.admin', 'admin'); Configure::write('Routing.admin', 'admin');
コントローラ(controller)内では、プレフィックスとして admin_ をメソッドの前につけたすべてのアクション(action)が呼び出されます。users を例にすると、/admin/users/edit/5 というURLへのアクセスで、UsersController の admin_edit メソッドが、引数5で呼び出されます。このビューファイルはapp/views/users/admin_edit.ctpとなります。
URL /admin を、pagesコントローラの admin_indexアクションにマップするには次のような route を使います。
Router::connect('/admin', array('controller' => 'pages', 'action' => 'index', 'admin' => true)); Router::connect('/admin', array('controller' => 'pages', 'action' => 'index', 'admin' => true));
複数のプレフィックスを使うように Router を設定することもできます。
Router::connect('/profiles/:controller/:action/*', array('prefix' => 'profiles', 'profiles' => true)); Router::connect('/profiles/:controller/:action/*', array('prefix' => 'profiles', 'profiles' => true));
profiles に対するすべての呼び出しは、profiles_ プレフィックスのついたメソッドを探して呼び出します。usersを例にとると、/profiles/users/edit/5 などの構造を持つURLは、UsersController の profiles_edit メソッドを呼び出します。
:controllerと似ている:prefixという名前のエレメントを使って、ワイルドカードプレフィックスルーティングをすることができます。以下のルートを使えば、apiコントローラのuser_createアクションに、/api/user/createというURLをマッピングすることができます。
Router::connect('/api/:prefix/:action', array('controller' => 'api')); Router::connect('/api/:prefix/:action', array('controller' => 'api'));
その他、apiコントローラのquestion_createアクションに/api/question/create、apiコントローラのquestion_editアクションに/api/question/editをマッピングすることもできます。
また、重要な点として、HTML ヘルパーを使用してリンクを作成するなら、プレフィックスを用いた呼び出しの管理も楽になります。HTML ヘルパーを使ったリンクの例は次のようになります。
echo $html->link('Edit your profile', array('profiles' => true, 'controller' => 'users', 'action' => 'edit', 'id' => 5)); echo $html->link('Edit your profile', array('profiles' => true, 'controller' => 'users', 'action' => 'edit', 'id' => 5));
この方法を使うと、複数のプレフィックスルートを定義することができ、柔軟な URL 構造を持つアプリケーションを作成できます。
3.4.5.6 Plugin routing
このセクションには保留されている変更があります. More information about translations
Plugin routing uses the plugin key. You can create links that point to a plugin by adding the plugin key to your url array.
echo $html->link('New todo', array('plugin' => 'todo', 'controller' => 'todo_items', 'action' => 'create'));
echo $html->link('New todo', array('plugin' => 'todo', 'controller' => 'todo_items', 'action' => 'create'));
Conversely if the active request is a plugin request and you want to create a link that has no plugin you can do the following.
echo $html->link('New todo', array('plugin' => null, 'controller' => 'users', 'action' => 'profile'));
echo $html->link('New todo', array('plugin' => null, 'controller' => 'users', 'action' => 'profile'));
By setting plugin => null you tell the Router that you want to create a link that is not part of a plugin.
3.4.5.7 File extensions
このセクションには保留されている変更があります. More information about translations
To handle different file extensions with your routes, you need one extra line in your routes config file:
Router::parseExtensions('html', 'rss');
Router::parseExtensions('html', 'rss');
This will tell the router to remove any matching file extensions, and then parse what remains.
If you want to create a URL such as /page/title-of-page.html you would create your route as illustrated below:
Router::connect(
'/page/:title',
array('controller' => 'pages', 'action' => 'view'),
array(
'pass' => array('title')
)
);
Router::connect('/page/:title',array('controller' => 'pages', 'action' => 'view'),array('pass' => array('title')));
Then to create links which map back to the routes simply use:
$html->link('Link title', array('controller' => 'pages', 'action' => 'view', 'title' => Inflector::slug('text to slug', '-'), 'ext' => 'html'))
$html->link('Link title', array('controller' => 'pages', 'action' => 'view', 'title' => Inflector::slug('text to slug', '-'), 'ext' => 'html'))

