nazolabo

なぞさんのブログ

CakePHP2.0ハンズオン@札幌で使用した資料を公開します

2011年11月26日に開催した、CakePHP2.0ハンズオン@札幌の資料をHTMLにしたものを公開します。
解説は現場で行ったため、説明不足の資料になっています。基本的には公式のチュートリアルと同一です。なので、以前の記事とも内容が重複しています。

1. 作業の注意・この資料について


プログラムを保存する場合、文字コードは「UTF-8」にして保存してください。改行コードは何でもいいです(LFのみ推奨)。
ファイル名の大文字小文字は間違えないように入力してください。
この資料は、http://book.cakephp.org/2.0/en/ を元に作成されています。

2. CakePHPのインストール


ダウンロード


http://cakephp.org/から、2.0.3 Stable(現時点での最新バージョン)のパッケージをダウンロードします。解凍し、Apacheが見える場所(htdocs直下など)に展開してください。
ここでは仮に、htdocs/cakeblog/に展開します。
XAMPP(Windows)環境の場合は、 ダウンロードして展開して作られた「cakephp-cakephp-2b55a02」(ファイル名はダウンロードしたバージョンによって違います)というフォルダを、「C:¥xampp¥htdocs」に移動します。その後、「 cakephp-cakephp-2b55a02」を「cakeblog」にリネームします。
MAMP(MacOSX)環境の場合は、ダウンロードして展開して作られた「cakephp-cakephp-2b55a02」(ファイル名はダウンロードしたバージョンによって違います)というフォルダを、「/Application/MAMP/htdocs」(あるいは「 アプリケーション -> MAMP -> htdocs」)に移動します。その後、「 cakephp-cakephp-2b55a02」を「cakeblog」にリネームします。

起動と設定


http://localhost/cakeblog/でアクセスできると思うので、アクセスしてみてください。(環境によっては変わるかもしれません)
エラーがいろいろ出ると思うので、確認していきます。(環境によって出るものと出ないものがあります)
「Please change the value of ‘Security.salt’ in app/Config/core.php to a salt value specific to your application」と出た場合

「app/Config/core.php」を開いて、

Configure::write(’Security.salt’, ‘DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi’);

の行の、よくわからない文字列の部分を、適当なよくわからない文字列に変更してください。(何でもいいです)

「Please change the value of ‘Security.cipherSeed’ in app/Config/core.php to a numeric (digits only) seed value 〜」と出た場合

「app/Config/core.php」を開いて、

Configure::write(’Security.cipherSeed’, ‘76859309657453542496749683645’);

の行の、よくわからない数字の部分を、適当なよくわからない数字に変更してください。

「Your database configuration file is NOT present.」と出た場合

次項で説明します。
「Cake is NOT able to connect to the database.」と出た場合

DBの設定は書けていますが、設定した通りのDBに接続ができません。次項を読みなおしてください。
「Warning: _cake_core_ cache was unable to write ‘cake_dev_ja’ to Apc cache in 〜」と出た場合

APCが無効になっています。APCを有効にするか、apc.soを読み込まないようにしてください。どちらもphp.iniで設定できます。
「Warning: _cake_core_ cache was unable to write ‘cake_dev_ja’ to File cache in 〜」と出た場合

APCは無効ですが、ファイルキャッシュへの書き込みができません。「chmod -R 777 app/tmp」で、いろいろエラーが消えます。
「Warning: /Application/MAMP/htdocs/cakeblog/app/tmp/cache/ is not writable in 〜」と出た場合

「chmod -R 777 app/tmp」で解決します。
「Your tmp directory is NOT writable.」と出た場合

「chmod -R 777 app/tmp」で解決します。
「URL rewriting is not properly configured on your server. 」と出た場合

Apacheでmod_rewriteを有効にしてください。できない場合は、app/Config/core.phpの、

//Configure::write(’App.baseUrl’, env(’SCRIPT_NAME’));

の行を、

Configure::write(’App.baseUrl’, env(’SCRIPT_NAME’));

と変更した上で、今後の説明の「http://localhost/cakeblog/」を、「http://localhost/cakeblog/index.php/」に読み替えてください。
(例:「http://localhost/cakeblog/posts」→「http://localhost/cakeblog/index.php/posts

3. DBの設定


「app/Config/database.php.default」を、「app/Config/database.php」にリネーム(あるいはコピー)してください。
中のプログラムの、

public $default = array(
        ‘datasource’ => ‘Database/Mysql’,
        ‘persistent’ => false,
        ‘host’ => ‘localhost’,
        ‘login’ => ‘user’,
        ‘password’ => ‘password’,
        ‘database’ => ‘database_name’,
        ‘prefix’ => ‘’,
        //’encoding’ => ‘utf8’,
);

の部分を、

public $default = array(
        ‘datasource’ => ‘Database/Mysql’,
        ‘persistent’ => false,
        ‘host’ => ‘localhost’,
        ‘login’ => ‘root’,
        ‘password’ => ‘’,
        ‘database’ => ‘cakeblog’,
        ‘prefix’ => ‘’,
        ‘encoding’ => ‘utf8’,
);

のように変更します。(独自のDB接続設定がある場合は、それに合わせて修正してください)
phpMyAdmin等で、「cakeblog」というデータベースを作成します。phpMyAdminの場合は、「新規データベースを作成する」の箇所に「cakeblog」と入力し、「照合順序」を、「utf8_bin」にして、「作成」を押してください。
その後、以下のSQLを入力します。phpMyAdminの場合は、「cakeblog」のデータベースを選択し、「SQL」タブで、以下を貼りつけて、「実行する」を押します。

CREATE TABLE posts (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
) Engine=InnoDB;

INSERT INTO posts (title,body,created)
    VALUES (’The title’, ‘This is the post body.’, NOW());
INSERT INTO posts (title,body,created)
    VALUES (’A title once again’, ‘And the post body follows.’, NOW());
INSERT INTO posts (title,body,created)
    VALUES (’Title strikes back’, ‘This is really exciting! Not.’, NOW());

4. モデルの作成


app/Model/Post.phpを作成します。

<?php

class Post extends AppModel {
}

5. コントローラーの作成


app/Controller/PostsController.phpを作成します。

<?php
class PostsController extends AppController {
    public $helpers = array (’Html’,’Form’);

    public function index() {
        $this->set(’posts’, $this->Post->find(’all’));
    }
}

6. ビューの作成


app/View/Posts/index.ctpを作成します。

<h1>Blog posts</h1>
<table>
    <tr>
        <th>Id</th>
        <th>Title</th>
        <th>Created</th>
    </tr>

    <?php foreach ($posts as $post): ?>
    <tr>
        <td><?php echo $post[’Post’][’id’]; ?></td>
        <td>
            <?php echo $this->Html->link($post[’Post’][’title’],
array(’controller’ => ‘posts’, ‘action’ => ‘view’, $post[’Post’][’id’])); ?>
        </td>
        <td><?php echo $post[’Post’][’created’]; ?></td>
    </tr>
    <?php endforeach; ?>

</table>

7. 詳細画面の作成


app/Controller/PostsController.phpを修正します。

<?php
class PostsController extends AppController {
    public $helpers = array(’Html’, ‘Form’);

    public function index() {
         $this->set(’posts’, $this->Post->find(’all’));
    }

    public function view($id = null) {
        $this->Post->id = $id;
        $this->set(’post’, $this->Post->read());
    }
}

app/View/Posts/view.ctpを作成します。

<h1><?php echo $post[’Post’][’title’]?></h1>

<p><small>Created: <?php echo $post[’Post’][’created’]?></small></p>

<p><?php echo $post[’Post’][’body’]?></p>

8. 新規作成画面の作成


app/Controller/PostsController.phpを修正します。

<?php
class PostsController extends AppController {
    public $components = array(’Session’);

    public function index() {
        $this->set(’posts’, $this->Post->find(’all’));
    }

    public function view($id) {
        $this->Post->id = $id;
        $this->set(’post’, $this->Post->read());

    }

    public function add() {
        if ($this->request->is(’post’)) {
            if ($this->Post->save($this->request->data)) {
                $this->Session->setFlash(’Your post has been saved.’);
                $this->redirect(array(’action’ => ‘index’));
            }
        }
    }
}

app/View/Posts/add.ctpを作成します。

<h1>Add Post</h1>
<?php
echo $this->Form->create(’Post’);
echo $this->Form->input(’title’);
echo $this->Form->input(’body’, array(’rows’ => ‘3’));
echo $this->Form->end(’Save Post’);

app/View/Post/index.ctpの末尾に、addへのリンクを追加します。

<?php echo $this->Html->link(’Add Post’, array(’controller’ => ‘posts’, ‘action’ => ‘add’)); ?>

9. バリデーション


app/Model/Post.phpを修正します。

<?php
class Post extends AppModel {
    public $validate = array(
        ‘title’ => array(
            ‘rule’ => ‘notEmpty’
        ),
        ‘body’ => array(
            ‘rule’ => ‘notEmpty’
        )
    );
}

10. 編集画面の作成


app/Controller/PostsController.phpに、以下の処理を追加します。

<?php
class PostsController extends AppController {
...
public function edit($id = null) {
    $this->Post->id = $id;
    if ($this->request->is(’get’)) {
        $this->request->data = $this->Post->read();
    } else {
        if ($this->Post->save($this->request->data)) {
            $this->Session->setFlash(’Your post has been updated.’);
            $this->redirect(array(’action’ => ‘index’));
        }
    }
}
…
}

app/View/Posts/edit.ctpを作成します。

<h1>Edit Post</h1>
<?php
    echo $this->Form->create(’Post’, array(’action’ => ‘edit’));
    echo $this->Form->input(’title’);
    echo $this->Form->input(’body’, array(’rows’ => ‘3’));
    echo $this->Form->input(’id’, array(’type’ => ‘hidden’));
    echo $this->Form->end(’Save Post’);
?>

app/View/Posts/index.ctpに、編集リンクを追加します。

<h1>Blog posts</h1>
<p><?php echo $this->Html->link("Add Post", array(’action’ => ‘add’)); ?></p>
<table>
    <tr>
        <th>Id</th>
        <th>Title</th>
                <th>Action</th>
        <th>Created</th>
    </tr>

<?php foreach ($posts as $post): ?>
    <tr>
        <td><?php echo $post[’Post’][’id’]; ?></td>
        <td>
            <?php echo $this->Html->link($post[’Post’][’title’], array(’action’ => ‘view’, $post[’Post’][’id’]));?>
                </td>
                <td>
            <?php echo $this->Form->postLink(
                ‘Delete’,
                array(’action’ => ‘delete’, $post[’Post’][’id’]),
                array(’confirm’ => ‘Are you sure?’)
            )?>
            <?php echo $this->Html->link(’Edit’, array(’action’ => ‘edit’, $post[’Post’][’id’]));?>
        </td>
        <td><?php echo $post[’Post’][’created’]; ?></td>
    </tr>
<?php endforeach; ?>

</table>

11. 削除機能の作成


app/Controller/PostsController.phpに、以下の処理を追加します。

<?php
class PostsController extends AppController {
...

public function delete($id) {
    if (!$this->request->is(’post’)) {
        throw new MethodNotAllowedException();
    }
    if ($this->Post->delete($id)) {
        $this->Session->setFlash(’The post with id: ‘ . $id . ‘ has been deleted.’);
        $this->redirect(array(’action’ => ‘index’));
    }
}
…
}

12. ルーティング


ルーティングを変更する場合は、app/Config/routes.phpを編集します。
現在は http://localhost/cakeblog/posts からアクセスしていますが、これを http://localhost/cakeblog/ でアクセスできるようにするには、

Router::connect(’/’, array(’controller’ => ‘posts’, ‘action’ => ‘index’));
//Router::connect(’/’, array(’controller’ => ‘pages’, ‘action’ => ‘display’, ‘home’));

のようにします。

13. 見た目の変更


app/View/Layouts/default.ctpを作成することによって、外観を自由に変更することができます。
以下の内容をサンプルに、自由に書き換えてみましょう。

<!DOCTYPE html>
<html>
<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>CakePHP 2.0 Sample</h1>
                </div>
                <div id="content">

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

                        <?php echo $content_for_layout; ?>

                </div>
                <div id="footer">
                        Powered by CakePHP
                </div>
        </div>
        <?php echo $this->element(’sql_dump’); ?>
</body>
</html>

14. コメント機能


ここまでの実装を元に、コメント機能を作成してみましょう。(時間が余った人向け)

15. 管理画面の作成


CakePHPには、管理画面を簡単に作成できる機能が入っています。
仮に、ユーザーというテーブルがあり、それを管理できる画面を簡単に作ります。
データベースに、以下のSQLを投入します。

CREATE TABLE users (
  id int(11) NOT NULL AUTO_INCREMENT,
  username varchar(255) NOT NULL,
  password varchar(255) NOT NULL,
  created datetime DEFAULT NULL,
  modified datetime DEFAULT NULL,
  PRIMARY KEY (id)
) Engine=InnoDB;

app/Model/User.phpを作成します。

<?php

class User extends AppModel {
}

app/Controller/UsersController.phpを作成します。

<?php

class UsersController extends AppController {
        public $scaffold;
}

http://localhost/cakeblog/users にアクセスしてみましょう。

16. 認証


一覧・表示以外は、認証が必要にします。
ここでは、先程作成した、Usersモデル及びコントローラーを使用します。
パスワードを暗号化しなければいけないので、自動で保存時に暗号化されるようにします。app/Model/User.phpを変更します。

<?php
App::uses(’AuthComponent’, ‘Controller/Component’);
class User extends AppModel {

// ...

public function beforeSave() {
    if (isset($this->data[$this->alias][’password’])) {
        $this->data[$this->alias][’password’] = AuthComponent::password($this->data[$this->alias][’password’]);
    }
    return true;
}

// ...

変更したら、http://localhost/cakeblog/usersから、適当にユーザーを作成してみてください。
作成したら、ログインとログアウトができるようにします。
ログイン関係はUsersControllerに追加します。app/Controller/UsersController.phpを編集します。

<?php

class UsersController extends AppController {
        public $scaffold;
        public $components = array('Session', 'Auth');

        public function beforeFilter() {
                parent::beforeFilter();
                $this->Auth->allow('login');
        }

        public function login() {
                if ($this->request->is('post')) {
                        if ($this->Auth->login()) {
                                $this->redirect($this->Auth->redirect());
                        } else {
                                $this->Session->setFlash(__('Invalid username or password, try again'));
                        }
                }
        }

        public function logout() {
                $this->redirect($this->Auth->logout());
        }
}

ログイン画面が必要なので、app/View/Users/login.ctpを作成します。

<div class="users form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('User');?>
        <fieldset>
<legend><?php echo __('Please enter your username and password'); ?></legend>
        <?php
                echo $this->Form->input('username');
                echo $this->Form->input('password');
        ?>
        </fieldset>
<?php echo $this->Form->end(__('Login'));?>
</div>

これでhttp://localhost/cakeblog/users/loginにアクセスするとログインができるようになります。
ログインできているのかどうかよくわからないので、ブログの投稿・編集・削除は、ログインが必要にします。
app/Controller/PostsController.phpを編集します。

<?php
class PostsController extends AppController {
        public $components = array('Session', 'Auth');

        public function beforeFilter() {
                parent::beforeFilter();
                $this->Auth->allow('index', 'view');
        }

        // .....

ついでに、ログインとログアウトのリンクを追加します。app/View/Layouts/default.ctpの適当な場所に、以下を追加します。

<?php
if ($login_user_id !== null) {
    echo "Welcome ".h($login_user_name)." !!<br />";
    echo $this->Html->link('Logout', array('controller' => 'users', 'action' => 'logout'));
} else {
    echo $this->Html->link('Login', array('controller' => 'users', 'action' => 'login'));
}
?>

このままではエラーになるので、$login_user_idと$login_user_nameが常に入るようにします。app/Controller/AppController.phpを作成します。

<?php

class AppController extends Controller
{
        public $components = array('Session', 'Auth');

        public function beforeFilter() {
                parent::beforeFilter();
                $this->set('login_user_id', $this->Auth->user('id'));
                $this->set('login_user_name', $this->Auth->user('username'));
        }
}

ブログの投稿時にユーザーIDを記録するようにします。
まず、postsテーブルにuser_idを追加します。phpMyAdmin等で、以下のSQLを入力してください。
ALTER TABLE posts ADD COLUMN user_id INT(11);
これに合わせて、PostsControllerのaddの際に、user_idを自動入力させます。app/Controller/PostsController.phpを編集します。

<?php
// app/Controller/PostsController.php
public function add() {
    if ($this->request->is(’post’)) {
        $this->request->data[’Post’][’user_id’] = $this->Auth->user(’id’); //Added this line
        if ($this->Post->save($this->request->data)) {
            $this->Session->setFlash(’Your post has been saved.’);
            $this->redirect(array(’action’ => ‘index’));
        }
    }
}