CakePHP ガイド 入門編

目次

CakePHP プログラマーズリファレンスガイド

本レポートでは CakePHP の入門記事を掲載します。

まずは日本語のドキュメントをご一読ください。

【公式】 CakePHP プログラマーズ リファレンスガイド

最後まで問題なく理解できれば本レポートは出番がありません。もし、理解に苦しむ表現がありましたら、本レポートからヒントを得ることができるかも知れませんので、再び当サイトを閲覧ください。

最速で理解したい方でも次の章は必ず目を通してください。それ以降はドキュメントと当サイトを平行して読み進めてください。

CakePHP における MVC パターン

CakePHP は Web システム開発でもっとも実績のある MVC パターンを採用しています。

開発者はモデル、ビュー、コントローラの各オブジェクトをプログラミングすることが Web システム構築における大部分の開発サイクルとなります。

MVC パターンの概要と CakePHP の各オブジェクトが MVC 中のどの役割を担っているかを解説します。

MVC パターン

MVCパターンとは次のようなWebシステムの構成を意味します。

mvc.jpg

モデル - Model

【公式】 http://cakephp.jp/doc/models.html

モデルはビジネスロジックとそれに付随するデータの入出力を担当します。また、たとえば「会員を退会扱いにする」場合などにdelete_flag を false から true へと変更するビジネスロジックはモデルが担当します。

主な役割は次のようなケースが考えられます。

■ビジネスロジックをメソッドとして提供する
        例)会員のデータ一覧を取得する
        例)会員を新規登録する
        例)パスワード再設定用のURLを会員にメールで送信する
        例)delete_flagをtrueにして削除扱いにする
■データベースからデータを取得する
        プライマリキーや条件からデータを1件取得する(SELECT)
        条件に合致する複数件のデータを取得する(SELECT)
        リレーションを理解して再帰的にデータを取得する(SELECT)
■データベースへデータを保存する
        新規登録(INSERT)
        更新(UPDATE)
■データを検証する(バリデーション)
        入力データを検証する
        データベースの整合性(一意属性など)を検証する

コントローラ - Controller

【公式】 http://cakephp.jp/doc/controllers.html

コントローラはモデルのメソッドを使いこなして目的を達成し、ビューへとデータを渡して、その結果をブラウザへ返す役割を担います。

主な役割は次のようなケースが考えられます。

■HTTPリクエストからアクションを実行する
        モデルのメソッドを使いこなして目的を達する
        画面を遷移するロジックを実行する
        ビューへデータを渡す

ビュー - View

【公式】 http://cakephp.jp/doc/views.html

ビューはコントローラから渡ってきたデータを駆使して HTML 、または XML などの表現を構成します。

主な役割は次のようなケースが考えられます。

■コントローラから渡ってきたデータを駆使してHTMLを返す
■コントローラから渡ってきたデータを駆使してXMLを返す

CakePHP におけるシンボルの命名規約

フレームワークともなるとクラス名を決める際にも規約があるものです。それは CakePHP とて例外ではありません。

次のリンクではシンボルの命名規約が表になっています。ほんの少しがんばって覚えていただけると、後になって当時の自分が賞賛に値するほど、ソースコードの高い保守性が実現するかも知れません。

CakePHP におけるシンボルの命名規約

CakePHP の基本的なデータ構造

CakePHP には、 MVC 間のデータ交換や、モデルを取り出したり保存したりする際に用いられる基本的なデータの形式があります。配列で表現するこのデータ形式は次のようなイメージになります。

<?php
$data = array("モデル名" => array("フィールド名" => "",
                                 "フィールド名" => "",
                                 …),
              "モデル名" => array("フィールド名" => "",
                                 "フィールド名" => "",
                                 …),
              …);
?>

前述の通り、この形式のデータは CakePHP の随所で見つけることができます。便宜上、当サイトではこれを CakePHP 式データと呼びます。

モデル内での使われ方

データベースからデータを取得する際に Model::find() メソッドや Model::read() メソッドを用いますが、それらのメソッドが返す値は CakePHP 配列です。返値にモデル名も含まれている点に注意してください。

また、モデルのデータをテーブルに追加・更新する際に Model::save() というメソッドを用いますが、このメソッドは引数として CakePHP 配列でデータを渡す必要があります。ここでもモデル名も含んで渡すという点に注意してください。

最後ですが、モデルにはコールバック関数があります。たとえば Model::beforeSave() は Model::save() の前に呼ばれるコールバック関数です。データベースへ保存される前にデータを修正したい場合などにこのメソッドをオーバーライドしますが、その際に対象となるデータは $Model::data というメンバ配列変数に CakePHP 配列として入っています。やはりそこでもモデル名を含んでいる点に注意してください。

コントローラ内での使われ方

CakePHP では POST 送信によって渡されたパラメータが自動的に $Controller::data メンバ配列変数へ格納されます。この形式が CakePHP 配列になっています。

ただし、自動的に格納されるのは HTML ヘルパーを使って出力した form 関連要素(inputタグなど)のみです。とはいえ CakePHP ではほとんどの場合 form 関連要素を HTML ヘルパーで出力しますので、事実上、全ての POST データが $Controller::data に自動的に格納されると考えて差し支えありません。

ビュー( HTML )内での使われ方

ビュー( HTML )で form 関連要素( input タグなど)を書く場合、 CakePHP では HTML ヘルパーを利用します。その理由のひとつがコントローラとのデータ交換です。 HTML ヘルパーで書かれた form の要素を POST 送信すると、送信先のコントローラの $Controller::data というメンバ配列変数に自動的に格納されます。

また、 HTML ヘルパーは form 関連要素を書く際に、コントローラ内の $Controller::data メンバ配列変数を参照して、これから出力しようとしている要素と同じ名前のデータがあった場合、そのデータを初期値として HTML を出力します。この仕様によって、たとえばデータ登録画面で入力項目にエラーがあった場合でも、今までに入力していたデータが消失してしまうのを防げるようになっています。

CakePHP のライブラリ

CakePHP には、ビヘイビア、コンポーネント、ヘルパーといった3つのライブラリがあります。ライブラリの役割は独立性の高いコードを再利用する手助けをすることです。ライブラリは自分で作ることができますし、他人のライブラリを使うことも可能です。逆にそれができないコードはライブラリではありません。

ビヘイビア - behavior

ビヘイビアはモデルに関する再利用性の高いメソッドをまとめたクラスです。

利用するためには次のように書きます。

<?php
class SomeModel extends AppModel
{

  // このモデルで利用するビヘイビアを書き連ねます
  // ここで書いたクラスは自動的に include されます
  var $actsAs = array("Hoge" , "Foo");

  function someMethod()
  {

    // 実際に使う場合
    $this->hogesMethod();

  }
}
?>

たとえば、パソコンやゲーム機のような電子機器は、電力をコンセントから得るという点で共通しています。そこで、 PowerSouce というビヘイビアを作って PowerSource::insertToConcentricPlug() というメソッドを書けば、別の電子機器が登場しても、再びコンセント接続のコードを書かなくてすみます。

なお、ビヘイビアを利用できるのは CakePHP 1.2.x.x. 以降です。

コンポーネント - component

コンポーネントは、ビヘイビアと同様に、コントローラに関する再利用性の高いメソッドをまとめたクラスです。

【公式】 コンポーネント

利用するためには次のように書きます。

<?php
class SomeController extends AppModel
{

  // このコントローラで利用するコンポーネントを書き連ねます
  // ここで書いたクラスは自動的に include されます
  var $components = array("Hoge" , "Foo");

  function someMethod()
  {

    // 実際に使う場合
    $this->Hoge->hogeMethod();

  }
}
?>

たとえば今後、ログインとログアウトをシームレスに Ajax で実装することが大流行するかもしれません。ログインとログアウトといった機能は Web アプリケーションの必須機能ですので、コンポーネントの利用を検討すべきです。

ヘルパー - helper

ヘルパーは HTML のテンプレートの記述に関する再利用性の高いメソッドをまとめたクラスです。

【公式】 ヘルパー

利用するには、まず…

<?php
class SomeController extends AppModel
{

  // このコントローラのビューで利用するヘルパーを書き連ねます
  // ここで書いたクラスは自動的に include されます
  var $helpers = array("Hoge" , "Foo");

}
?>

…と宣言し、テンプレートの中で…

<body>
  <p><?php echo $hoge->somePrint() ?></p>
</body>

…と書きます。 SomeController::helpers 配列のヘルパーは、コントローラの中で宣言して、テンプレートの中で変数としてアクセスする点に注意してください。

たとえば、今日を基準とした3年前から3年後の西暦を要素に持つ select タグを出力するといったコードは多くの人がヘルパーで実現したいかもしれません。

モデルの動的なアソシエーション

CakePHP にはアソシエーションという機能があります。

【公式】 アソシエーション

アソシエーションとはモデル間の依存関係のことで、通常はモデルを設計するタイミングで指定します。次の例は User モデルには Profile モデルと一対一の関係があることを設定しています。

<?php
class User extends AppModel
{
  var $name = "User";
  var $hasOne = array("Profile" =>
                array("className"  => "Profile",
                      "conditions" => "",
                      "order"      => "",
                      "dependent"  => true,
                      "foreignKey" => "user_id"
            )
          );
}
?>

従来は User モデルと対応する Profile モデルを取得する際には SQL の JOIN が必要でした。しかし、アソシエーションを利用することで、 $appModelInstance->find() の1回で User モデルと Profile モデルを取得できるようになります。

しかし、これほど有用なアソシエーションが枷になる場合があります。アソシエーションは関連するモデルを一度に取得できますが、逆に言うと関連するモデルまでもを一度に取得してしまうのです。いうまでもなくデータ件数が多い場合にはレスポンスが悪くなります。

このような場合、キャッシュで対応するという手段もあるのですが、ここでは動的なアソシエーションを紹介します。

前述したように、アソシエーションはモデルの設計時に設定します。しかし、パフォーマンスの向上などの理由から、必要に応じてアソシエーションを設定、または解除することが可能です。このような動的なアソシエーションは次のメソッドで実現します。

  • アソシエーションを動的に設定する

    Model::bindModel( $params )
    Bind model associations on the fly.
    Parameters:
    array $params

  • アソシエーションを動的に解除する

    Model::unbindModel( $params )
    Turn off associations on the fly.
    Parameters:
    array $params

最後に例を掲載します。

<?php
// 本来モデルの設計時に書くことを…
$assoc = array("Profile" =>
                array("className"  => "Profile",
                      "conditions" => "",
                      "order"      => "",
                      "dependent"  => true,
                      "foreignKey" => "user_id"
                )
              );

// 必要に応じて指定する
$this->User->bindModel(array("hasOne" => $assoc));
?>

CakePHP の暗黙なサニタイズ

ひとつ重要な事実があります。

実は CakePHP は、データの永続化( INSERT, UPDATE ) に際して PreparedStatement を使用していません。

PreparedStatement はインジェクションに代表される SQL をターゲットとする攻撃を防止する最も簡単にして強力で確実な技術でした。しかし CakePHP では PreparedStatement を使用せず、攻撃やメタ文字に注意しながら、 SQL の全文を自分( CakePHP の DB ライブラリ)が作ってサーバに投げています。明確な理由はわかりませんが、デバッグ情報として実行した SQL の全文を出力したいのかもしれません。

このように、 CakePHP ではデータを暗黙のうちにサニタイズしています。タイミングは次の2カ所です。

  1. モデルとデータベース間
    前述の通り CakePHP では各 RDBMS ごとに、もっとも好ましいであろう手段でデータをサニタイズします。たとえば MySQL では、データを mysql_escape_string 関数に通してから SQL 文を作ってサーバへ送信します。
  2. HTML ヘルパー
    データ入力画面などのテンプレート HTML を出力する際には、 HTML ヘルパーを用いて form タグを出力すると思われます。このような用途を見越してか HTML ヘルパーでは form 関連タグの value 属性の値を htmlspecialchars() 関数に通してから出力します。また HtmlHelper::tagValue() メソッドでも htmlspecialchars() 関数を通してから値を出力します。この振る舞いは入力データの確認画面で恩恵が得られることと思います。( tagValue メソッドは第二引数を true に設定しないとエスケープしなくなりました)

CakePHP の理解を深めるためのメソッド

オープンソースのプログラムを利用すると、ドキュメントに無い仕様を理解するためにソースコードの読み解きが必要になる可能性があります。それは CakePHP も例外ではありません。幸いなことに CakePHP は PHP で書かれていますし、覚えておくと便利なコードは、まだそれほど多くありません。

コントローラ - Controller

Controller::cleanUpFields()
HTML ヘルパーで書かれた年月日や時刻のデータを結合して文字列にします。たとえば、入力画面を作る際、テーブル上では Date 型と定義されているフィールドの HTML を HTML ヘルパーを用いて書くと、年と月と日とでそれぞれ3つの select 要素が出力されます。

<!-- Date 型のフィールド some_date を HTML ヘルパーで出力したイメージ -->
<select name="some_date_year"><option>1980</option>
  <option>1981</option>
  <option>1982</option></select><select name="some_date_month"><option>3</option>
  <option>4</option>
  <option>5</option></select><select name="some_date_day"><option>13</option>
  <option>14</option>
  <option>15</option></select>

各 select 要素の name 属性が some_date をベースとして some_date_year, some_date_month, some_date_day と修飾されている点に注目してください。Controller::cleanUpFields() は、このようなデータがコントローラ( Controller::data )に入っているとき、年月日を結合して 1981-4-14 のようにデータベースが理解しやすい値に変換したうえ、名前を some_date として Controller::data にデータを登録します。

モデル - Model

モデルには値の検証(バリデーション)に関する、名前がよく似ているメソッドがいくつかあります。 Model::validates(), Model::invalidFields(), Model::invalidate() の3つで、このメソッドを理解することで、モデルへの理解がいっそう深まります。

  
Model::invalidFields()
Model::validate に基づき、データ( Model::data )を検証して、エラーが発生した件数フィード名のリストを返します。名前からは想像しづらいのですが、このメソッドが検証(バリデーション)の中心で、検証のアルゴリズムはここに書かれています。
Model::validates()
Model::invalidFields() に基づき、すべてのデータが検証にパスしたかの真偽値を返します。このメソッドは Model::save() の中で自動的に呼ばれ、すべてのデータが検証にパスできなかった場合は Model::save() を中止します(強制的に save() する場合は引数でその旨を指定する)。
Model::invalidate()
モデルの中の指定したフィールドに、検証失敗のフラグを立てます。 Model::invalidFields() が、検証(バリデーション)にパスできなかったフィールドを記憶するためのメソッドですが、 Model::beforeValidate() の中で使用することで、独自の検証(バリデーション)の後に CakePHP の検証(バリデーション)を連続で実行することができます( Model::beforeValidate() は独自の検証ルールを記述するための場所ですが、通常、このメソッドを使用して検証を失敗させると CakePHP の検証が実行されません)。

HTML ヘルパー - HtmlHelper

HtmlHelper::setFormTag()
たとえば “SomeModel/SomeField” のように、”モデル名/フィールド名”という形式になっている文字列から、モデル名とフィールド名をパースして HtmlHelper のインスタンスに格納します。格納されたモデル名とフィールド名は HtmlHelper の各所で使われますので、このメソッドは多用されています。
HtmlHelper::selectTag()
select タグ( option タグを含む)を出力します。公式マニュアルに載っていないのは、今後 HtmlHelper で select タグを出力するという常識が無くなる可能性があるためかと思われます( 2007年6月5日現在、 1.2.x.x には HtmlHelper::selectTag() はありません)。ちなみにインタフェースは次のようになります。

HtmlHelper::selectTag(
  $fieldName,
  $optionElements,
  $selected = null,
  $selectAttr = array(),
  $optionAttr = null,
  $showEmpty = true,
  $return = false 
)

パラメータ

string $fieldName
タグの name 属性
array $optionElements
option タグの内容( ’value’ => ’text’ のペア配列)
mixed $selected
初期選択の option の value
array $selectAttr
select タグの属性( style 属性など)
array $optionAttr
各 option タグの属性( style 属性など)
boolean $show_empty
先頭に空の option タグを追加するか?
boolean $return
結果を出力するか値を返すのか?

返値
  select タグの HTML

グローバル関数 - basics.php

loadModel() という関数はモデルを任意のタイミングで読み込むことができます。基本的に利用するケースはありませんが、弊社では CakePHP の 1.1.x.x で 1.2.x.x のビヘイビアをエミュレートする場合などに使用しています。

scaffolding と bake.php から学ぶ CakePHP

最後に CakePHP をより具体的に理解できるように、実際に動く scaffolding のサンプルを紹介します。しかし scaffold してしまうと PHP のソースが見えないため bake.php というソースジェネレータを使って scaffolding と同等のコードを出力しました。

完成品のデモサイトを作りましたので、まずイメージをつかんでください
http://www.blueocean.bz/blog/cakephp.demo/

イメージがつかめましたでしょうか?次に仕様を説明します。

本サンプルは、中古品の売買マッチングシステムのマスタ管理画面という設定にしました。ユーザマスタ管理画面とユーザが所持している商品マスタ管理画面が実際に動作します(補足:中古品の売買ですので、本来は商品は管理者ではなくユーザ自身が管理する方が自然なのですが、サンプルの簡略化のため、このような仕様にしました)。

ここで作るサンプルが使うデータベースの構造は次のようになります。 RDBMS には MySQL を採用しました。

sample_er.gif

-- 
-- 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

<?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

<?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

<?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

<?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 を利用した方が楽です。ビューはファイル数が大きくなってしまいますので、読み解くためのポイントだけ掲載します。

<?php echo $html->input('User/name',
          array('size' => '60'));?>
User モデルの name に対する input タグを作っています。第2引数の配列はタグの属性になります。この場合 <input name="data[User][name]" size="6" /> となります。
<?php echo $html->tagErrorMsg('User/name',
          '名前は必ず入力してください');?>
User モデルの name に対するエラーメッセージを書いています。エラーがあった場合は「名前は必ず入力してください」が出力されます。エラーがなかった場合は何も表示しません。
<?php echo $html->url('/users/add'); ?>
CakePHP 特有の URL を作っています。 CakePHP は様々な環境で動作しますので URL を書く際にはこのメソッドを使用します。
<?php echo $html->link('List Users', '/users/index')?>
List Users という文字を含む a タグを作っています。前述の $html->url() メソッドと同様に、環境の違いを吸収するためのメソッドです。
<?php $user['Item'] ?>
ユーザ( $user )にひもづく商品( [’Item’] )の情報を参照しています。アソシエーションを利用して取得したデータはこのように配置されます

これでユーザと商品に対する一覧・詳細・登録・編集・削除が可能になりました。しかし実際には入力画面と登録完了画面との間に確認画面を挟みたくなると思います(海外と日本との文化の違いでしょうか?)。その際には Controller::add() や Controller::edit() に数行の追記が必要です。

参考
確認画面を挟む(CakePHPまとめ@Wiki)

トップページへ戻る / 前のページへ戻る

back to top