久しぶりにcakeの記事を書きます。

最近、cakePHP以外のフレームワークにも浮気しようとしているのですが勇気がでません。

要件は悩み | チャゲってる日々 – @tyageにて書いてあります。
一応解決したのですが、先ほどの記事を今読むと、かなり意味不明な内容になっているため、簡単にまとめてみます。

やりたかったこと

タイトルにあるように、アクセスした際のURLにあるパラメータを保持する方法を探していました。

具体的には、

「/users/view/1」というURLにアクセスして表示される「/users/mail/1」というリンクが、
「/iframe/users/view/1」というURLでアクセスすると、「/iframe/users/mail/1」というリンクに変わるといいなーという話です。

リンクに限らず、formの送信先URL等や、controller側の操作($this->flashとか)でも適用されてほしいので、
自作Helperを作るだけでは対応しきれません。

Prefix Routing

cakePHPにはPrefix Routing機能があります。

その機能の説明は省きますが、その機能によりHTMLヘルパーを使用した際等に、URLに使用中のprefixをつけることができます。

例えば、adminというprefixを使用している時に、

// APP/views/pages/index.ctp,
// APP/views/pages/admin_index.ctp
 
<?= $html->link('hello', array('controller' => 'users', 'action' => 'add')) ?>

という2つの内容の同じviewがあるとします。

その時、/pages/indexというURLでアクセスすると、リンク先は「/users/add」となりますが、
/admin/pages/indexというURLの場合、リンク先は「/admin/users/add」と、prefixのadminが維持される訳です。

しかしここで当たり前なのですが厄介なのが、先ほどの例でadminというprefixをつけた場合、
呼び出されるactionは「index」から「admin_index」となり、
呼び出されるviewも「index.ctp」から「admin_index.ctp」となるため、
prefixごとにactionとviewを作成する必要が出てきます。

なので、今回のように単純にURLにprefixを維持させたい場合にprefixを使うと、無駄な手間がかかることが分かります。

対策

今回の場合、prefix機能が働いてほしいのは、URLを整形する際です。
(URLを整形たびに、現在有効なprefixを調べて付加しているようです。)

逆に、呼び出すactionを呼び出すときにprefixが働いてしまうと、やっかいなことになります。

そこで、呼び出すactionが決定した後にprefixを再指定すればいいのでは、という結論に至りました。

とりあえずcakeのコードを調べた結果、以下のようにすれば望んでいたことができるようになりました。

// config/routes.php
 
<?php
...
Router::connect('/iframe/:controller', array('iframe' => true));
Router::connect('/iframe/:controller/:action/*', array('iframe' => true));
// controllers/app_controller.php
 
<?php
...
function beforeFilter() {
	if (!empty($this->params['iframe'])) {
		$this->layout = 'iframe';
		Configure::write('Routing.prefixes', array('iframe'));
		$router =& Router::getInstance();
		$router->__setPrefixes();
	}
}

「config/routes.php」にてiframe prefixがあるかどうかをパラメータで指定し、
その後controller側で無理やり新しいprefixをRouterに叩き込んでいます。

これで、「インラインフレームでアクセスしたときだけレイアウトを変える」ということができます!
(※ただしそのアプリ内のURLに限る)

結論

やっぱり上手く説明できない