前回はデータベーススキーマに関するコンソール操作を説明したんだけど、なかなか興味深い内容だったと思う。
今回はいわゆるBake(ベイク)を使ってみる予定。
BakeというのはCakePHPというケーキを美味しく焼きあげるために用意された、いわばCakePHPのユーザランドファイルの自動生成ライブラリだ。簡単な設定で面倒な各種ファイルの準備をしてくれる。
作業に先立ち、今回のスキーマを書いておくので、コピペする人は参考にされたし。
app/Config/Schema/schema.php
class AppSchema extends CakeSchema {
public function before($event = array()) {
return true;
}
public function after($event = array()) {
}
public $users = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 5, 'key' => 'primary', 'collate' => NULL, 'comment' => ''),
'username' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 64, 'key' => 'index', 'collate' => 'utf8_general_ci', 'comment' => '', 'charset' => 'utf8'),
'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_general_ci', 'comment' => '', 'charset' => 'utf8'),
'is_enabled' => array('type' => 'boolean', 'null' => false, 'default' => NULL, 'collate' => NULL, 'comment' => ''),
'is_deleted' => array('type' => 'boolean', 'null' => false, 'default' => NULL, 'collate' => NULL, 'comment' => ''),
'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL, 'collate' => NULL, 'comment' => ''),
'modefied' => array('type' => 'datetime', 'null' => false, 'default' => NULL, 'collate' => NULL, 'comment' => ''),
'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'users_idx' => array('column' => array('username', 'password', 'is_enabled', 'is_deleted', 'created', 'modefied'), 'unique' => 0)),
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB')
);
public $profiles = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 5, 'key' => 'primary', 'collate' => null, 'comment' => ''),
'user_id' => array( 'type' => 'integer', 'null' => false, 'default' => null, 'length' => 5, 'key' => 'index', 'collate' => null, 'comment' => '' ),
'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 32, 'key' => 'index', 'collate' => 'utf8_general_ci', 'comment' => '表示名', 'charset' => 'utf8'),
'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL, 'collate' => NULL, 'comment' => ''),
'modified' => array('type' => 'datetime', 'null' => false, 'default' => NULL, 'collate' => NULL, 'comment' => ''),
'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'profile_idx' => array('column' => array('user_id', 'name', 'created', 'modified'), 'unique' => 1)),
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB')
);
}
このファイルを作成後、コンソールでスキーマを自動生成。$ ./cake schema -app ../../app create
このままでもいいんだけど、int型がただの整数になってしまっているので、可能であれば符号なしの正の数にしておきたい。
MySQL WorkbenchやphpMyAdminなどのGUIツールでunsignedに変更しても良いし、以下のSQLを実行するなどで対応しておこう。
ALTER TABLE `profiles` CHANGE `id` `id` INT( 5 ) UNSIGNED NOT NULL AUTO_INCREMENT ALTER TABLE `profiles` CHANGE `user_id` `user_id` INT( 5 ) UNSIGNED NOT NULL ALTER TABLE `users` CHANGE `id` `id` INT( 5 ) UNSIGNED NOT NULL AUTO_INCREMENT ALTER TABLE `users` CHANGE `is_enabled` `is_enabled` TINYINT( 1 ) UNSIGNED NOT NULL COMMENT '使用フラグ', CHANGE `is_deleted` `is_deleted` TINYINT( 1 ) UNSIGNED NOT NULL COMMENT '削除フラグ'
出来ればschemaコマンド側でこの対応をしてもらうと助かるんだけどね。しばらく掛かりそう。
■モデルを作ってみよう
さて、すでにデータベーススキーマが生成されているので、Bakeがモデルを認識してくれるはず。
一旦Bakeを起動して確かめてみよう。
$ ./cake bake -app ../../appこのコマンドがBakeの基本だ。正しく入力すると、以下のようなメニューが表示される。
Welcome to CakePHP v2.0.5 Console --------------------------------------------------------------- App : app Path: /cakephp2.0/htdocs/app/Console/../../app/ --------------------------------------------------------------- Interactive Bake Shell --------------------------------------------------------------- [D]atabase Configuration [M]odel [V]iew [C]ontroller [P]roject [F]ixture [T]est case [Q]uit What would you like to Bake? (D/M/V/C/P/F/T/Q) >上から順に説明すると、
- [D]atabase Configuration
- app/Config/database.phpの自動生成
- [M]odel
- app/Model内にモデルファイル自動生成
- [V]iew
- app/View内にビューファイル自動生成
- [C]ontroller
- app/Controller内にコントローラファイル自動生成
- [P]roject
- appと同じフォルダ構造(プロジェクト)を自動生成
- [F]ixture
- テストで使うフィクスチャ自動生成
- [T]est case
- テストケース自動生成
- [Q]uit
- Bakeの終了
ちなみにコマンドで直接指定することも可能だ。
その場合、上記には表示されてないが、プラグインも可能となっている。
以下がそのコマンドになる。
$ cake bake <オプション>オプションは以下が使用可能。
- db_config
- model
- view
- controller
- project
- fixture
- test
- plugin <プラグイン名>
- all
今回はすでにデータベーススキーマが存在しているのが前提なので、モデルから作ってみよう。「m」もしくは「M」をタイプしてEnterする。
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[F]ixture
[T]est case
[Q]uit
What would you like to Bake? (D/M/V/C/P/F/T/Q)
> m ◀ モデルを自動生成させるので「m」もしくは「M」
---------------------------------------------------------------
Bake Model
Path: /cakephp2.0/htdocs/app/Console/../../app/Model/
---------------------------------------------------------------
Use Database Config: (default/test)
[default] > ◀ database.phpの$defaultを使用するので「Enter」
Possible Models based on your current database:
1. Profile
2. User
Enter a number from the list above,
type in the name of another model, or 'q' to exit
[q] > 1 ◀ Profileモデルを先に作るので「1」
Would you like to supply validation criteria
for the fields in your model? (y/n)
[y] > n ◀ バリデーションはとりあえず後にするので「n」
Would you like to define model associations
(hasMany, hasOne, belongsTo, etc.)? (y/n)
[y] > y ◀ Userモデルとアソシエーションするので「y」もしくは「Enter」
One moment while the associations are detected.
---------------------------------------------------------------
Please confirm the following associations:
---------------------------------------------------------------
Profile belongsTo User? (y/n)
[y] > y ◀ Profile belongs to Userなので「y」もしくは「Enter」
Would you like to define some additional model associations? (y/n)
[n] > n ◀ 他にアソシエーション張るモデルは無いので「n」もしくは「Enter」
---------------------------------------------------------------
The following Model will be created:
---------------------------------------------------------------
Name: Profile
DB Table: `profiles`
Associations:
Profile belongsTo User
---------------------------------------------------------------
Look okay? (y/n)
[y] > y ◀ これで良いか?と聞かれるので「y」もしくは「Enter」
これだけの問答で、以下のソースが生成されるんだ。app/Model/Profile.php
App::uses('AppModel', 'Model');
/**
* Profile Model
*
* @property User $User
*/
class Profile extends AppModel {
/**
* Display field
*
* @var string
*/
public $displayField = 'name';
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* belongsTo associations
*
* @var array
*/
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
同じように、Userモデルも作ってみよう。
---------------------------------------------------------------
Bake Model
Path: /cakephp2.0/htdocs/app/Console/../../app/Model/
---------------------------------------------------------------
Possible Models based on your current database:
1. Profile
2. User
Enter a number from the list above,
type in the name of another model, or 'q' to exit
[q] > 2 ◀ Userモデルを作るので「2」
A displayField could not be automatically detected
would you like to choose one? (y/n)
> n ◀ $displayFieldが自動検出できなかったが使わないので「n」
Would you like to supply validation criteria
for the fields in your model? (y/n)
[y] > n ◀ バリデーションはとりあえず後にするので「n」
Would you like to define model associations
(hasMany, hasOne, belongsTo, etc.)? (y/n)
[y] > y ◀ Profileとアソシエーション張るので「y」もしくは「Enter」
One moment while the associations are detected.
---------------------------------------------------------------
Please confirm the following associations:
---------------------------------------------------------------
User hasMany Profile? (y/n)
[y] > n ◀ hasManyではないので「n」
User hasOne Profile? (y/n)
[y] > y ◀ hasOneなので「y」もしくは「Enter」
Would you like to define some additional model associations? (y/n)
[n] > n ◀ 他にアソシエーション張るモデルは無いので「n」もしくは「Enter」
---------------------------------------------------------------
The following Model will be created:
---------------------------------------------------------------
Name: User
DB Table: `users`
Associations:
User hasOne Profile
---------------------------------------------------------------
Look okay? (y/n)
[y] > y ◀ これで良いか?と聞かれるので「y」もしくは「Enter」
これでProfile.phpと同じように、以下のソースが自動生成される。app/Model/User.php
App::uses('AppModel', 'Model');
/**
* User Model
*
* @property Profile $Profile
*/
class User extends AppModel {
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* hasOne associations
*
* @var array
*/
public $hasOne = array(
'Profile' => array(
'className' => 'Profile',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
もうナイス過ぎて下痢しそうだ。モデルはこれで一旦完了とする。
■コントローラを作ってみよう
さて、モデルファイルを自動生成したように、今度はコントローラを作ってみようじゃないか。
どういった内容のアクションを作るのかというと、基本的な以下の5つになる。
- Userの一覧
- Userの詳細
- Userの追加
- Userの編集
- Userの削除
という訳で早速行ってみよう。
Welcome to CakePHP v2.0.5 Console
---------------------------------------------------------------
App : app
Path: /cakephp2.0/htdocs/app/Console/../../app/
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[F]ixture
[T]est case
[Q]uit
What would you like to Bake? (D/M/V/C/P/F/T/Q)
> c ◀ Controllerを作るので「c」もしくは「C」
---------------------------------------------------------------
Bake Controller
Path: /cakephp2.0/htdocs/app/Console/../../app/Controller/
---------------------------------------------------------------
Use Database Config: (default/test)
[default] > ◀ database.phpの$defaultを使用するので「Enter」
Possible Controllers based on your current database:
1. Profiles
2. Users
Enter a number from the list above,
type in the name of another controller, or 'q' to exit
[q] > 2 ◀ Usersコントローラを作成するので「2」
---------------------------------------------------------------
Baking UsersController
---------------------------------------------------------------
Would you like to build your controller interactively? (y/n)
[y] > y ◀ 対話式に構築していくので「y」もしくは「Enter」
Would you like to use dynamic scaffolding? (y/n)
[n] > n ◀ 動的スカフォルドしないので「n」もしくは「Enter」
Would you like to create some basic class methods
(index(), add(), view(), edit())? (y/n)
[n] > y ◀ 一覧、追加、詳細、編集アクションを自動生成するので「y」
Would you like to create the basic class methods for admin routing? (y/n)
[n] > n ◀ 管理用アプリは今は作らないので「n」もしくは「Enter」
Would you like this controller to use other helpers
besides HtmlHelper and FormHelper? (y/n)
[n] > n ◀ 今のところHTMLヘルパー、Formヘルパー以外使わないので「n」もしくは「Enter」
Would you like this controller to use any components? (y/n)
[n] > n ◀ コンポーネントはまだ使用しないので「n」もしくは「Enter」
Would you like to use Session flash messages? (y/n)
[y] > y ◀ セッションフラッシュメッセージを使うので「y」もしくは「Enter」
---------------------------------------------------------------
The following controller will be created:
---------------------------------------------------------------
Controller Name:
Users
---------------------------------------------------------------
Look okay? (y/n)
[y] > y ◀ これでよろしいかと聞かれるので「y」もしくは「Enter」
これで以下のようなソースが自動生成される。app/Controller/UsersController.php
App::uses('AppController', 'Controller');
/**
* Users Controller
*
* @property User $User
*/
class UsersController extends AppController {
/**
* index method
*
* @return void
*/
public function index() {
$this->User->recursive = 0;
$this->set('users', $this->paginate());
}
/**
* view method
*
* @param string $id
* @return void
*/
public function view($id = null) {
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
$this->set('user', $this->User->read(null, $id));
}
/**
* add method
*
* @return void
*/
public function add() {
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
/**
* edit method
*
* @param string $id
* @return void
*/
public function edit($id = null) {
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('The user has been saved'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
} else {
$this->request->data = $this->User->read(null, $id);
}
}
/**
* delete method
*
* @param string $id
* @return void
*/
public function delete($id = null) {
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
if ($this->User->delete()) {
$this->Session->setFlash(__('User deleted'));
$this->redirect(array('action' => 'index'));
}
$this->Session->setFlash(__('User was not deleted'));
$this->redirect(array('action' => 'index'));
}
}
NetBeansのナビゲータでコントローラを見てみると、このような感じになっている。簡単にアクションを説明してみよう。
indexアクション
Userモデルから全データを引っ張ってきて、users変数に代入している。
viewアクション
$idをidに持つUserモデルから1つデータを引っ張ってくる。
$idの指定がなかった場合はNotFoundException例外を投げる。
addアクション
ポストされた場合、入力されたデータをUserモデルに保存する。
保存が失敗したらエラーメッセージをフラッシュする。
editアクション
$idの指定がなかった場合はNotFoundExceptionという例外を投げる。
$idをidにもつUserモデルから1つデータを引っ張ってくる。
ポストされた場合、入力されたデータをUserモデルに保存する。
ポストされてなければ引っ張ってきたデータをリクエストデータに代入
deleteアクション
リクエストがpost形式出なかった場合、MethodNotAllowedException例外を投げる。
Userモデルのidプロパティをセットする。
該当するUserモデルがなければNotFoundException例外を投げる。
該当するUserモデルを削除し、メッセージをフラッシュする。
削除に失敗したらエラーメッセージをフラッシュする。
だいたいこんな感じだ。
同じようにして、ProfileControllerも作っておいてくだされ。
このBakeで自動生成されたソースはかなり参考になるので、最低一度はBakeでコントローラを作成しておくことをすすめるよ。
■ビューをつくってみよう
さて、モデル、コントローラとできたら、今度は具体的にブラウザで表示するビューの出番だ。これもBakeで自動生成させてしまおう。
Welcome to CakePHP v2.0.5 Console --------------------------------------------------------------- App : app Path: /cakephp2.0/htdocs/app/Console/../../app/ --------------------------------------------------------------- Interactive Bake Shell --------------------------------------------------------------- [D]atabase Configuration [M]odel [V]iew [C]ontroller [P]roject [F]ixture [T]est case [Q]uit What would you like to Bake? (D/M/V/C/P/F/T/Q) > v ◀ ビューファイルを自動生成するので「v」もしくは「V」 --------------------------------------------------------------- Bake View Path: /home/develop/cake2.torhamzedd.com/htdocs/app/Console/../../app/View/ --------------------------------------------------------------- Use Database Config: (default/test) [default] > ◀ database.phpの$defaultを使用するので「Enter」 Possible Controllers based on your current database: 1. Profiles 2. Users Enter a number from the list above, type in the name of another controller, or 'q' to exit [q] > 2 ◀ Usersコントローラがベースになるので「2」 Would you like bake to build your views interactively? Warning: Choosing no will overwrite Profiles views if it exist. (y/n) [n] > n ◀ まだProfile用のビューは作らないので「n」もしくは「Enter」これで、以下のファイルが生成される。
- app/View/Users/add.ctp
- app/View/Users/edit.ctp
- app/View/Users/index.ctp
- app/View/Users/view.ctp
Usersと同じく、Profilesのビューも作っておいてくだされ。
これでひと通り、最低限ブラウザで確認できるまで自動生成させることができた。
■ブラウザで見てみよう
アクセスするURLは/usersになる。
自動的にUsersControllerのindexアクションが実行され、Users/index.ctpをテンプレートとして表示してくれるはずだ。
これまたなんと凝った画面だこと。
CakePHP1.2の時代に比べると、恐ろしく発展を遂げているではないか。
画面左側にはナビゲーションまで付いている。
- New User
- List Profile
- New Profile
試しに「New User」でユーザを追加してみよう。
適当にデータを入れて、「Submit」ボタンをクリック。
正直言うと、赤はやめて欲しいんだけど、どうやら問題なくUserモデルにユーザが追加できたようだ。
セッションフラッシュでメッセージを表示させているようなので、F5キーなどでリロードしたら、この赤い忌々しいメッセージは消える。
さて、次はプロフィールを追加だ。画面左の「New Profile」をクリック。
俺の嫁の名前を入れて、「Submit」ボタンをクリック。
正直言うと、赤はやめて欲しいんだけど、どうやら問題なくProfileモデルにプロフィールが追加できたようだ。
セッションフラッシュでメッセージを表示させているようなので、F5キーなどでリロードしたら、この赤い不安なメッセージは消える。
「List User」で一旦一覧へ戻り、今追加したユーザの「View」ボタンを押してみよう。
このように、俺の嫁の詳細を確認することができる。
パスワードがモロ出しなのはサービスだ。というのは嘘で、Authコンポーネントを使えば勝手に暗号化してくれるので、安心されたし。
ちなみに「Delete」する場合、即座には消さない。一旦アラートダイアログが表示され、そこで本当に消すのかどうかを再度確認することができるので、結構安心。
OSやブラウザによっては、予め「OK」にフォーカスが当たっていることがあるので、くれぐれもEnterやスペースキーの連打には要注意だ。
■終わり
Bakeは本当に開発を助けてくれると思う。
Webアプリの設計段階でまず、モデルを決めてしまおう。そうすればスキーマが決まり、あとはBakeで焼くだけだ。
実際には、モデルにはビヘイビア(Behavior)、コントローラにはコンポーネント(Component)、ビューにはヘルパー(Helper)という拡張要素が存在し、ビヘイビアから開発し始めるようなツワモノもいるようだが、慌てないでも大丈夫。いつか、俺も君もそうなる。
というわけで、ざっくりとBakeを解説してみた。
俺もCakePHP2.0のBakeは初めてなので、いろいろ面白かった。
ぜひみんなにもやってみて欲しいと思う。そしてわかったことを俺にそっと教えてくれれば、少なくともそっちに足を向けて寝ることは無いと思うんで、ぜひよろしくだ。









facebook
twitter
google+
fb share