はじめに: CakePHPのMVCモデルについて
MVCモデルは、データや再利用可能なアプリケーションの機能を表すモデル(M)、処理結果を表示したりユーザインターフェイスを提供するビュー(V)、そしてモデルとビューの間に入って処理を制御するコントローラ(C)の各機能をできるだけ明確に分けて、わかりやすいプログラムを書くための考え方で、Webアプリケーションの他さまざまなアプリケーション開発に適用されています。MVCモデルについて詳しくは解説書やWebページがたくさんありますのでそれらに譲らせていただき、ここではCakePHPにおけるMVCモデルの実装の全体像を簡単に見ておきましょう。
モデル(M)
MVCモデルと一口に言っても、実装によって少しずつ違った考え方があるようです。CakePHPにおけるモデルは、データベースのテーブルを表すと考えるとよいと思います。モデルにはテーブル間の関係やバリデーションルールなどを記述します。モデルはAPP/models/の直下に、データベースのテーブルの単数形の名前をつけて配置します。
コントローラ(C)
コントローラの役割は、モデル等を使って処理を行い結果をビューに渡しすことです。コントローラクラスの各メソッドがそれぞれアクションと呼ばれる処理になります。コントローラは、APP/controllers/の直下にモデルの複数形に"Controller"(ファイル名では"_controller")をつけた名前で配置します。
CakePHPでリクエストを受け取るためのURLの規則は次のようになります。これを見ると、コントローラの働きがよくわかると思います。
http://server.domainname.com/コントローラ名/アクション名/パラメータ1/パラメータ2/...
または
http://server.domainname.com/コントローラ名/アクション名?パラメータ1=値1&パラメータ名2=値2...
ビュー(V)
ビューはコントローラから受け取った処理結果を出力するHTMLを記述したファイルで、基本的にコントローラのアクションごとに作成するビューテンプレートと、複数のアクションやコントローラで共通して表示するミニビューであるエレメント、ビューテンプレートやエレメントを整形して配置してページ全体の構成を記述するレイアウトの3つがあります。一般にビューといった場合はビューテンプレートのことを指します。ビューテンプレートはAPP/views/の下にモデル名の複数形のディレクトリを作成してその下にアクション名.ctpという名前をつけて配置します。エレメントとレイアウトはAPP/views/elements, APP/views/layoutsにそれぞれ配置し、拡張子はいずれも.ctpです。
CakePHPの命名規則について詳しくは、マニュアルの2.4.CakePHPの規約をご覧ください。
開発の手順
次のような手順で行うとやりやすいと思います。
- 仕様の策定:アプリケーション全体の仕様を決めます。
- データベースの設計:CakePHPでの開発は「はじめにテーブル有りき」でテーブルが全ての基準となるので、これをはじめにきちんと行うことが大切です。
- モデルの作成:設計したテーブルを基にモデルクラスを作成します。原則、1つのテーブルに対応するモデルを1つ作成します。モデルにはリレーションとバリデーションルールを記述します。
- コントローラの作成:コントローラは最も関係の深いモデル1つと対応関係があるのが原則ですが、モデルを複数持つこともできます。コントローラにはアプリケーション固有のアクションメソッドを記述します。
- ビューの作成:まずはどのページでも共通して使用するレイアウトを作成し、次にそれぞれのアクションに対応するビューを作成します。必要に応じてelementも作成します。
1. 今回作成するユーザ登録の仕様
それではいよいよユーザ登録機能の作成を始めましょう。ここでは一般的に使えそうな次のような仕様を想定します。
仕様
ユーザ登録画面で、ユーザがフォームから名前、メールアドレス、パスワード入力して「登録する」ボタンを押すと、入力されたデータがデータベースに保存され、登録完了画面が表示される。
とりあえず今回はここまでを目標にします。これから作ろうとしているユーザ登録機能の具体的な動作をイメージすると、次のようになります。
- ユーザは/users/registerにアクセスする。
- ブラウザに新規登録フォームが表示される。
- ユーザはフォームに名前、メールアドレス、パスワードを入力して「登録する」ボタンを押す。
- /users/addにアクセスされ、入力した内容がPOSTされる。
- POSTされた内容がデータベースに挿入される。
- ブラウザに登録完了画面が表示される。
2. ユーザテーブルの作成
まずはデータベースにテーブルを作成します。"users"テーブルは次のような構造にすることにします。
| Field | Type | Null | Key | Default | Extra |
|---|---|---|---|---|---|
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(30) | NO | - | - | - |
| varchar(40) | NO | UNI | - | - | |
| password | varchar(80) | YES | - | NULL | - |
| created | datetime | YES | - | NULL | - |
| modified | datetime | YES | - | NULL | - |
"id"列はCakePHPに主キーとして認識されます。また、"created"と"modified"列が存在すると、CakePHPは自動的にレコードを挿入した日時と最後に変更した日時をそれぞれ記録してくれます。
列名は全て小文字にすることに注意してください。SQLは大文字小文字を区別しませんが、CakePHPの中では列名は全て小文字であることを前提にしています。
コマンドプロンプトを開いてMySQLに接続し、次のCREATE文を実行してテーブルを作成します。
>mysql -u (ユーザ名) -p Enter password: ********* (パスワード) Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 78 Server version: 5.0.45-community-nt MySQL Community Edition (GPL) Type 'help;' or 'h' for help. Type 'c' to clear the buffer. mysql> use (データベース名); Database changed CREATE TABLE users( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(30) NOT NULL, email VARCHAR(40) NOT NULL, password VARCHAR(80), created DATETIME, modified DATETIME, PRIMARY KEY(id), UNIQUE KEY(email) ) COMMENT='User information table'; Query OK, 0 rows affected (0.09 sec)
3. モデルの作成
テーブルが作成できたら、次にモデルを作ります。
eclipseを開いて、APPプロジェクトのmodelsディレクトリの下に、"user.php"というPHPファイルを作成します。テーブル名が"users"なので、その単数形の"user"がファイル名に、はじめの1文字を大文字で表記した"User"がクラス名になります。
user.phpには次のように記述します。
APP/models/user.php<?php
class User extends AppModel {
}
?>
このように、自作する全てのモデルクラスはAppModelクラスを継承するようにします。
本来ならばここにテーブルの関係やバリデーションルールを書くのですが、それぞれテーマが大きいので別のコラムでご紹介することにして、今回はこのまま次へ進みます。
4. コントローラの作成
今作成したモデルに対応するコントローラをAPPプロジェクトのcontrollersの下に作成します。モデルのクラス名が"User"なので、コントローラのクラス名はそれを複数形にして後ろに"Controller"をつけた、"UsersController"に、ファイル名はこれを小文字にしてアンダースコア("_")区切りにした"users_controller.php"になります。
controllersディレクトリの直下に"users_controller.php"というPHPファイルを作成したら、このファイルに次のように記述します。
APP/controllers/users_controller.php<?php
class UsersController extends AppController {
}
?>
このように、自作する全てのコントローラクラスはAppControllerクラスを継承するようにします。
コントローラにはアクションメソッドを記述するのでしたね。今回はユーザ登録フォームを表示するアクション"register"と、実際にデータベースにデータを挿入するアクション"add"を作成することにしましょう。
APP/controllers/users_controller.php<?php
class UsersController extends AppController {
public function index() {
$this->redirect('register');
}
public function register() {
}
public function add() {
$this->User->save($this->data);
}
}
?>
ここで、index()というメソッドを作成しておくと、URLでアクション名が省略されたときにここに記述した処理が実行されます。私的にはindexには処理は記述せず、アクション名が省略されたときに実行したいアクションメソッドにリダイレクトするだけにしています。リダイレクトには、Controllerクラスから継承したredirect()メソッドを使用します。
registerアクションは特に処理をせずにフォームを表示するだけなので、メソッドには何も記述しません。これが無駄に思える人は、addメソッドでポストデータがなければフォーム表示、あれば登録といったようにしてもよいでしょう。実際、CakePHPのマニュアルのサンプルプログラムや市販の本やWeb上の解説ページなどにはその方法で書かれているものの方が多いようです。ただ、私的にはメソッドを分けたほうがずっとわかりやすくて、サーバとクライアントのやりとりが自然な流れに思えるので、今回は分けて書くことにしました。
add()メソッドに今回書くのはたった一行だけです。コントローラからはこのようにモデルのクラス名を通してアクセスできるようになります。これも、「コントローラ名はモデル名の複数形に"Controller"をつけたもの」という規約を守っているからこそできることで、こういった規約のおかげでかなりの労力を減らすことができます。
ここではModelクラスのメソッドであるsave()を呼んでいます。引数で渡している$this->dataには、フォームから送られてきたデータが配列として入っています。これにより、save()メソッドはフォームから送られてきたデータを受け取り、モデル内でINSERT文が発行されてデータベースにデータが挿入されます。
5. ビューの作成
ビューはAPP/views/の下にモデルの複数形で小文字の名前のディレクトリを作成し、その下に各アクションごとのビューテンプレートを作成するのでした。今回はモデル名がUserなので、APP/views/の下に"users"というディレクトリを作成し、その下にregisterアクション用の"register.ctp"と、addアクション用の"add.ctp"という2つのビューテンプレートファイルを作成しましょう。ファイルを作成したら、それぞれのファイルに次のように記述します。
APP/views/users/register.ctp<div>
<?php
echo $form->create('User');
echo $form->input('name');
echo $form->input('email');
echo $form->input('password');
echo $form->end('登録する');
?>
</div>
APP/views/users/add.ctp
<div>
<p>ユーザ登録が完了しました!</p>
</div>
register.ctpの中に$formという変数がいきなり出てきていますが、これはViewクラスで作られるFormヘルパーというもののオブジェクトです。ここで呼んでいるinput()というメソッドを使うと、テキストボックスやラジオボタンといったHTMLのformの要素を簡単に作成することができます。input()の最初の引数には、現在使用中のテーブル(users)の列名を渡します。input()は対応するテーブルの列の方に応じてさまざまなタイプの入力フォームを魔法のように作成してくれるので、CakePHPのマニュアルでは"Automagic"と表現されているほどです。
input()のコールの前後はcreate()とend()のコールで囲みます。create()~end()が<form>と</form>で囲まれるわけです。create()の引数には使用するモデル名を、end()の引数にはsubmitボタン上に表示するラベルをそれぞれ渡します。
このコードは実行時には次のようなHTMLを出力します。
<div>
<form id="UserAddForm" method="post" action="/users/add">
<fieldset style="display:none;">
<input type="hidden" name="_method" value="POST" />
</fieldset>
<div class="input text">
<label for="UserName">Name</label>
<input name="data[User][name]" type="text" maxlength="30" value="" id="UserName" />
</div>
<div class="input text">
<label for="UserEmail">Email</label>
<input name="data[User][email]" type="text" maxlength="40" value="" id="UserEmail" />
</div>
<div class="input password">
<label for="UserPassword">Password</label>
<input type="password" name="data[User][password]" value="" id="UserPassword" />
</div>
<div class="submit">
<input type="submit" value="登録する" />
</div>
</form>
</div>
「このぐらいたいして大変ではないんだから、HTMLタグを手で書けばいいじゃん?」と思われるかもしれませんが、Formヘルパーは単にHTMLタグを吐き出すだけではなく、他にもさまざまなことをやってくれます。例えば・・・
- 対応するデータベースのテーブルの列が日付型の場合、日付入力用のコンボボックスを自動的に作成してくれる。
- データベースからとってきたデータをそのままリストとして表示したいときに、コントローラから配列をセットするだけで、簡単に<options>のリストを作成してくれる。
- 作成したリストでどの値をデフォルトで選択しておくかを簡単に指定できる。
などなど。これらのことをFormヘルパーを使わないで行うのは結構大変です。PHPスクリプトで処理を書いたら数十行になってしまうところを、Formヘルパーを使えば一行で済むこともあるでしょう。
さて、addアクションに対応するビューができあがりました。コントローラで処理を行った結果がこのビューテンプレートに適用されて、レイアウトに出力されます。レイアウトの作成は第二回でもやりましたが、ここでは次のような超単純なレイアウトを使うことにします。APP/views/layouts/の下に、default.ctpという名前のファイルを作成し、下の内容を記述します。(既にレイアウトが用意されている場合は作り直す必要はありません。)
APP/views/layouts/default.ctp<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<link rel="stylesheet" type="text/css" href="/css/base.css" />
<title><?php echo $title_for_layout; ?></title>
</head>
<body>
<div id="page">
<?php if (isset($content_for_layout)) { echo $content_for_layout; } ?>
</div>
</body>
</html>
最後に、トップページに今作成したusersコントローラのaddアクションを呼ぶためのリンクを作成しましょう。
APP/views/pages/home.ctp<div>
<h1>Awaresoft開発室</h1>
<ul>
<li><a href="/users/register">ユーザ登録</a></li>
</ul>
</div>
お疲れ様でした!これでモデル、コントローラ、ビューの一通りの作成が終わりました。
6. 実行してみましょう!
それでは早速ブラウザから実行してみましょう。トップページにアクセスして、users/registerへのリンクをクリックします。次のようなフォームが表示されれば成功です。

あら。。。ラベルが"Name"、"Email"、"Password"と英語で表示されてしまいました。これはCakePHPがテーブル名から判断してつけてくれたものです。残念ながら翻訳まではしてくれないんですね。そこで、ラベルを指定してみましょう。ビューとして作成したregister.ctpを次のように編集します。
APP/views/users/register.ctp<div>
<?php
echo $form->create('User');
echo $form->input('name', array('label' => 'お名前'));
echo $form->input('email', array('label' => 'Eメール'));
echo $form->input('password', array('label' => 'パスワード'));
echo $form->end('登録する');
?>
</div>
結果は次のようになりました。

今度はラベルが日本語で表示されました。このように、Formヘルパーのinput()メソッドの第二引数にオプションの配列を渡すことで、さまざまな設定が可能です。上の例で'label'に値を設定する変わりに、falseを指定するとラベルを表示しないようにもできますので、ラベルは自分でHTMLで書きたいという場合にも柔軟に対応できます。その他のオプションの種類はマニュアルの7.3.3 Automagic Form Elementsをご覧ください。
さて、ブラウザ上で「お名前」「Eメール」「パスワード」をそれぞれ入力して、「登録する」ボタンを押してみましょう。/users/addアクションが実行されて、画面に「ユーザ登録が完了しました!」と表示されるはずです。

最後に、今入力した内容がきちんとデータベースに挿入されているか確認してみましょう。MySQLのコマンドラインからSELECT文を実行してみます。
mysql> select * from users; +----+------+-----------------+----------+---------------------+---------------------+ | id | name | email | password | created |modified | +----+------+-----------------+----------+---------------------+---------------------+ | 1 | 倉知 | このメールアドレスは、スパムロボットから保護されています。アドレスを確認するにはJavaScriptを有効にしてください | password | 2008-09-09 23:43:39 |2008-09-09 23:43:39 | +----+------+-----------------+----------+---------------------+---------------------+ 1 row in set (0.00 sec) mysql>
ちゃんと挿入できていますね。





