コントローラは MVC の 'C' です。ルーティングが適用されて適切なコントローラが見つかった後、 コントローラのアクションが呼ばれます。コントローラはリクエストを解釈して操作し、適切なモデルが 呼ばれるのを確認して、正しいレスポンスまたはビューを書き出します。コントローラはモデルとビューの 仲介者とみなすことが出来ます。コントローラは薄くシンプルに、モデルを大きくしましょう。 そうすれば、あなたの書いたコードは再利用しやすくなり、そして簡単にテスト出来るでしょう。
一般的に、コントローラは1つのモデルのロジックを管理するために使われます。 たとえば、オンラインベーカリーのサイトを構築しようとしている場合、レシピやその材料を管理する RecipesController と IngredientsController を作るでしょう。 コントローラは複数のモデルを扱う場合でも問題なく動作しますが、CakePHP では 主に操作するモデルにちなんで、コントローラの名前が付けられます。
アプリケーションのコントローラは AppController
クラスを継承し、そしてそれは
Controller
クラスを継承しています。 AppController
クラスは
/app/Controller/AppController.php
に定義し、アプリケーションのコントローラ全体で
共有されるメソッドを含めるようにしましょう。
コントローラは、リクエストを操作するための アクション と呼ばれるいくつかのメソッドを提供します。 デフォルトで、コントローラ上のすべての public メソッドはアクションとなり、URL からアクセスが出来ます。 アクションはリクエストを解釈してレスポンスを返す役割があります。 普通、レスポンスは描画されたビューの形式となっていますが、同様のレスポンスを作成する方法は他にもあります。
冒頭で述べたように、 AppController
クラスはアプリケーションのすべてのコントローラの
親クラスとなります。 AppController
はそれ自身、CakePHP のコアライブラリに含まれる
Controller
クラスを継承しています。
AppController
は /app/Controller/AppController.php
に次のように定義されます。
class AppController extends Controller {
}
AppController
で作られたクラス変数とメソッドはアプリケーション中のすべてのコントローラで
有効となります。コントローラ共通のコードを AppController
に書くのが理想的です。
コンポーネントは多くのコントローラ (必ずしもすべてのコントローラとは限りません) で使われるコードを
まとめるのに使われます (コンポーネントについてはあとで学びます)。
コントローラにある特定の変数が指定された場合には、CakePHP は通常のオブジェクト指向の継承ルールが
適用された上で、少し特別な動作をします。コントローラで使用されるコンポーネントとヘルパーは
特別に扱われます。この時、 AppController
の配列の値と子コントローラの配列の値でマージされます。
常に子クラスの値が AppController
の値を上書きします。
AppController
で $helpers
変数を定義したら、デフォルトで
Html ヘルパーと Form ヘルパーが追加されます。
また、子コントローラのコールバック中で AppController
のコールバックを呼び出すのは、
このようにするのがベストです。
public function beforeFilter() {
parent::beforeFilter();
}
CakePHP アプリケーションにリクエストがあった時、CakePHP の Router
クラスと
Dispatcher
クラスは適切なコントローラを見つけて、それを生成するために
ルーティングの設定 を使います。リクエストデータはリクエストオブジェクトの中に
カプセル化されています。CakePHP は、すべての重要なリクエスト情報を $this->request
プロパティにセットします。CakePHP のリクエストオブジェクトについてのより詳しい情報は
CakeRequest セクションを参照してください。
コントローラのアクションは、リクエストパラメータを、要求を送ってきたブラウザやユーザーに対する レスポンスに変換する役割があります。CakePHP は規約に則ることで、このプロセスを自動化し、 本来であればあなたが書かなければならなかったコードを省いてくれます。
CakePHP は規約に従って、アクション名のビューを描画します。オンラインベーカリーのサンプルに
戻ってみてみると、 RecipesController は view()
, share()
, search()
アクションが
含まれています。このコントローラは /app/Controller/RecipesController.php
にあり、
次のようなコードになっています。
# /app/Controller/RecipesController.php
class RecipesController extends AppController {
public function view($id) {
//action logic goes here..
}
public function share($customerId, $recipeId) {
//action logic goes here..
}
public function search($query) {
//action logic goes here..
}
}
これらのアクションのビューは app/View/Recipes/view.ctp
、
app/View/Recipes/share.ctp
、 app/View/Recipes/search.ctp
にあります。
規約に従ったビューのファイル名は、アクション名を小文字にしてアンダースコアでつないだものです。
通常、コントローラのアクションは View
クラスがビューを描画するために使うコンテキストを
作るために set()
を使います。CakePHP の規約に従うと、手動でビューを
描画したり生成したりする必要はありません。コントローラのアクションが完了すると、CakePHP はビューの
描画と送信をします。
もしデフォルトの動作をスキップさせたければ、次のテクニックを使えばビューを描画するデフォルトの動作を バイパスできます。
コントローラのアクションから文字列もしくは文字列に変換可能なオブジェクトを返した場合、 その文字列がレスポンスのボディとして使われます。
レスポンスとして CakeResponse
を返すことが出来ます。
コントローラのメソッドが requestAction()
から呼ばれた時、
文字列ではないデータを返したい場合があると思います。もし通常のウェブのリクエストからも
requestAction からも呼ばれるコントローラのメソッドがあれば、値を返す前にリクエストタイプを
チェックしましょう。
class RecipesController extends AppController {
public function popular() {
$popular = $this->Recipe->popular();
if (!empty($this->request->params['requested'])) {
return $popular;
}
$this->set('popular', $popular);
}
}
上記のコントローラのアクションは requestAction()
と
通常のリクエストとで、メソッドがどのように使われるかの例です。requestAction ではない
通常のリクエストに配列のデータを戻り値として返せば、エラーの原因になるのでやめましょう。
requestAction()
のより詳しい情報については
requestAction()
のセクションを参照してください。
アプリケーションでコントローラを効率的に使うために、CakePHP のコントローラから提供される いくつかのコアな属性やメソッドを説明しましょう。
CakePHP のコントローラは、リクエストのライフサイクル周りにロジックを挿入できる コールバック関数がついています。
この関数はコントローラの各アクションの前に実行されます。 アクティブセッションのチェックや、ユーザー権限の検査をするために役立ちます。
注釈
beforeFilter() メソッドはアクションが存在しない場合や scaffold アクションの場合でも呼ばれます。
コントローラのアクションの後で、ビューが描画される前に呼ばれます。
このコールバックはあまり使われませんが、アクションの最後で
render()
を手動で呼び出した場合に使うことがあるかもしれません。
コントローラのアクションの後で、描画が完了した後に呼ばれます。 これはコントローラの最後に実行されるメソッドです。
コントローラのコールバックに加えて、 コンポーネント も同じようなコールバックを 提供します。
コントローラのメソッドとその説明については、 CakePHP API を確認してください。
コントローラはビューとお互いに影響しあっています。最初に、コントローラは
set()
を使って、ビューにデータを渡すことが出来ます。
どのビュークラスを使うか、どのビューを描画すべきか、を決めることも出来ます。
set()
メソッドはコントローラからビューへデータを渡すための主な方法です。
set()
を一度使えば、その変数はビュー内でアクセスできるようになります。
// まずコントローラからデータを渡します
$this->set('color', 'pink');
// すると、ビューでそのデータを利用できます
?>
You have selected <?php echo $color; ?> icing for the cake.
set()
メソッドは最初のパラメータに連想配列も指定できます。
この方法はデータのかたまりを簡単にビューに割り当てる方法としてよく使われます。
$data = array(
'color' => 'pink',
'type' => 'sugar',
'base_price' => 23.95
);
// ビューで $color, $type, $base_price の変数が使えるようになります
$this->set($data);
$pageTitle
という変数はもう存在しません。タイトルをセットするには
set()
を使ってください。
$this->set('title_for_layout', 'This is the page title');
バージョン 2.5 から $title_for_layout は非推奨です。代わりにビューブロックを使用してください。
render()
メソッドは各アクションの最後に自動的に呼ばれます。
このメソッドは (set()
を使って渡したデータを使って)
すべてのビューロジックを実行し、ビューを $layout
内に配置し、
エンドユーザーに表示します。
レンダリングに使用されるデフォルトのビューファイルは、規約によって決定されます。
RecipesController の search()
アクションがリクエストされたら、
/app/View/Recipes/search.ctp のビューファイルが描画されます。
class RecipesController extends AppController {
// ...
public function search() {
// /View/Recipes/search.ctp のビューが描画されます
$this->render();
}
// ...
}
CakePHP は ($this->autoRender
に false をセットしない限り) アクションの後に
自動的に描画メソッドを呼び出しますが、コントローラで $view
にビュー名を指定することで、
別のビューファイルを指定することが出来ます。
$view
が '/' で始まっていれば、 /app/View
への相対パスでビューまたはエレメントを
探そうとします。これはエレメントを直接描画することができ、Ajax 呼び出しではとても有用です。
// /View/Elements/ajaxreturn.ctp のビューが描画されます
$this->render('/Elements/ajaxreturn');
$layout
パラメータはビューが描画されるレイアウトの指定が出来ます。
コントローラでは、規約に従ったものではなく、別のビューを描画したことがあるかもしれません。
これは render()
を直接呼び出すことで出来ます。
一度 render()
を呼び出すと、
CakePHP は再度ビューを描画することはありません。
class PostsController extends AppController {
public function my_action() {
$this->render('custom_file');
}
}
これは app/View/Posts/my_action.ctp
の代わりに
app/View/Posts/custom_file.ctp
を描画します。
また、次のような書式で、プラグイン内のビューを描画することもできます。
$this->render('PluginName.PluginController/custom_file')
例:
class PostsController extends AppController {
public function my_action() {
$this->render('Users.UserDetails/custom_file');
}
}
これは app/Plugin/Users/View/UserDetails/custom_file.ctp
を描画します。
もっともよく使う、フローをコントロールするメソッドは redirect()
です。
このメソッドは最初の引数に、CakePHP の相対 URL を指定します。
ユーザーが正常に注文を出した時、レシート画面にリダイレクトさせるとすると:
public function place_order() {
// 注文終了のためのロジック
if ($success) {
return $this->redirect(
array('controller' => 'orders', 'action' => 'thanks')
);
}
return $this->redirect(
array('controller' => 'orders', 'action' => 'confirm')
);
}
$url に相対 URL または絶対 URL を指定することも出来ます。
$this->redirect('/orders/thanks');
$this->redirect('http://www.example.com');
アクションにデータを渡すこともできます。
$this->redirect(array('action' => 'edit', $id));
redirect()
の2つ目のパラメータは、リダイレクトに伴う
HTTP ステータスコードを定義することが出来ます。通常のリダイレクトに従って、
301 (moved parmanently) または 303 (see other) を使ったほうが良いでしょう。
このメソッドは3つ目のパラメータに false
を指定しない限りはリダイレクト後に
exit()
を呼び出します。
リファラのページにリダイレクトする必要があれば、次のように出来ます。
$this->redirect($this->referer());
このメソッドは名前付きパラメータもサポートしています。
たとえば http://www.example.com/orders/confirm/product:pizza/quantity:5
のような URL にリダイレクトしたい場合、以下のように使います。
$this->redirect(array(
'controller' => 'orders',
'action' => 'confirm',
'product' => 'pizza',
'quantity' => 5)
);
クエリ文字列とハッシュを使う場合は次のようになります。
$this->redirect(array(
'controller' => 'orders',
'action' => 'confirm',
'?' => array(
'product' => 'pizza',
'quantity' => 5
),
'#' => 'top')
);
生成される URL はこのようになります:
http://www.example.com/orders/confirm?product=pizza&quantity=5#top
redirect()
のように、 flash()
メソッドはある操作の後に、ユーザーを新しいページに誘導するために使われます。
flash()
メソッドはユーザーを別の URL へ移動させる前に
メッセージを表示するという点において違いがあります。
最初のパラメータは表示されるメッセージを指定し、2つ目のパラメータは CakePHP の相対 URL とします。
CakePHP はユーザーを転送する前に、 $pause
秒の間 $message
を表示します。
表示させたいメッセージの特定のテンプレートがあるなら、 $layout
パラメータにそのレイアウト名を指定します。
ページ内の flash メッセージについては、 SessionComponent::setFlash()
メソッドを参照してください。
リクエストライフサイクルコールバック に加えて、CakePHP は scaffolding に関連したコールバックも サポートしています。
$method は、たとえば index, edit などの、呼び出されたメソッドの名前です。
$method は、 edit か update のどちらかの、呼び出されたメソッドの名前です。
$method は、 edit か update のどちらかの、呼び出されたメソッドの名前です。
$method は、たとえば index, edit などの、呼び出されたメソッドの名前です。
このメソッドはコントローラに必要とされるモデルをロードします。
モデルをロードするプロセスは通常 CakePHP によって行われますが、
このメソッドは別の視点からコントローラにアクセスする時に便利です。
コマンドラインスクリプトまたは何かしらの外部のプログラムを CakePHP で使う必要がある場合、
constructClasses()
が便利でしょう。
現在のリクエストに対するリファラ URL を返します。 $default
パラメータは、
HTTP_REFERER がヘッダから読み取れなかった場合にデフォルト URL として使うために指定します。
つまり、このようにする代わりに:
class UserController extends AppController {
public function delete($id) {
// delete code goes here, and then...
if ($this->referer() != '/') {
return $this->redirect($this->referer());
}
return $this->redirect(array('action' => 'index'));
}
}
このように出来ます。
class UserController extends AppController {
public function delete($id) {
// delete code goes here, and then...
return $this->redirect(
$this->referer(array('action' => 'index'))
);
}
}
$default
がセットされていなければ、この関数はドメインのルート
'/' をデフォルト値とします。
$local
パラメータに true
をセットすれば、ローカルサーバーに対するリファラ
URL を制限します。
ユーザーの使っている ブラウザ に対して、現在のリクエストをキャッシュしないように 伝えるために使われます。これはビューキャッシュとは違います。ビューキャッシュについては あとの章で説明します。
これを使った時のヘッダは次のようなものを送ります。
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Last-Modified: [current datetime] GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0
Pragma: no-cache
POST されたモデルのデータ (HtmlHelper に互換性のある入力からのデータ) をモデルの
find 条件にセットするための形式に変換ためのメソッドです。この関数は、検索ロジックを
構築する上でのショートカットとなります。たとえば管理権限のあるユーザーは、どの商品を
出荷する必要があるのかを知るために、注文を検索出来たほうがよいでしょう。
ここで、Order モデルに基づくフォームを作るために、CakePHP の FormHelper
と
HtmlHelper
を使えます。そうすると、コントローラのアクションはそのフォームから
ポストされたデータを find 条件を作るために使うことができます。
public function index() {
$conditions = $this->postConditions($this->request->data);
$orders = $this->Order->find('all', compact('conditions'));
$this->set('orders', $orders);
}
$this->request->data['Order']['destination']
が" Old Towne Bakery" であれば、
postConditions はその条件を Model->find() メソッドで使える配列に変換します。
この場合は、 array('Order.destination' => 'Old Towne Bakery')
という形になります。
もし他の SQL 演算子を使いたければ、それらの演算子を2つ目のパラメータに渡してください。
/*
$this->request->data は以下のような形式です。
array(
'Order' => array(
'num_items' => '4',
'referrer' => 'Ye Olde'
)
)
*/
// 'Ye Olde' を含んでいて、少なくとも4つの商品をもっている注文を探しましょう
$conditions = $this->postConditions(
$this->request->data,
array(
'num_items' => '>=',
'referrer' => 'LIKE'
)
);
$orders = $this->Order->find('all', compact('conditions'));
3つ目のパラメータは、条件の結合になにを使うのかを指定することが出来ます。 'AND', 'OR', 'XOR' のような文字列です。
最後のパラメータが true で、$op パラメータが配列であれば、$op に含まれていない項目は、 この関数から返ってくる条件には含まれなくなります。
このメソッドはモデルから取得した結果をページングするために使われます。 ページサイズやモデルの find 条件などを指定出来ます。 paginate のより詳しい使い方は ページ制御 セクションを参照してください。
この関数は任意の場所からコントローラのアクションを呼び出し、アクションからのデータを返します。
$url
には、CakePHP の相対 URL を渡します (/controllername/actionname/params)。
呼び出し先のコントローラのアクションに対して追加のデータを渡すためには、$options 配列を
指定してください。
注釈
描画されたビューを取得するために、 requestAction($url, array('return'));
のように
options に 'return' を渡して requestAction()
を
使うことができます。注意してほしいのは、コントローラのメソッドから
requestAction()
で return
を使うと、
script タグと css タグが正しく動作しない原因となるということです。
警告
requestAction()
をキャッシュせずに利用すると、
パフォーマンスの低下につながります。コントローラやモデルで使用するのは稀です。
requestAction()
は (キャッシュされた) エレメントと
組み合わせてよく使われます。
レンダリング前にエレメントに対してデータを取得するような場合です。
レイアウトの中に"最新のコメント"のエレメントを配置するサンプルを使ってみましょう。
まず、データを返すコントローラの関数を作ります。
// Controller/CommentsController.php
class CommentsController extends AppController {
public function latest() {
if (empty($this->request->params['requested'])) {
throw new ForbiddenException();
}
return $this->Comment->find(
'all',
array('order' => 'Comment.created DESC', 'limit' => 10)
);
}
}
requestAction()
で実行したいメソッドは、実際に
requestAction()
からリクエストが発せられているかどうかを
チェックするべきです。そうしないと、そのメソッドは URL から直接からアクセスできるように
なってしまいます。それは望ましくありません。
上記の関数を呼ぶための簡単なエレメントを作ったら:
// View/Elements/latest_comments.ctp
$comments = $this->requestAction('/comments/latest');
foreach ($comments as $comment) {
echo $comment['Comment']['title'];
}
どこか出力したい場所にさっきのエレメントを配置できます。
echo $this->element('latest_comments');
ここに書かれた方法では、エレメントが描画されると毎回、データを取得するためにコントローラに対して リクエストが作られ、データが処理されて結果が返ってきます。 したがって、不必要な処理を防ぐためにエレメントのキャッシュを使うのが良いでしょう。
echo $this->element('latest_comments', array(), array('cache' => true));
requestAction()
の呼び出しはキャッシュされたエレメントの
ビューファイルが存在してそれが有効な限り、リクエストの発行はしません。
加えて、 requestAction()
は CakePHP 形式の URL 配列を
引数に取ることが出来ます。
echo $this->requestAction(
array('controller' => 'articles', 'action' => 'featured'),
array('return')
);
これは、 requestAction()
の呼び出しが、パフォーマンスを向上できる
Router::url()
の利用を避けることが出来ます。配列ベースの URL は次の1点を除いて
HtmlHelper::link()
で使うものと同じです。
その違いは、名前付きパラメータや GET で渡されるパラメータを使う場合、それらを2つ目の引数に指定して、
適切なキーでラップしなければならないということです。
これは、 requestAction()
が名前付きパラメータの配列
(requestAction の2つ目の引数) を Controller::params
配列にマージして、
名前付きパラメータに対して明示的に 'named' というキーを付けないからです。
$option
配列で指定した追加の値は、リクエストされたアクションの
Controller::params
配列の中で使えるようになります。
echo $this->requestAction('/articles/featured/limit:3');
echo $this->requestAction('/articles/view/5');
上記の場合、requestAction()
の配列は次のようになります。
echo $this->requestAction(
array('controller' => 'articles', 'action' => 'featured'),
array('named' => array('limit' => 3))
);
echo $this->requestAction(
array('controller' => 'articles', 'action' => 'view'),
array('pass' => array(5))
);
注釈
配列の URL が文字列のURLと似ている他の部分とは異なり、
requestAction()
は URL の扱い方が違います。
requestAction()
で配列の URL を使う時は、
リクエストされるアクションにおいて必要となる 全て のパラメータを指定しなければなりません。
これは $this->request->data
のようなパラメータも含まれます。
必要な全てのパラメータを渡すことに加えて、名前付き及びGETパラメータも上記で見たように、
2つ目の引数に指定しなければなりません。
loadModel()
関数は、コントローラのデフォルトモデルまたは
それに関連づいたモデル以外のモデルを使う必要がある時に便利です。
$this->loadModel('Article');
$recentArticles = $this->Article->find(
'all',
array('limit' => 5, 'order' => 'Article.created DESC')
);
$this->loadModel('User', 2);
$user = $this->User->read();
コントローラの変数とその説明については、 CakePHP API を確認してください。
$name
変数はコントローラ名がセットされます。
通常これは、コントローラが使うメインのモデルの複数形となります。
このプロパティは必須ではありません。
// $name 変数の使い方のサンプル
class RecipesController extends AppController {
public $name = 'Recipes';
}
次に説明するコントローラの変数は、現在のコントローラの中でどのヘルパー、コンポーネント、モデルを
使うのかを CakePHP に伝える役割をはたします。これらの変数はもっとも良く使われる変数です。
これらを使うことで、 $components
や $uses
で与えられた MVC クラスはコントローラの中でクラス変数として (例えば $this->ModelName
)、また
$helpers
で与えられたクラスはビューの中でオブジェクトへの参照として
(例えば $this->{$helpername}
) 有効になります。
注釈
それぞれのコントローラはデフォルトでこのようなクラスをいくつか持っていて、使える状態になっています。 したがって、コントローラではすべてを設定する必要はありません。
コントローラはデフォルトで主要なモデルへアクセスできるようになっています。
RecipesController は $this->Recipe
でアクセスできるモデルクラスを持っており、
ProductsController もまた $this->Product
に Product モデルを持っています。
しかし、 コントローラが $uses
変数に追加のモデルを指定して、
それらが使えるようになっている時は、 $uses
に現在のコントローラの
モデルの名前も含めなければなりません。これについては、下の方のサンプルで説明します。
コントローラでモデルを使いたくない場合は、 public $uses = array()
とセットしてください。
これでコントローラを使うのに対応するモデルファイルが必要なくなります。それでも、
AppController
で定義されたモデルはロードされます。false
を使うことで、
AppController
で定義されていたとしてもモデルがロードされなくなります。
バージョン 2.1 で変更: $uses
は新しい値を持ちます。
それは、 false
とは違った扱いになります。
SessionComponent
と同様に、HtmlHelper
、
FormHelper
、SessionHelper
はデフォルトで有効になっています。
しかし、 AppController
で $helpers
を独自に定義している場合、
HtmlHelper
と FormHelper
をコントローラで有効にしたければ、
それらを $helpers
に含むようにしてください。
このマニュアルの後ろにある、それぞれのセクションを確認して、これらのクラスについて
よく詳しく学んでください。
追加で利用する MVC クラス達をどうやって CakePHP のコントローラに伝えるのかを見てみましょう。
class RecipesController extends AppController {
public $uses = array('Recipe', 'User');
public $helpers = array('Js');
public $components = array('RequestHandler');
}
これらの変数はそれぞれ、継承された値とマージされます。したがって、たとえば
FormHelper
や AppController
で宣言されている他のクラスを、
再度宣言する必要はありません。
components 配列はコントローラで使う コンポーネント をセットします。
$helpers
や $uses
のように、
あなたのコントローラのコンポーネントは AppController
のコンポーネントとマージされます。
$helpers
のように、$components
には設定を渡すことが出来ます。より詳しくは コンポーネントの設定 を参照してください。
コントローラの変数の詳細については、 API ページで確認すれば、 ここで説明した以外の他のコントローラ変数についてのセクションがあります。
cacheAction 変数はフルページキャッシュの持続時間やその他の情報を定義するために使われます。
フルページキャッシュについてのより詳しい情報は CacheHelper
のドキュメントを
読んでください。
paginate 変数は非推奨の互換性のあるプロパティです。
この変数を使って、 PaginatorComponent
の読み込みと設定をします。
次のように、コンポーネントの設定を使うように修正することが推奨されます。
class ArticlesController extends AppController {
public $components = array(
'Paginator' => array(
'Article' => array(
'conditions' => array('published' => 1)
)
)
);
}