現在開いてるページへのリンクを判別できるHtmlHelperを作る。CakePHP1.3

CakePHPHtmlHelper は、名前の通り html を生成します。
Baker暦の浅い自分は、何で html を普通に書かずわざわざHelperを介するのか、よく分からなかったりします。
でも、組み込みだし、どうも CakePHP 関連の情報を調べてると、HtmlHelper は使うのが当たり前っぽい。
変に自分流でやっちゃうよりは、CakePHP のルールに従ったほうがトラブルも少ないと思うので、一応使います。

<ul>
  <li><a href="/">ホーム</a></li
  ><li><a href="/product" class="current">プロダクト</a></li
  ><li><a href="/faq">よくある質問</a></li
  ><li><a href="/contact">お問い合わせ</a></li>
</ul>

で、ナビゲーションにリンクを作ってる時に、開いてるページへのリンクには、こんな感じで current とかの class属性を付けたりしたい。
ですが、HtmlHelper を使ってるとこれが出来そうにないです。
しょうがないので HtmlHelper を継承して ExHtmlHelper を作り、HtmlHelper->link() をオーバーライドしました。

環境

Mac Mac OS X 10.5.8(Leopard
MAMP 1.7.2
CakePHP 1.3.0
php 5.2.6

コード

APP/views/helpers/ex_html.php
<?php
/**
 * Extended HtmlHelper
 * @author kanonji
 */
class ExHtmlHelper extends HtmlHelper {
    /**
     * Extended HtmlHelper->link() to understand the url is current or not.
     *
     * $options['current'] is additional option key.
     * If no value set, 'current' is set as default.
     * example:
     * $this->ExHtml->link('foo', 'bar', array('class'=>'baz', 'current'=>'qux'))
     * result:
     * <a href="/foo/" class="baz qux">bar</a>
     *
     * @param string $title The content to be wrapped by <a> tags.
     * @param mixed $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
     * @param array $options Array of HTML attributes.
     * @param string $confirmMessage JavaScript confirmation message.
     * @return string An `<a />` element.
     * @access public
     * @link http://book.cakephp.org/view/1442/link
     */
    public function link($title, $url = null, $options = array(), $confirmMessage = false) {
        $current = 'current';
        if(isset($options['current']))
            $current = $options['current'];
        unset($options['current']);
        if ($url === null)
            $str = Router::url($title);
        else
            $str = Router::url($url);
        list($str) = explode('?', $str);
        if(rtrim($str, '/') === rtrim(Router::url(""), '/'))
            $options['class'] = isset($options['class']) ? "{$options['class']} {$current}" : $current;
        return parent::link($title, $url, $options, $confirmMessage);
    }
}
ex_html.php · GitHub


https://gist.github.com/571144/e345173f09e776ce4d179c1c977a374340984775
コードに間違いがあったので修正しました。

APP/app_controller.php
<?php
class AppController extends Controller {
    public $helpers = array('Session', 'Html', 'Form', 'ExHtml');
}

View内で $this->ExHtmlHelper->link() の様に使います。
基本的に HtmlHelper->link() と同じように使えますが、第3引数に渡す配列に $options['current'] を追加してあります。
$options['current']に、現在開いてるページへのa要素に付けたいclass属性を指定できます。

難点

作ってから思ったんですが、これは HtmlHelper を拡張するより、JavaScriptでDOMとか使ってやったほうが良いかもしれません。
今時JavaScriptが動かない環境は少ないですし、もし現在のページへのリンクが判別できなくても、そんなに困らないですし。

ExHtmlHelperという名前が微妙

やってることが HtmlHelper の1つのメソッドに、ちょっとした改良を加えるだけなので、正直適切なHelper名が思いつきません。
名前から機能が予想できない ExHtmlHelper はあんまり良いネーミングじゃないですし。
CakePHP のコアにあるHtmlHelper CAKE/libs/view/helpers/html.php を APP/views/helpers/html.php にコピーすると、APPのほうからロードされるので、コピーしてからlink()メソッドを改造するという手もあります。
この方法なら、HtmlHelper という名前のままに出来ますが、CakePHP本体のマイナーアップデートで、もし HtmlHelper のバグが修正されたら、ちょっと面倒なことになります。
もしくは HtmlHelper だけ更新し忘れたり。
そう考えると、JavaScriptで対応した方が良いかなぁと。折角なので消さずに書くけど。