AppController.phpに関するあれやこれやそれやどれ?

この記事はだいぶ前に書かれたものなので情報が古いかもしれません

※今日の内容は管理人的には未解決問題です。リーマン予想よりは難しくないと思うのでまあいつかは解決されるでしょう。でも今日のところはまだ解決されていないので、それを求めて来た方には大変申し訳ないんですが、ブラウザバックをオススメする所存にございます。

ごめんね。てへぺろ。



Cakephp1.2とか1.3の頃って、app_controller.phpやapp_model.phpはappディレクトリの外に出すように置いてありまたよね。

・app
 ・コントローラーやモデル、ビュー
・cake
 ・コアライブラリ
・app_controller.php
・app_model.php

階層で示すとこんな感じ。

でもCakephp2.0では、app_controller.phpやapp_model.phpは他のコントローラーやモデルのファイルと同じところに置くようになったみたいです。

・app
 ・Controller
  ・AppController.php
  ・その他のコントローラー
 ・Model
  ・AppModel.php
  ・その他のモデル
 ・View他
・Cake
 ・コアライブラリ

あ、前にも言ったけど、2.0からはファイル名は先頭大文字のアンダーバーはキャメルケースにするので、app_controller.phpはAppController.phpになりますね。app_model.phpも同様。

今までと違うとはいえ、この変更は個人的には良いんじゃないかなって思います。AppController.phpだろうと何だろうと、コントローラーはコントローラーで一つのディレクトリにまとめておきたいとは、以前のバージョンのときにも思ってたことがあるので。

まあ、これについては特に言うことはございやせん。

ただねぇ……AppController.phpに関して、上手くいかないこともあるんですよ。僕の場合はあったんですよ。

今日はちょっとその愚痴を聞いてくだせえ。



今まではAppControllerを複数に分けていたんですが……

例えば、ウェブサイトに管理画面とフロント画面があったとして、その二つで挙動がいろいろと違う場合って、結構ありますよね。管理画面の方には全ページ共通でAuth認証を適用させるけど、フロント画面では使わないとかね。

そんなときは、AppControllerを複数に分割した方が便利だったりします。

つまりですね……じゃあ例えば、こんなファイル構成だったとしましょう。

・app
 ・users_controller.php (管理画面)
 ・sample_controller.php (フロント画面)
・cake
 ・コアライブラリ
・app_controller.php
・app_model.php

あ、この構成はバージョン1.2の頃の書き方です。紛らわしくてごめんなさいね。ファイル名がキャメルしてなかったら古いバージョンのこと言ってんだなって思って下さい。

こんな構成の場合、users_controller.phpとsample_controller.phpの中身はこんな感じになりますよね。

//users_controller.php
class UsersController extends AppController {
  var $name = 'Users';
}

//sample_controller.php
class SampleController extends AppController {
  var $name = 'Sample';
}

上記みたいに管理画面とフロント画面でコントローラーが一つずつしかないような場合は良いですけど、どっちもたくさんコントローラーがあって、管理画面の方には全部Auth認証を効かせたいとか思ったら、app_controller.phpのbeforeFilterで共通の制御をまとめて書いたりすると思います。

でもapp_contoller.phpのbeforeFilterにまとめて処理を書くと、フロント画面の方にも影響が出てしまう。その場合分け処理を書いてるとソースがごちゃごちゃになってしまう。それは嫌だ。

そこで、app_controllerを管理画面とフロント画面に分けちゃうわけです。正確には、app_controller.phpとusers_controller.phpやsample_controller.phpの間にもう一つクラスを継承するapp_controller.phpを作るわけですね。

ここでは仮に、管理画面側のapp_controller.phpをadmin_app_controller.php、フロント画面のapp_controller.phpをfront_app_controller.phpとしましょうか。

・app
 ・users_controller.php
 ・sample_controller.php
・cake
 ・コアライブラリ
・admin_app_controller.php
・front_app_controller.php
・app_controller.php
・app_model.php

各コントローラーの中身はこんな感じ。

//users_controller.php
class UsersController extends AdminAppController {
  var $name  = 'Users';
}

//sample_controller.php
class SampleController extends FrontAppController {

}

//admin_app_controller.php
class AdminAppController extends AppController {
  function beforeFilter() {
    //Auth認証の処理を書く
  }
}

//front_app_controller.php
class FrontAppController extends AppController {

}

こうやっておくと、管理画面のみの共通処理はadmin_app_controller.phpに、フロント画面のみの共通処理はfront_app_controller.phpに書けばソースもごちゃごちゃせずに済みます。

ただ、実際やってみれば分かるんですけど、admin_app_controller.phpやfront_app_controller.phpを任意の場所に置きさえすれば自動的に読み込むようになってくれるのかと言うとCakephpの仕様的にそんなことはないようで、app_controller.phpもそれに併せてちょいと書き換えないといけません。

//app_controller.php
App::import('Controller', 'AdminApp');
App::import('Controller', 'FrontApp');
class AppController extends Controller {

}

ようは、app_controller.phpにadmin_app_controller.phpやfront_app_controller.phpのファイルを読み込む処理を加えてやるわけです。こうしておかないと、sample_controller.phpとかを読み込んだ際に『FrontAppControllerなんてクラスねーんじゃボケ』みたいなエラーが返って来ます。

『Fatal Error: Class ‘FrontAppController’ not found in』みたいな感じかな、たぶん。


あかつきのお宿にも小説や音楽を投稿するための管理画面ってのがあるんで、一応こんな感じでapp_controller.phpを二つに分けてたんですよ。

今まではそれで上手くいっておりました。

……が、しかし。



分割すること自体はできたのですが……

冒頭で言った通り、できたことはできたんですが、もう一段階先の解決があるような気がしているので、それがはっきりするまではとりあえず未解決ということで、堪忍ね。

今日のところは、一応の解決をしたってことで、それを記しておきます。


Cakephp2.0になってファイルの置き場は変わりましたので、AppControllerを上と同じように分割したい場合は、こんな感じになります。

・app
 ・Controller
  ・UsersController.php
  ・SampleController.php
  ・AdminAppController.php
  ・FrontAppController.php
  ・AppController.php
 ・Model
  ・AppModel.php
・Cake
 ・コアライブラリ

基本はこれで問題ないはず……なんですが。

試しにこうやってみたら、何度やっても例のFatal Errorが消えないのよ。AdminAppController.phpやFrontAppController.phpが読み込まれてない。

「ああん? 1年9組なんて最初からなかったじゃねーか」っていう、涼宮ハルヒの消失のときの古泉君がいたクラスみたいな扱いになっておりました。

んで……ちょっとコアライブラリの方にあるAppController.phpを見てみたんですが、それを見るとこんな処理が入ってるんですよ。

App::uses('Controller', 'Controller');

これはどうやらクラスを読み込む関数らしいですね。App::import()の代わりと言いますか、前にもuses()っていうクラスだからライブラリだかを読み込むための関数があったんですが、それと似たようなものだと思います。

コアの方を見てみると、AppController.phpに限らず、とにかくあらゆるファイルで最初にまずApp::uses()を使って親クラスとかを読み込んでるみたいなんですよ。AppModelとかHtmlHelperとか、もうとにかく軒並み。

正直、細かく調べてないんで合ってるか分からないんですけど、今まではある程度自動的に必要なクラスを勝手に読み込むような処理になっていたところが、2.0ではあらゆるところで明示的に読み込むようにしたって感じではないかと思います。

だからそれに従って、各コントローラーの先頭で、それぞれの親クラスを読み込めば、一応問題は解決します。

//UsersController.php
App::uses('AdminAppController', 'Controller');
class UsersController extends AdminAppController {

}

//SampleController.php
App::uses('FrontAppController', 'Controller');
class SampleController extends FrontAppController {

}

//AdminAppController.php
App::uses('AppController', 'Controller');
class AdminAppController extends AppController {

}

//FrontAppController.php
App::uses('AppController', 'Controller');
class FrontAppController extends AppController {

}

//ちなみにApp::uses()の書き方
App::uses('クラス名', 'ロケーション');

前はapp_controller.phpでApp::import()をしておけば良かったところを、各コントローラーでクラスを読み込む処理に変えるってことですね。

App::uses()の第2引数に入れるロケーションってのは、置いてあるファイルの場所みたいなものですね。

だからAuthコンポーネントを読み込むとしたら、こんな書き方になりますかね。

App::uses('AuthComponent', 'Controller/Component');

まあ、これで解決したから良いと言えば良いんですけど……。

じゃあ何をもって君は未解決と言っているのかね? と言いますと……。



もっと楽したいじゃない?

2.0からはそういう仕様になったから仕方ないと言われてしまえばそれまでなんですが。

できればAdminAppController.phpとかFrontAppController.phpに2、3行書いとくだけで毎回App::uses()を書かなくても良いようにならないかなぁと思わずにはいられないんですよ。

だって、AppController.phpの場合は読んでくれるんだもん。

//SampleController.php
class SampleController extends AppController {

}

こうやって、直接AppControllerを継承した場合、App::uses()を書かなくてもちゃんと読み込んでくれるんですよね。Fatal Errorが出ないんですよね。

だから今までと同じような、AppController.phpにApp::import()を一回書けば済むような、そんな解決方法があるのではないかと、そんなことを思っておるわけです。

2.0ではそれができないってことが分かれば、それはそれで良いんですけどね。その辺が調査不足なので、今のところは揺らいでる感じでお願いします。




てなわけで、app_controller.php改めAppController.phpに関する考察でした。

次はビュー行きましょうか。ヘルパーとか。

んじゃ、次回もこのチャンネルで一緒に小宇宙(コスモ)を感じようぜ!

AppControllerを分けたときのhelperやcomponentのマージ - 日記の間 | あかつきのお宿 2012年04月05日 11:18:54
[...] この記事を書いた時に、AppControllerを複数に分けてるって話をしているんですが、分けるだけならここに書いたようなことで問題ないんですけど、少しだけ頑張らないといけない問題がありまして……。 というのは、ヘルパーやコンポーネントのマージについてです。 例えば、SampleController.phpでだけ使いたいSampleHelperというヘルパーと、全コントローラーで共通で使いたいUtilHelperというヘルパーがあったとしますね。 [...]
Cakephp2.0について書いた記事のまとめ | 日記の間 | あかつきのお宿 2012年05月07日 16:21:07
[...] ・Cakephp2.0を触ってみたよ ・index.phpを適切に書き直す ・AppController.phpに関するあれやこれやそれやどれ? ・ビューでのヘルパーで変更された部分……の一部 [...]
づや 2012年10月09日 12:24:37
検索でたどりつきました。
2系からは、普通にApp::uses('AppController', 'Controller');を先頭に書くので、問題ないんじゃないでしょうか。
まっち~ 2012年10月11日 10:42:14
おお、まさか知り合いの技術者にこのサイトを見つけてもらえるとは……メルシーボークー光栄の至りですね。

しかし普通に先頭に書くってことは、やっぱり2系はそういう仕様ってことなんですかね。
じゃあ仕方ないのか……。

情報ありがとうございまっする。
もしかしたら何か関連しているかも?