scaffolding と bake.php から学ぶ CakePHP
最後に CakePHP をより具体的に理解できるように、実際に動く scaffolding のサンプルを紹介します。しかし scaffold してしまうと PHP のソースが見えないため bake.php というソースジェネレータを使って scaffolding と同等のコードを出力しました。
完成品のデモサイトを作りましたので、まずイメージをつかんでください
http://www.blueocean.bz/blog/cakephp.demo/
イメージがつかめましたでしょうか?次に仕様を説明します。
本サンプルは、中古品の売買マッチングシステムのマスタ管理画面という設定にしました。ユーザマスタ管理画面とユーザが所持している商品マスタ管理画面が実際に動作します(補足:中古品の売買ですので、本来は商品は管理者ではなくユーザ自身が管理する方が自然なのですが、サンプルの簡略化のため、このような仕様にしました)。
ここで作るサンプルが使うデータベースの構造は次のようになります。 RDBMS には MySQL を採用しました。

-- -- users -- CREATE TABLE users ( id integer NOT NULL auto_increment, name varchar(50) NOT NULL, note varchar(255), PRIMARY KEY (id) ); -- -- items -- CREATE TABLE items ( id integer NOT NULL auto_increment, user_id integer NOT NULL, title varchar(100) NOT NULL, note varchar(255), PRIMARY KEY (id) );
データベースが決まりましたら、最初にモデルを作成します。
User モデル - /add/models/user.php
/** * * User モデル * */ class User extends AppModel { // CakePHP の慣例です。 // 必ず書くようにしましょう。 // モデルの場合は単数形を書きます。 var $name = 'User'; // このモデルのデータを select タグの option 要素として出力したとき、 // <option> と </option> との間に表示するフィールドを指定します。 // この場合は title を出力します。 var $displayField = 'name'; // ユーザは0個以上の商品を持っている、というアソシエーションです。 var $hasMany = array('Item' => array('className' => 'Item', 'foreignKey' => 'user_id', 'conditions' => '', 'fields' => '', 'order' => '', 'limit' => '', 'offset' => '', 'dependent' => '', 'exclusive' => '', 'finderQuery' => '', 'counterQuery' => '' ), ); }
Item モデル - /app/models/item.php
/** * * Item モデル * */ class Item extends AppModel { // CakePHP の慣例です。 // 必ず書くようにしましょう。 // モデルの場合は単数形を書きます。 var $name = 'Item'; // このモデルのデータを select タグの option 要素として出力したとき、 // <option> と </option> との間に表示するフィールドを指定します。 // この場合は title を出力します。 var $displayField = 'title'; // 商品はユーザに属する、というアソシエーションです。 var $belongsTo = array('User' => array('className' => 'User', 'foreignKey' => 'user_id', 'conditions' => '', 'fields' => '', 'order' => '', 'counterCache' => '' ), ); }
モデルの次はコントローラを作成します。手書き、自動生成、どちらでもかまいません。
Users コントローラ - /app/controllers/users_controller.php
/** * * Users コントローラ * */ class UsersController extends AppController { // CakePHP の慣例です。 // 必ず書くようにしましょう。 // コントローラの場合は複数形を書きます。 var $name = 'Users'; // このコントローラが使用するモデルを記述します。 var $uses = array('User', 'Item'); // ビューで利用するヘルパーを書きます var $helpers = array('Html', 'Form' ); /** * ユーザを一覧表示するアクションです。 */ function index() { // User モデルの子要素は不要なので0を指定します $this->User->recursive = 0; // users テーブルからユーザ( User モデル)をすべて取得します $this->set('users', $this->User->findAll()); } /** * ユーザの詳細を出力するアクションです。 */ function view($id = null) { // パラメータが渡ってこなかった場合はエラーを出力します if(!$id) { $this->flash('Invalid id for User', '/users/index'); } // プライマリキーが $id のユーザを読み込んでビューへ渡します $this->set('user', $this->User->read(null, $id)); } /** * ユーザを新規登録します */ function add() { if(empty($this->data)) { // 最初のリクエストの場合の処理 // ビューを実行します $this->render(); } else { // form からデータが post された場合の処理 // 年月日時刻を結合します $this->cleanUpFields(); // form から送られたデータで保存を試みます if($this->User->save($this->data)) { // 成功した場合はその旨を表示して一覧画面に戻ります $this->flash('User saved.', '/users/index'); } else { // 失敗した場合は再び入力を促します // 入力を促す= Controller::flash() や // Controller::redirect() をしなければ、 // 再び入力画面へ戻ります。 } } } /** * ユーザ情報を編集します */ function edit($id = null) { if(empty($this->data)) { // 最初のリクエストの場合の処理 // 編集対象のユーザのプライマリキーが飛んでこない場合は // エラーメッセージを表示して一覧画面へ戻ります if(!$id) { $this->flash('Invalid id for User', '/users/index'); } // 対象のユーザのデータを HTML ヘルパーが form 関連タグを出力する際に、 // 初期値を決める基準となる変数 Controller::data に格納します $this->data = $this->User->read(null, $id); } else { // form からデータが post された場合の処理 // 年月日時刻を結合します $this->cleanUpFields(); // form から送られたデータで保存を試みます if($this->User->save($this->data)) { // 成功した場合はその旨を表示して一覧画面に戻ります $this->flash('User saved.', '/users/index'); } else { // 失敗した場合は再び入力を促します // 入力を促す= Controller::flash() や // Controller::redirect() をしなければ、 // 再び入力画面へ戻ります。 } } } /** * ユーザ情報を削除します */ function delete($id = null) { // 削除対象のユーザのプライマリキーが飛んでこない場合は // エラーメッセージを表示して一覧画面へ戻ります if(!$id) { $this->flash('Invalid id for User', '/users/index'); } // プライマリキーで指定したユーザを削除します if($this->User->del($id)) { // 成功した場合はその旨を表示して一覧画面に戻ります $this->flash('User deleted: id '.$id.'.', '/users/index'); } } }
Items コントローラ - /app/controllers/items_controller.php
/** * * Items コントローラ * */ class ItemsController extends AppController { // CakePHP の慣例です。 // 必ず書くようにしましょう。 // コントローラの場合は複数形を書きます。 var $name = 'Items'; // このコントローラが使用するモデルを記述します。 var $uses = array('Item', 'User'); // ビューで利用するヘルパーを書きます var $helpers = array('Html', 'Form' ); /** * 商品を一覧表示するアクションです。 */ function index() { // Item モデルの子要素は不要なので0を指定します $this->Item->recursive = 0; // items テーブルから商品( Item モデル)をすべて取得します $this->set('items', $this->Item->findAll()); } /** * 商品の個別の詳細を出力するアクションです。 */ function view($id = null) { // パラメータが渡ってこなかった場合はエラーを出力します if(!$id) { $this->flash('Invalid id for Item', '/items/index'); } // プライマリキーが $id の商品を読み込んでビューへ渡します $this->set('item', $this->Item->read(null, $id)); } /** * 商品を新規登録します */ function add() { if(empty($this->data)) { // 最初のリクエストの場合の処理 // 誰の商品か?という選択をさせるため // ユーザのリストを取得します。 $this->set('users', $this->Item->User->generateList()); // ビューを実行します $this->render(); } else { // form からデータが post された場合の処理 // 年月日時刻を結合します $this->cleanUpFields(); // form から送られたデータで保存を試みます if($this->Item->save($this->data)) { // 成功した場合はその旨を表示して一覧画面に戻ります $this->flash('Item saved.', '/items/index'); } else { // 失敗した場合は再び入力を促します // 入力を促す= Controller::flash() や // Controller::redirect() をしなければ、 // 再び入力画面へ戻ります。 // 誰の商品か?という選択をさせるため // ユーザのリストを取得します。 $this->set('users', $this->Item->User->generateList()); } } } /** * 商品情報を編集します */ function edit($id = null) { if(empty($this->data)) { // 最初のリクエストの場合の処理 // 編集対象の商品のプライマリキーが飛んでこない場合は // エラーメッセージを表示して一覧画面へ戻ります if(!$id) { $this->flash('Invalid id for Item', '/items/index'); } // 対象の商品のデータを HTML ヘルパーが form 関連タグを出力する際に、 // 初期値を決める基準となる変数 Controller::data に格納します $this->data = $this->Item->read(null, $id); // 誰の商品か?という選択をさせるため // ユーザのリストを取得します。 $this->set('users', $this->Item->User->generateList()); } else { // form からデータが post された場合の処理 // 年月日時刻を結合します $this->cleanUpFields(); // form から送られたデータで保存を試みます if($this->Item->save($this->data)) { // 成功した場合はその旨を表示して一覧画面に戻ります $this->flash('Item saved.', '/items/index'); } else { // 失敗した場合は再び入力を促します // 入力を促す= Controller::flash() や // Controller::redirect() をしなければ、 // 再び入力画面へ戻ります。 // 誰の商品か?という選択をさせるため // ユーザのリストを取得します。 $this->set('users', $this->Item->User->generateList()); } } } /** * 商品情報を削除します */ function delete($id = null) { // 削除対象の商品のプライマリキーが飛んでこない場合は // エラーメッセージを表示して一覧画面へ戻ります if(!$id) { $this->flash('Invalid id for Item', '/items/index'); } // プライマリキーで指定した商品を削除します if($this->Item->del($id)) { // 成功した場合はその旨を表示して一覧画面に戻ります $this->flash('Item deleted: id '.$id.'.', '/items/index'); } } }
最後にビューを作成します。手書きでもかまいませんが bake.php を利用した方が楽です。ビューはファイル数が大きくなってしまいますので、読み解くためのポイントだけ掲載します。
-
echo $html->input('User/name', array('size' => '60'));
- User モデルの name に対する input タグを作っています。第2引数の配列はタグの属性になります。この場合 <input name="data[User][name]" size="6" /> となります。
-
echo $html->tagErrorMsg('User/name', '名前は必ず入力してください');
- User モデルの name に対するエラーメッセージを書いています。エラーがあった場合は「名前は必ず入力してください」が出力されます。エラーがなかった場合は何も表示しません。
-
echo $html->url('/users/add');
- CakePHP 特有の URL を作っています。 CakePHP は様々な環境で動作しますので URL を書く際にはこのメソッドを使用します。
-
echo $html->link('List Users', '/users/index')
- List Users という文字を含む a タグを作っています。前述の $html->url() メソッドと同様に、環境の違いを吸収するためのメソッドです。
-
$user['Item']
- ユーザ( $user )にひもづく商品( [’Item’] )の情報を参照しています。アソシエーションを利用して取得したデータはこのように配置されます。
これでユーザと商品に対する一覧・詳細・登録・編集・削除が可能になりました。しかし実際には入力画面と登録完了画面との間に確認画面を挟みたくなると思います(海外と日本との文化の違いでしょうか?)。その際には Controller::add() や Controller::edit() に数行の追記が必要です。