院生エンジニアのにっき

  • Change style to Blue
  • Change style to Red
  • Change style to Green
  • Change style to Pink

CakePHPで存在しないcontrollerが指定された際にデフォルトのcontrollerクラスを利用する方法   2007-05-12

現在作成中のBlogシステム(http://life-hack.jp/blog/charly)はCakePHPで作られているのですが、HatenaDiaryの仕組みを元にしているため、最後の「charly」の部分はユーザーIDとなっています。

しかし、CakePHPは不便なことにルートディレクトリ(この場合は「http://life-hack.jp/blog/」)の次のディレクトリ(この場合は「charly」)をコントローラークラスの名称だと認識してCharlyControllerを探しに行きます。

当然CharlyControllerは見つからないため「Missing Controller」となるわけです。

Missing Controllerを対処するためには

  1. 指定のcontrollerクラスが存在する
  2. /app/config/routes.phpをいじって指定のcontrollerクラスに値を渡せるようにする

の2通りが考えられます。

今回の目標ははBlogControllerに引数として「charly」とそれ以降の引数を渡すことです。


この対処法として色々試してみました。

  • /app/webroot/index.phpを改造+routes.phpにRouter::connect('/blog/*', array('controller' => 'blog', 'action' => 'index'));と記述
    • /app/webroot/index.phpにてDispatcherクラスを作成しているため、Dispathcerクラスにわたる前にURLから「charly」にあたる部分を抽出する
    • →うまくはいくのですが、URLがどうしても「http://life-hack.jp/blog/blog/」となってしまうので却下
  • /cake/libs/router.phpを修正する
    • Cakeのコア部分を修正します。
    • しかし・・・断念しました。
    • URLは「http://life-hack.jp/blog/charly」になるんですが、HtmlHelperなどで使われている$html->linkなどは全てrouterクラスを通してURLを作成していました。PaginatorHelperもrouterクラスを経由しているので、うまく作らないとCakePHP1.2系の恩恵が受けられなくなります。自分の場合はrouter.phpを下手に修正しようとしたせいでPaginateの次へ・前へのURLが利用できなくなるなどの不具合がありました。
  • /app/config/routes.phpをごりごり書く
    • 無理です・・・
    • routes.phpが正規表現によって記述しないといけないため、「いずれかの文字列(存在するcontrollerクラス)以外にマッチする文字列の場合」という指定がとてもとても大変です。
    • 存在する全てのcontrollerクラスに対するroutes.phpへのRoute::connectを書いた後にRouter::connect('/*', array('controller' => 'blog', 'action' => 'index'));を書くことで半分うまくはいきますが、様々な箇所でひずみが生じます・・・orz..


まぁ色々試してはみたんですが、一番あっさりしてうまくいっているのは

Dispatcherクラス(/cake/dispatcher.php)の修正でした。

CakePHPのだいぶコアな部分なので修正が広範囲に広がってしまうのではないかとびくびくしていましたが、要は「Missing Controller」がでる際にデフォルトのcontrollerクラスをロードして指定してしまえばいいんだ・・・ということでした。


ってなわけでどんな修正をしたかというと・・・(タブを空白2個に変換しています)

  1. <?
  2. //下準備(/app/config/bootstrap.phpに記載→bootstrapに定数を指定するべきかの議論はここでは割愛)
  3. define('DEFAULT_CONTROLLER','blog'); //デフォルトで使用するcontroller名
  4. define('DEFAULT_CONTROLLER_ACTION','index'); //そのcontrollerで使用するaction名
  5.  
  6. ///cake/dispatcher.phpの91行目以降(Revision: 4758にて)
  7. if (empty($params['controller'])) {
  8.   $missingController = true;
  9. } else {
  10.   $ctrlName = Inflector::camelize($params['controller']);
  11.   $ctrlClass = $ctrlName.'Controller';
  12.  
  13.   if (!loadController($ctrlName)) {
  14.     $pluginName = Inflector::camelize($params['action']);
  15.     if (!loadPluginController(Inflector::underscore($ctrlName), $pluginName)) {
  16.       if(preg_match('/([\\.]+)/', $ctrlName)) {
  17.         Router::setRequestInfo(array($params, array('base' => $this->base, 'webroot' => $this->webroot)));
  18.  
  19.         return $this->cakeError('error404',
  20.                         array(array('url' => strtolower($ctrlName),
  21.                             'message' => 'Was not found on this server',
  22.                             'base' => $this->base)));
  23.       //controllerクラスが見つからない場合
  24.       } elseif(!class_exists($ctrlClass)) {
  25.         //変更ここから
  26.         $ctrlName = Inflector::camelize(DEFAULT_CONTROLLER);
  27.         //強引にデフォルトのcontrollerクラスをロード
  28.         if(!loadController($ctrlName)){
  29.           $missingController = true;
  30.         }else{
  31.           $ctrlClass = $ctrlName.'Controller';
  32.           //このままではパラメータが渡らないので新しいパラメータをURLから指定(若干の疑問あり)
  33.           //ここを変更すれば、controllerクラスへ渡るパラメータを修正できる。
  34.           $newParams = split('/',$url);
  35.           $params['pass'] = $newParams;
  36.           $params['controller'] = DEFAULT_CONTROLLER;
  37.           $params['action'] = DEFAULT_CONTROLLER_ACTION;
  38.         }
  39.         //変更ここまで
  40.         //$missingController = true;   //コメントアウト
  41.       } else {
  42.         $params['plugin'] = null;
  43.         $this->plugin = null;
  44.       }
  45.     } else {
  46.       $params['plugin'] = Inflector::underscore($ctrlName);
  47.     }
  48.   } else {
  49.     $params['plugin'] = null;
  50.     $this->plugin = null;
  51.   }
  52. }
  53. ?>

しかしやはりCakeコア部分を修正するのには気が引けました。

現在はこれでうまくいっていますが、もしかすると将来的に機能追加などした際に変な歪が生じるかも知れません。

やはり一番スマートそうなのはroutes.phpの修正で、「指定したcontrollerクラス以外ならこのクラスを利用する」といった指定が簡単にできることでしょうか・・・。

なにかスマートな方法を知っている・思いついた方がおられましたら連絡いただければ幸いです。


コメントを書く