CakePHP2を学ぼう!優しいCakePHP超入門!スクリーンショット多め

CakePHP

この記事は2月12日(日)にGuildCafe Costa(ギルドカフェ コスタ)で行われた「8時間耐久CakePHP2勉強会@福岡」の勉強会の内容を元に書かれています。

  

この記事は1年以上前に書かれたものです。
情報が古い可能性があります。

INDEX

  1. CakePHPって?
  2. インストール方法
  3. 初期設定

    1. mod_rewriteの設定
    2. mod_rewriteを使わない場合は?
    3. /app/tmpディレクトリのパーミッション
    4. Security.saltとSecurity.cipherSeed
    5. データベースの設定
    6. 再度確認
  4. CakePHPのドキュメント
  5. Controller

    1. 「Hello World!」と表示するだけのシンプルなコントローラ
    2. アクションを追加してみる
    3. HelloWorldControllerという場合のURLは?
    4. 命名規則に従ったViewを呼び出す
    5. 呼び出すViewを指定する
    6. 自動レンタリングを無効にする
    7. Viewに値を渡す
    8. 別アクションやURLにリダイレクトさせる
    9. formの値を取得する
    10. $this->Session->setFlash()
    11. Modelの指定
    12. Scaffoldingの利用
  6. Model

    1. テーブルの作成
    2. データを全件取得する
    3. データの絞り込み
    4. データの絞り込み(LIKE検索)
    5. テーブルの関連付け($belongsTo)
    6. テーブルの関連付け($hasMany)
  7. View

    1. Layoutの変更
    2. Layout名の指定
    3. formの値を取得する(実はViewでも可能)
  8. 当日のスライド
  9. 主催者@yandoさんのブログ

1. CakePHPって?

CakePHP(けいくぴーえいちぴー)はPHPで作られているMVCフレームワークです。
フレームワークはシステムの基盤みたいなもので、通常システムで自分で実装しなければならない部分が最初からたくさん詰まってます。ライブラリやクラスの集まりとでもいうんでしょうか。
MVCはModel View Controllerの略でMVC定義はフレームワークによって若干意味合いが違うのですが、Modelはデータ制御、Viewは表示制御、ModelとViewを制御する司令塔がControllerみたいな感じです。

Model View Controller – Wikipedia

2. インストール方法

実際にインストールを行なってみましょう。
事前にXAMPPなりMAMPなりで以下が動作する環境を整えてください。

  • PHP ( >= 5.2.8 )
  • pdo_mysql
  • mod_rewrite

勿論MySQL以外のデータベースで動作させることも可能ですが、今回はMySQL前提に進めていきます。

PHP5.2.6はPDOのバグがあって、PHP5.2.7はセキュリティの脆弱性の問題で配布停止になっているのでCake2.0.6からPHP5.2.8以上が推奨されるようになりました。


CakePHPのページから安定版の2.0.6をダウンロードします。


PHPが動作するドキュメントルート(htdocsフォルダ)等にダウンロードしたファイルを解凍します。
取り敢えず今回は「cake」とリネームするとします。


「cake」フォルダの中身です。ちなみに「/lib/Cake」内のCakePHPのコアのファイルは触らないでください。

3. 初期設定

「http://localhost/cake/」にアクセスしてみましょう。
MAMPの方は「http://localhost:8888/cake/」かも知れませんが、以降説明時は「http://localhost/cake/」で統一します。

3-1. mod_rewriteの設定


上の画像のようにスタイルシートが適用されておらず「URL rewriting is not properly configured on your server. 1) Help me configure it 2) I don’t / can’t use URL rewriting」とエラーが表示されていないでしょうか。
このエラーはApache(Webサーバで)mod_rewriteというURL書き換えを行うモジュールが有効になっていない場合に出ます。

そこでApacheの設定ファイルを編集しましょう。
xamppの場合インストールディレクトリ配下の「apache/conf/httpd.conf」ファイルを編集します。

#LoadModule rewrite_module modules/mod_rewrite.so

↓「#」を外してコメントアウトを解除してください

LoadModule rewrite_module modules/mod_rewrite.so

編集し終えたらApacheの再起動を行います。


スタイルシートが適用された状態です。mod_rewriteが有効になりました。

3-2. mod_rewriteを使わない場合は?

「/app/Config/core.php」を編集します。

95行目の 「//Configure::write(‘App.baseUrl’, env(‘SCRIPT_NAME’));」の部分の「//」を外してコメントアウトを解除して保存した後、以下の.htacessファイルを削除します。

  • /.htaccess
  • /app/.htaccess
  • /app/webroot/.htaccess

3-3. /app/tmpディレクトリのパーミッション

「Your tmp directory is NOT writable.」と警告が出る場合は、読み書き可能にしておきましょう。

3-4. Security.saltとSecurity.cipherSeed

「/app/Config/core.php」の「Security.salt」と「Security.cipherSeed」の値を適当なものに変更します。
これはセキュリティ上変えておく必要があります。
(※警告にファイル名が出てるからってCORE/Cake/Utility/Debugger.phpを編集したら駄目ですよ)

Configure::write('Security.salt', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi');
Configure::write('Security.cipherSeed', '76859309657453542496749683645');

3-5. データベースの設定

「Your database configuration file is NOT present.
Rename APP/Config/database.php.default to APP/Config/database.php」
と警告が出ていますので、「/app/Config」の「database.php.default」というファイルを同ディレクトリにコピーして「database.php」というファイルを作成します。

「database.php」をご自身の環境に合わせて設定してください。
今回「cake」というデータベースを「utf8-general-ci」で作成しました。
(※存在しないデータベースを指定してもエラーになりますので注意してください)

public $default = array(
    'datasource' => 'Database/Mysql',    //MySQLを使用
    'persistent' => false,             //持続的接続使用しない
    'host' => 'localhost',               //DBホスト名
    'login' => 'root',                   //MySQLユーザ
    'password' => '',                    //MysQLパスワード
    'database' => 'cake',                //DB名
    'prefix' => '',                      //DB内のすべてのテーブルの頭に付ける接頭辞
    'encoding' => 'utf8',                //DBエンコーディング
);

encodingは文字化けを防ぐために設定しますが、encodingを指定すると内部的にはPDOクラスで「SET NAMES」が使用されてるみたいですね。
基本的に全て(DB・PHP・JS・HTML)の文字コードをUTF-8で統一してたら問題ないような気もしますが、プログラムから「SET NAMES」を投げるのは危険性があるようなので以下参考までに。

3-6 再度確認


前置きが長くなりましたが「http://localhost/cake/」にアクセスしてエラーや警告が出なくなればOKです。

4. CakePHPのドキュメント

CakePHP 2.0.xのマニュアルが以下に掲載されています。
日本語版はまだ不完全なようなので足りない部分は英語版を参照してください。

またAPIもこちらからご覧になれますので、CakePHPで用意されてるクラスで何をしてるのか、どういったものがあるのか調べるのに便利です。

5. Controller

では実際にControllerを書いてみましょう。記述は勿論PHPです。
取り敢えずModelやらViewやらはまだ使いません。

ControllerのPHPファイルは「/app/Controller/」内に作成します。

ControllerはCakePHPのコアで用意されている「AppController」というクラスを継承します。
HelloControllerを作成する場合

//AppControllerを継承したHelloController
class HelloController extends AppController {}

という風に書きます。

ファイル名(.phpを抜かした部分)とクラス名は同名になります。
「HelloController」というクラスを作成した場合、ファイル名は「HelloController.php」になります(※CakePHP2系からアンダーバー「_」などは使わずファイル名やフォルダ名には大文字を使用するようになりました)。

MVCフレームワークは大体こういった命名規則に乗っ取って繋がってることが多いのでここ注意が必要です。

5-1. 「Hello World!」と表示するだけのシンプルなコントローラ


「/app/Controller/HelloController.php」を作成します。

Viewを使用しないので

public $autoRender = false;

と書いて、Viewファイルの読み込みと、自動レンタリングを無効にします。

CakePHP1系では

var $autoRender = false;

などと書いていたかも知れませんが、これはPHP4と互換性を保つための記述なので、PHP5を前提としているCakePHP2系ではvarと書く必要はないでしょう。
(varはPHP5だとpublicメンバとみなされるのでCakePHP2系ではpublicと記述します。)

HelloController.php

<?php
/**
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /** ビュー未使用 */
    public $autoRender = false;
    
    /**
     * indexアクション
     */
    public function index() {
        echo "Hello World!";
    }
}

「http://localhost/cake/hello/」にアクセスします。
何故「hello」というパスを叩くのかというと「HelloController」の「Controller」を抜かして小文字にした部分となります。

結果

Hello World!

indexアクションが呼ばれて「Hello World!」と出力されます。

5-2. アクションを追加してみる

HelloController.php

<?php
/**
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /** ビュー未使用 */
    public $autoRender = false;
    
    /**
     * indexアクション
     */
    public function index() {
        echo "Hello World!";
    }
    
    /**
     * otherアクション
     */
    public function other() {
        echo "Other!";
    }
}

結果

「http://localhost/cake/hello/other/」にアクセスします。

Other!

otherアクションが呼ばれて「Other!」と出力されます。

5-3. HelloWorldControllerという場合のURLは?

HelloController.php

<?php
/**
 * /app/Controller/HelloWorldController.php
 */
class HelloWorldController extends AppController
{
    /** ビュー未使用 */
    public $autoRender = false;
    
    /**
     * indexアクション
     */
    public function index() {
        echo "Hello World!";
    }
}

結果

「http://localhost/cake/hello_world/」と「http://localhost/cake/helloworld/」という両方のURLでアクセス出来ました。

Hello World!

5-4. 命名規則に従ったViewを呼び出す

ここからViewも作成していきます。

  • /app/Controller/HelloController.php・・・ファイル
  • /app/View/Hello・・・ディレクトリ
  • /app/View/Hello/index.ctp・・・ファイル

上記三つを作成しましょう。

HelloController.php

<?php
/**
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /**
     * indexアクション
     */
    public function index() {}
}

index.ctp

<?php
/**
 * /app/View/Hello/index.ctp
 */
?>
Hello World:)

結果

「http://localhost/cake/hello/」

indexアクションを用意しておくと上記のURLにアクセスした時には「/app/View/Hello/index.ctp」というViewファイルが読み込まれます。

5-5. 呼び出すViewを指定する


/app/View/Hello/other.ctpを追加します。

HelloController.php

<?php
/**
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{   
    /**
     * indexアクション
     */
    public function index() {
        $this->render('other');    //other.ctpを表示
    }
}

other.ctp

<?php
/**
 * /app/View/Hello/other.ctp
 */
?>
Other!

結果

「http://localhost/cake/hello/」

読み込むViewをindexアクションで指定しているので上記のURLにアクセスした時には「/app/View/Hello/other.ctp」というViewファイルが読み込まれます。

5-6. 自動レンタリングを無効にする

HelloController.php

Controllerでは$autoLayoutというプロパティが「true」(有効)になっており、標準だと「/lib/Cake/View/Layouts」内にある「default.ctp」というレイアウトファイルで自動的にレンタリングされます。
これを無効にするには

public $autoLayout = false;

と記述します。

<?php
/**
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /** 自動レンタリング無効 */
    public $autoLayout = false;
    
    /**
     * indexアクション
     */
    public function index() {}
}

結果

「http://localhost/cake/hello/」

5-7. Viewに値を渡す

Viewに値を渡すには$this->set()メソッドを使用します。

$this->set(string $var, mixed $value)
$this->set(変数名, 値)

HelloController.php

<?php
/* 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /**
     * indexアクション
     */
    public function index() {
        //Viewに値を渡す(名前, 値)
        $this->set('message', 'Hello World:)');
    }
}

index.ctp

<?php
/**
 * /app/View/Hello/index.ctp
 */
echo $message;  //指定した名前で変数が使用できます

結果

「http://localhost/cake/hello/」

ちなみにこの例だとindexアクションで必ずセットしてるからNoticeエラーは出ないんですけれど、setしてないのにViewで使うとすると「Notice (8): Undefined variable〜」とNoticeエラーが発生します。
変数がセットされてるかはisset()関数でチェックできるんですが、Cake的にそういうものが用意されてるかもしれませんねー。

どうやらマニュアルを見てるとCake2.1ではViewクラスにfetchというメソッドが用意されてて

echo $this->fetch('message');

とかいう風に取得できそうですね。
ソース眺めてたらViewBlockクラスのgetメソッドに飛んでissetとかやってくれてるような感じだったので2.1からはfetch使った方がよさげですね。

5-8. 別アクションやURLにリダイレクトさせる

以下は「http://localhost/hello/」にアクセスした際に、別アクションやURLにリダイレクトさせる例です。

otherアクションにリダイレクト

<?php
/* 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /**
     * indexアクション
     */
    public function index() {
        //otherアクションにリダイレクト
        $this->redirect('/hello/other');
    }
    
    /**
     * otherアクション
     */
    public function other() {}
}

otherアクションにリダイレクト

<?php
/* 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /**
     * indexアクション
     */
    public function index() {
        //otherアクションにリダイレクト
        $this->redirect('other');
    }
    
    /**
     * otherアクション
     */
    public function other() {}
}

WebCakeにリダイレクト

<?php
/* 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /**
     * indexアクション
     */
    public function index() {
        //WebCakeにリダイレクト
        $this->redirect('http://webcake.no003.info/');
    }
}

5-9. formの値を取得する

フォームから入力された値をControllerで取得します。やっとアプリケーションらしくなってきましたね。

例のごとく「/app/Controller/HelloController.php」、「/app/View/Hello/index.ctp」、「/app/View/Hello/other.ctp」を作成します。

「index.ctp」が入力フォーム用のView、「other.ctp」が結果表示用のViewです。

(※例ではControllerからViewにsetメソッドでリクエストパラメータを受け渡していますが、こんなことしなくてもView自身で取得することも可能です。「formの値を取得する(実はViewでも可能)」

index.ctp

<?php
/**
 * /app/View/Hello/index.ctp
 */
?>
<form action="/cake/hello/other" method="POST">
    <div class="input text">
        <label for="txtTitle">テキスト</label>
        <input type="text" name="txt" id="txtTitle" />
    </div>
    <div class="submit">
        <input type="submit" value="Send" />
    </div>
</form>

HelloController.php

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /**
     * indexアクション
     */
    public function index() {}
    
    /**
     * otherアクション
     */
    public function other() {
    
        //リクエストがPOSTメソッドで送られてきた場合
        if($this->request->is('post')) {
            
            //formのパラメータ取得
            $post = $this->request->data('txt');
            
            //Viewへ値のセット
            $this->set('post', $post);
        }
        
    }
}

other.ctp

<?php
/**
 * /app/View/Hello/other.ctp
 */
 
//「post」という名前の変数がセットされていたら値を表示
if(isset($post)) echo $post;

結果

「http://localhost/cake/hello/」

index.ctp

<?php
/**
 * /app/View/Hello/index.ctp
 */
?>
<form action="/cake/hello/other" method="POST">
    <div class="input text">
        <label for="txtTitle">テキスト</label>
        <input type="text" name="data[Post][txt]" id="txtTitle" />
    </div>
    <div class="submit">
        <input type="submit" value="Send" />
    </div>
</form>

HelloController.php

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /**
     * indexアクション
     */
    public function index() {}
    
    /**
     * otherアクション
     */
    public function other() {
    
        //リクエストがPOSTメソッドで送られてきた場合
        if($this->request->is('post')) {
            
            //formのパラメータ取得
            $post = $this->request->data('Post.txt');
            
            //Viewへ値のセット
            $this->set('post', $post);
        }
        
    }
}

other.ctp

<?php
/**
 * /app/View/Hello/other.ctp
 */
 
//「post」という名前の変数がセットされていたら値を表示
if(isset($post)) echo $post;

結果

「http://localhost/cake/hello/」

5-10. $this->Session->setFlash()

$this->Session->setFlash()は登録完了時など一時的にメッセージを表示したい際などに使用できます。

index.ctp

<?php
/**
 * /app/View/Hello/index.ctp
 */
?>
<form action="/cake/hello/other" method="POST">
    <div class="input text">
        <label for="txtTitle">テキスト</label>
        <input type="text" name="txt" id="txtTitle" />
    </div>
    <div class="submit">
        <input type="submit" value="Send" />
    </div>
</form>

HelloController.php

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    /**
     * indexアクション
     */
    public function index() {}
    
    /**
     * otherアクション
     */
    public function other() {
    
        //リクエストがPOSTメソッドで送られてきた場合
        if($this->request->isPost()) {
            
            //formのパラメータ取得
            $post = $this->request->data('txt');
            
            //リクエストパラメータが空じゃない場合
            if($post !== '') {
                $this->Session->setFlash('送信完了');
            }
        }

        //indexアクションにリダイレクト
        $this->redirect('/hello');
        
    }
}

結果

「http://localhost/cake/hello/」

$this->Session->setFlash()でセットしたメッセージを表示する記述をViewに書いてないのに表示されるのは自動レンタリングが関係しています。
「/lib/Cake/View/default.ctp」を見てみると

<?php echo $this->Session->flash(); ?>

という記述があるのが分かります。
これがセットした値を表示する記述です:)

5-11. Modelの指定

使用するModelを指定する方法です。例は、配列で複数のModelの名前を指定しています。
省略時はControllerと同名になるようです(例の場合だとHello)。

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    //使用するモデルを指定
    public $uses = array('Hello', 'Users');
}

5-12. Scaffoldingの利用

Controllerで「public $scaffold;」と宣言するだけで標準の機能であるScaffoldingを利用することができます。
DBのテーブルとModelを作成することで対象のデータ一覧表示、データの追加・更新・削除などの機能を実装してくれます。

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{
    //標準機能を利用する
    public $scaffold;
}

6. Model

さて、ここからModelです。
ModelはDBのデータを扱うのでDBにテーブルを作成しなければなりません。
最初に作成したデータベース「cake」にテーブルを3つ作るとします。

6-1. テーブルの作成

以下のテスト用SQLをphpMyAdminで流してください。
「created」や「modified」というフィールドを作っておくと、saveメソッドで更新や追加をした際に自動で更新してくれるようです。

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
--
-- 分類テーブル
--
CREATE TABLE IF NOT EXISTS `terms` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL DEFAULT '',
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;

-- 分類データ
INSERT INTO `terms` (`id`, `name`, `created`, `modified`) VALUES
(1, '未分類', NOW(), NOW()),
(2, 'ブログ', NOW(), NOW());

--
-- 投稿記事テーブル
--
CREATE TABLE IF NOT EXISTS `posts` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `term_id` bigint(20) NOT NULL,
  `title` text NOT NULL,
  `content` longtext NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `term_id` (`term_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;

-- 投稿記事データ
INSERT INTO `posts` (`id`, `term_id`,`title`, `content`, `created`, `modified`) VALUES
(1, 1, 'Hello world!', 'こんにちは!これはテスト投稿です。', NOW(), NOW()),
(2, 2, 'CakePHPを始めてみよう!', 'CakePHPを始めてみましょう。これはテスト投稿です。', NOW(), NOW());

--
-- コメントテーブル
--
CREATE TABLE IF NOT EXISTS `comments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `post_id` int(11) NOT NULL,
  `title` tinytext NOT NULL,
  `content` text NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;

-- コメントデータ
INSERT INTO `comments` (`id`, `post_id`, `title`, `content`, `created`, `modified`) VALUES
(1, 1, 'おはようございます!', '今日もいい天気ですね!', NOW(), NOW()),
(2, 1, 'そうですね!', '今日も頑張りましょう!', NOW(), NOW()),
(3, 2, '質問なのですが', 'どうしたらよいでしょうか', NOW(), NOW());

terms

posts

comments

6-2. データを全件取得する

ここから駆け足で説明していくので、今回作成するファイルは下記のもの程度にします。

  • /app/Model/Comments.php
  • /app/Model/Posts.php
  • /app/Model/Terms.php

がModelのクラスファイルです。

Posts.php

先ずはPosts.phpを作成してみましょう。
使用テーブル名と主キーの設定を行なっています。

<?php
/** 
 * /app/Model/Posts.php
 */
class Posts extends AppModel 
{
    /** 使用テーブル名 */
    public $useTable = 'posts';

    /** 主キー(省略時は「id」になるので省略も可) */
    public $primaryKey = 'id';
}

HelloController.php

次にコントローラです。
使用するModelクラスを指定して、find(‘all’)メソッド(allはオプション)で全件取得して、デバッグ表示をします。

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{   
    /** 使用するモデルを指定 */
    public $uses = array('Posts');

    /** 自動レンタリング無効 */
    public $autoLayout = false;

    /**
     *  indexアクション
     */
    public function index() {
        //全件取得
        $data = $this->Posts->find('all');

        //デバッグ表示
        debug($data);
    }
}

index.ctp

Viewは空で構いません。

<?php
/**
 * /app/View/Hello/index.ctp
 */
?>

結果

「http://localhost/cake/hello/」

この場合だと、$data[0][‘Posts’][‘title’];に一件目の投稿のタイトルが入っていることになりますね。

app\Controller\HelloController.php(line 21)
Array
(
    [0] => Array
        (
            [Posts] => Array
                (
                    [id] => 1
                    [term_id] => 1
                    [title] => Hello world!
                    [content] => こんにちは!これはテスト投稿です。
                    [created] => 2012-03-05 18:46:07
                    [modified] => 2012-03-05 18:46:07
                )

        )

    [1] => Array
        (
            [Posts] => Array
                (
                    [id] => 2
                    [term_id] => 2
                    [title] => CakePHPを始めてみよう!
                    [content] => CakePHPを始めてみましょう。これはテスト投稿です。
                    [created] => 2012-03-05 18:46:07
                    [modified] => 2012-03-06 08:01:11
                )

        )

)

6-3. データの絞り込み

postsテーブルのtitleの値が「Hello world!」のデータを抽出する例です。

HelloController.php

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{   
    /** 使用するモデルを指定 */
    public $uses = array('Posts');
    
    /** 自動レンタリング無効 */
    public $autoLayout = false;
    
    /**
     *  indexアクション
     */
    public function index() {
        
        //絞り込み条件
        $conditions = array(
            'conditions' => array(
                'Posts.title' => 'Hello world!'
            )
        );
        
        //条件に一致するものを全件取得
        $data = $this->Posts->find('all', $conditions);
        
        //デバッグ表示
        debug($data);
    }
}

結果

「http://localhost/cake/hello/」

app\Controller\HelloController.php (line 29)

Array
(
    [0] => Array
        (
            [Posts] => Array
                (
                    [id] => 1
                    [term_id] => 1
                    [title] => Hello world!
                    [content] => こんにちは!これはテスト投稿です。
                    [created] => 2012-03-05 18:46:07
                    [modified] => 2012-03-05 18:46:07
                )

        )

)

6-4. データの絞り込み(LIKE検索)

postsテーブルのtitleの値に「Cake」を含むデータを抽出する例です。

HelloController.php

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{   
    /** 使用するモデルを指定 */
    public $uses = array('Posts');
    
    /** 自動レンタリング無効 */
    public $autoLayout = false;
    
    /**
     *  indexアクション
     */
    public function index() {
    
        //キーワード
        $value = "Cake";
        
        //絞り込み条件
        $conditions = array(
            'conditions' => array(
                'Posts.title LIKE' => '%' . $value . '%'
            )
        );
        
        //条件に一致するものを全件取得
        $data = $this->Posts->find('all', $conditions);
        
        //デバッグ表示
        debug($data);
    }
}

結果

「http://localhost/cake/hello/」

app\Controller\HelloController.php (line 32)
Array
(
    [0] => Array
        (
            [Posts] => Array
                (
                    [id] => 2
                    [term_id] => 2
                    [title] => CakePHPを始めてみよう!
                    [content] => CakePHPを始めてみましょう。これはテスト投稿です。
                    [created] => 2012-03-05 18:46:07
                    [modified] => 2012-03-06 08:01:11
                )

        )

)

6-5. テーブルの関連付け($belongsTo)

Postsを軸に考えると一対多の時の関係の時に使用します。
投稿(Posts)が数レコードに対して対象の分類(Terms)が一です。

Posts.php

<?php
/** 
 * /app/Model/Posts.php
 */
class Posts extends AppModel 
{
    /** 使用テーブル名 */
    public $useTable = 'posts';
    
    //モデルが属する上位モデルTermsを関連付ける
    //Posts(多) => Terms(一) の関係
    public $belongsTo = array(
        'Terms' => array(
            'foreignKey' => 'term_id'    //外部キー
        )
    );
}

Terms.php

<?php
/** 
 * /app/Model/Terms.php
 */
class Terms extends AppModel 
{
    /** 使用テーブル名 */
    public $useTable = 'terms';
}

HelloController.php

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{   
    /** 使用するモデルを指定 */
    public $uses = array('Posts');
    
    /** 標準機能を利用 */
    public $scaffold;
}

結果

「http://localhost/cake/hello/」
関連するデータ(分類のデータ)を結合して自動で取ってきてくれます:)

下記のSQLが発行されていることがわかります。

SELECT 
    `Posts`.`id`, 
    `Posts`.`term_id`, 
    `Posts`.`title`, 
    `Posts`.`content`, 
    `Posts`.`created`, 
    `Posts`.`modified`, 
    `Terms`.`id`, 
    `Terms`.`name`, 
    `Terms`.`created`, 
    `Terms`.`modified` 
FROM `posts` AS `Posts` 
    LEFT JOIN `terms` AS `Terms` ON (`Posts`.`term_id` = `Terms`.`id`) 
WHERE 1 = 1 LIMIT 20

6-6. テーブルの関連付け($hasMany)

投稿(Posts)が1レコードに対して複数のコメント(Comments)があります。
こういう時は、hasManyを使用します。

Posts.php

<?php
/** 
 * /app/Model/Posts.php
 */
class Posts extends AppModel 
{
    /** 使用テーブル名 */
    public $useTable = 'posts';
    
    //Postsに対して複数のレコードがある下位モデルCommentsを指定
    public $hasMany = array('Comments');
}

Terms.php

<?php
/** 
 * /app/Model/Terms.php
 */
class Terms extends AppModel 
{
    /** 使用テーブル名 */
    public $useTable = 'terms';
}

Comments.php

<?php
/** 
 * /app/Model/Comments.php
 */
class Comments extends AppModel 
{
    /** 使用テーブル名 */
    public $useTable = 'comments';
}

HelloController.php

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{   
    /** 使用するモデルを指定 */
    public $uses = array('Posts');
    
    /** 標準機能を利用 */
    public $scaffold;
}

結果

「http://localhost/cake/hello/」
関連するデータ(コメントのデータ)を自動で取ってきてくれます:)

下記のSQLが発行されていることがわかります。

SELECT 
    `Comments`.`id`, 
    `Comments`.`post_id`, 
    `Comments`.`title`, 
    `Comments`.`content`, 
    `Comments`.`created`, 
    `Comments`.`modified` 
FROM 
    `comments` AS `Comments` 
WHERE 
    `Comments`.`post_id` = (1) 

7. View

最後はViewです。Viewは表示部分なので、一番とっつきやすい部分だと思います。

7-1. Layoutの変更

今まで自動レンタリング機能について何度か書きましたが、これを独自のものにする方法です。

なんに使えるのさ?ってことなんですけれど、ヘッダーとかフッターとか読み込むCSSとかあらかじめ必要な共通レイアウトを作っておくことでぐんと楽になります。

「/lib/Cake/View/Layouts/default.ctp」ファイルを「/app/View/Layouts」内にコピーします。
CSSやJS,画像などは「/app/webroot」に入れます。

まずはそのままの状態のdefault.ctpを見てみましょう。

default.ctp

注目して欲しい箇所にはハイライトを入れています。

<?php
/**
 *
 * PHP 5
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @package       Cake.View.Layouts
 * @since         CakePHP(tm) v 0.10.0.1076
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */

$cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <?php echo $this->Html->charset(); ?>
    <title>
        <?php echo $cakeDescription ?>:
        <?php echo $title_for_layout; ?>
    </title>
    <?php
        echo $this->Html->meta('icon');

        echo $this->Html->css('cake.generic');

        echo $scripts_for_layout;
    ?>
</head>
<body>
    <div id="container">
        <div id="header">
            <h1><?php echo $this->Html->link($cakeDescription, 'http://cakephp.org'); ?></h1>
        </div>
        <div id="content">

            <?php echo $this->Session->flash(); ?>

            <?php echo $content_for_layout; ?>

        </div>
        <div id="footer">
            <?php echo $this->Html->link(
                    $this->Html->image('cake.power.gif', array('alt' => $cakeDescription, 'border' => '0')),
                    'http://www.cakephp.org/',
                    array('target' => '_blank', 'escape' => false)
                );
            ?>
        </div>
    </div>
    <?php echo $this->element('sql_dump'); ?>
</body>
</html>
24行目 <?php echo $this->Html->charset(); ?>

これはHTMLヘルパーと呼ばれるもので、HTMLヘルパーは様々なHTMLタグを出力してくれます。

<?php echo $this->Html->charset(); ?>

これは以下ように出力されます(デフォルトはUTF-8)

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
27行目 <?php echo $title_for_layout; ?>
<?php echo $title_for_layout; ?>

ページのタイトルを出力します。

Controllerから

$this->set('title_for_layout', 'ページタイトル');

とかでセット出来るみたいです。
(※Cake1.3から「$this->pageTitle = ‘ページタイトル’;」とか「$this->set(‘title’, ‘ページタイトル’);」とかしてもダメになったみたいです。)

30行目 echo $this->Html->meta('icon');
echo $this->Html->meta('icon');

これは次のように出力されます。

<link href="/cake/favicon.ico" type="image/x-icon" rel="icon" /><link href="/cake/favicon.ico" type="image/x-icon" rel="shortcut icon" />

「favicon.ico」は「/app/webroot」に置きます。

32行目 echo $this->Html->css('cake.generic');
echo $this->Html->css('cake.generic');

CSSファイルを読み込むタグを出力します。これは次のように出力されます。

<link rel="stylesheet" type="text/css" href="/cake/css/cake.generic.css" />

CSSファイルは「/app/webroot/css」に置いてください。
(※CakePHP2.1から「$this->fetch('css')」となるようです。)

34行目 echo $scripts_for_layout;
echo $scripts_for_layout;

JSファイルを読み込むタグ展開します(Viewテンプレートから$this->Html->scriptとかで渡される)。

(※CakePHP2.1から「$this->fetch('js')」となるようです。)

44行目 <?php echo $this->Session->flash(); ?>
<?php echo $this->Session->flash(); ?>

$this->Session->setFlashなどでセットされた値を表示します。

46行目 echo $content_for_layout;
echo $content_for_layout;

Viewファイルの内容を表示します(ここ大事です)。
今まで「/app/View/Hello」内に「index.ctp」とか「other.ctp」とか作ってきたと思いますが、Viewファイルとはそれらのことです。
(※CakePHP2.1から「$this->fetch('content')」となるようです。)

58行目 <?php echo $this->element('sql_dump'); ?>
<?php echo $this->element('sql_dump'); ?>

発行したSQLのデバッグ情報を出力しています。

7-2. Layout名の指定

ControllerでLayoutを指定することができます。
下記の例では「/app/View/Layouts/hello.ctp」というレイアウトファイルを使用します。

HelloController.php

<?php
/** 
 * /app/Controller/HelloController.php
 */
class HelloController extends AppController
{   
    /* レイアウトファイル指定 */
    public $layout = 'hello';
}

7-3. formの値を取得する(実はViewでも可能)

ControllerからsetメソッドでリクエストパラメータをViewに渡さなくてもViewでの取得が可能です。

<?php
/**
 * /app/View/Hello/index.ctp
 */
 
//フォームヘルパーを用いてフォーム出力
echo $this->Form->create();
echo $this->Form->input('txt');
echo $this->Form->end('Add');
 
//リクエストパラメータを出力
if( $this->request->isPost() ) {
    echo $this->request->data('txt');
}
?>

8. 当日のスライド

9. 主催者@yandoさんのブログ

  

共有やブックマークなど