CakePHP のアソシエーション!モデルを連携させて複数テーブルからデータを取得する勉強します! | oki2a24 の続きの投稿となります。
本を参考に、掲示板アプリを勉強しました。ただ、こう変えたほうがいいかもしれない、という点がいくつかありましたので、メモして残しておきます。
ポイント
- 引数が必要だったり入らなかったりするアクションの場合、関数の定義で引数を書きますけれども引数がなかった場合に備えて null などを設定します。そうしないと、エラーが出ます。
たとえば、public function edit($param = NULL) などとします。 - FormHelper、HtmlHelper をきちんと使いたい。直にタグを書いてごり押さないようにしたいです。
前提
- CakePHP 2.1.3 導入済み。
- ドキュメントルート/cake/sample/(ここにCakePHP が入ってます。app ディレクトリとか、.travis.yml ファイルとか。)
お勉強アプリ、「掲示板」の簡単な仕様
アプリ自体の仕様は次のようになります。
- 一覧 http://localhost/cake/sample/boards 投稿者、タイトル、を一覧表示します。
- 投稿者をクリックで、ユーザ情報を表示します。
- ユーザを登録する画面はありません。そのため、personals テーブルに直接入れてください。
- タイトルをクリックで、投稿詳細ページにジャンプします。投稿者、タイトル、内容を表示します。編集ページへのリンクを設置します。
- 編集ページでは、投稿者の名前を表示します。パスワード、タイトル、投稿内容は編集できるようにテキストボックスとします。パスワードは空欄とし、タイトルと内容は投稿の内容を引っ張ってきて表示します。
- データベーステーブルは、personals と boards を作成して使用します。personals が 1 、 boards が n という 1:n の関係になります。よって、boards に personal_id カラムを用意し、紐付けます。
以上です。
ソースと気になった点
SQL。personals テーブルと、boards テーブルの定義と、それぞれのデータ
-- -- テーブルの構造 `boards` -- CREATE TABLE IF NOT EXISTS `boards` ( `id` int(11) NOT NULL AUTO_INCREMENT, `personal_id` int(11) NOT NULL, `title` varchar(255) NOT NULL, `content` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=13 ; INSERT INTO `boards` (`id`, `personal_id`, `title`, `content`) VALUES (1, 1, 'はっけよーい', 'のこった!★のこった!★'), (2, 1, '位置について、よーい', 'ドン!ドン!ドン!ドドンガドン! ドン!ドン!ドン!ドドンガドン! '), (3, 1, 'こんにちは!', 'こんにちは!こんにちは!'), (4, 1, 'おはよう!', 'おはようっ!ぐっとモーニング!'), (5, 1, 'はっけはっけ', 'のこった!のこった!のこった!のこった!'), (6, 2, 'かかかかkっか', 'カッカッカッカッカッ!'), (7, 2, '佐藤佐藤!!', '佐藤佐藤佐藤佐藤'), (8, 2, 'あまいよ', 'あっま~い!甘い、砂糖だよ♪'), (9, 3, 'しましま', 'しまうましまうまサファリアフリカ'), (10, 1, 'ある意味初めての投稿2', 'なんかいろいろ変だよこのサンプルソース一式!2'), (11, 2, '佐藤さんのアプリからの投稿', 'やってみたよ♪'), (12, 2, '佐藤さん2', '2回目だー!'); -- -------------------------------------------------------- -- -- テーブルの構造 `personals` -- CREATE TABLE IF NOT EXISTS `personals` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, `comment` varchar(255) DEFAULT NULL, `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ; INSERT INTO `personals` (`id`, `username`, `password`, `comment`, `created`, `modified`) VALUES (1, 'yamada', 'yamada', '山田', NULL, NULL), (2, 'sato', 'sato', '佐藤', NULL, NULL), (3, 'kato', 'kato', '加藤', NULL, NULL);
cake/sample/app/Model/Personal.php
<?php class Personal extends AppModel { public $name = 'Personal'; public $validate = array( 'username' => array( 'rule' => 'notEmpty', 'message' => '名前は必ず入力してください。', ), 'password' => array( 'rule' => 'notEmpty', 'message' => 'パスワードは必ず入力してください。', ), 'comment' => array(), ); public function checkNameAndPass($data) { $n = $this->find('count', array( 'conditions' => array( 'Personal.username' => $data['Personal']['username'], 'Personal.password' => $data['Personal']['password'] ) )); return $n > 0 ? true : false; } } ?>
cake/sample/app/Model/Board.php
<?php class Board extends AppModel { public $name = 'Board'; public $validate = array( 'title' => array( 'rule' => 'notEmpty', 'message' => 'タイトルは必ず入力してください' ), 'content' => array( 'rule' => 'notEmpty', 'message' => '内容は必ず入力してください' ), ); public $belongsTo = array( 'Personal' => array( 'className' => 'Personal', 'conditions' => '', 'order' => '', 'dependent' => false, 'foreignKey' => 'personal_id', ), ); } ?>
cake/sample/app/Controller/BoardsController.php
<?php class BoardsController extends AppController { // The name of this controller. Controller names are plural, named after the model they manipulate. public $name = 'Boards'; // An array containing the class names of models this controller uses. public $uses = array('Board', 'Personal'); /** * 初期表示 */ public function index() { // Board を取得。 $data = $this->Board->find('all', array('order' => 'Board.id desc')); // ビューで使えるように DB データをセット $this->set('data', $data); } public function add() { // このページに始めてきたときは何もしない。 if ($this->request->is('post')) { // 以下、送信ボタンを押したときの処理。 // 入力内容の保存の前に名前とパスワードのチェックを行う。 if (!$this->Personal->checkNameAndPass($this->data)) { $this->Personal->invalidate('username','名前またはパスワードを確認ください。'); $this->Personal->invalidate('password','名前またはパスワードを確認ください。'); } else { // ここから登録処理 // id を知りたいので、入力者 Personal 情報を取得 $res = $this->Personal->find('all',array( 'conditions' => array( 'Personal.username' => $this->data['Personal']['username'], 'Personal.password' => $this->data['Personal']['password'], ) )); // 画面で入力された情報のうち、Personal の username、password を除いた、 // Board の title、content を設定。 $record = $this->data['Board']; // 検索した Personal の id を追加。 $record['personal_id'] = $res[0]['Personal']['id']; $flg = $this->Board->save($record); if($flg){ $this->redirect('.'); } } } } public function show($param) { $data = $this->Board->find('all', array( 'conditions' => array( 'Board.id' => $param ) )); $this->set('data', $data); } public function show2($param) { $data = $this->Personal->find('all', array( 'conditions' => array( 'Personal.id' => $param ) )); $this->set('data', $data); } public function edit($param = NULL) { if (!empty($this->data)) { // 編集時の処理 $this->set('data',$this->data); if (!$this->Personal->checkNameAndPass($this->data)) { $this->Personal->invalidate('password','パスワードを確認ください。'); } else { $this->Board->save($this->data); $this->redirect('.'); } } else { // ページにはじめてきたときの表示 $this->Board->id = $param; $res = $this->Board->read(); $res['Personal']['password'] = null; $this->data = $res; $this->set('data',$res); } } } ?>
本では edit($param) だったのですが、編集して送信ボタンクリック後表示された時に Warning が出ました。この場合は edit アクションにパラメータを送らず引数がないため Warning となった、というのが原因でした。
そこで、 edit($param = NULL) と、引数ないときは null とするようにしたら、解決できました。
cake/sample/app/View/Boards/index.ctp
<h1>掲示板</h1> <br /><a href="/cake/sample/boards/add">※投稿する</a> <br /><br /> <table> <tr><th>投稿者</th><th>タイトル</th></tr> <?php for ($i = 0; $i < count($data); $i++) { $arr = $data[$i]; echo '<tr>'; echo "<td><a href='/cake/sample/boards/show2/{$arr['Personal']['id']}'> {$arr['Personal']['username']}</a></td>"; echo "<td><a href='/cake/sample/boards/show/{$arr['Board']['id']}'> {$arr['Board']['title']}</a></td>"; echo "</tr>"; } ?> </table>
HtmlHelper::link を使ってリンクを作成したほうがよさそうです。ヘルパーを使えば、シングルクォート「’」とダブルクォート「”」を使い分ける必要も多分なくなると思います。
cake/sample/app/View/Boards/add.ctp
<h1>投稿フォーム</h1> <br /><a href="/cake/sample/boards/index">※一覧に戻る</a> <br /><br /> <?php echo $this->Form->create(false,array('type'=>'post', 'action'=>'')); echo '名前:' . $this->Form->text('Personal.username'); echo $this->Form->error('Personal.username'); echo 'パスワード:' . $this->Form->password('Personal.password'); echo $this->Form->error('Personal.password'); echo 'タイトル:' . $this->Form->text('Board.title'); echo $this->Form->error('Board.title'); echo '内容:' . $this->Form->textarea('Board.content'); echo $this->Form->error('Board.content'); echo $this->Form->end("送信") ?> <br /><br />
FormHelper::create の使い方が変だと思いました。本のとおりにしたのですが、第一引数の false がわかりません。たぶん使い方を間違っています。
cake/sample/app/View/Boards/edit.ctp
<h1>編集フォーム</h1> <br /><a href="/cake/sample/boards/index">※一覧に戻る</a> <br /><br /> <?php echo $this->Form->create(false,array('type'=>'post', 'action'=>'')); echo $this->Form->hidden('Board.id'); echo $this->Form->hidden('Board.personal_id'); echo $this->Form->hidden('Personal.id'); echo $this->Form->hidden('Personal.username'); echo "名前:{$data['Personal']['username']}<br /><br />"; echo "パスワード:{$this->Form->password('Personal.password')}"; echo $this->Form->error('Personal.password'); echo "タイトル:{$this->Form->text('Board.title')}"; echo $this->Form->error('Board.title'); echo "内容:{$this->Form->textarea('Board.content')}"; echo $this->Form->error('Board.content'); echo $this->Form->end('送信') ?> <br />
できれば hidden をあまり使いたくないです。セキュリティ上あまりよろしくないと聞いたことがあります。session の勉強するときに、しっかり見極めたいです。
cake/sample/app/View/Boards/show.ctp
<h1>掲示板</h1> <br /><a href="/cake/sample/boards/index">※一覧に戻る</a> <br /><br /> <table> <?php echo "<tr><th>投稿者</th><td>{$data[0]['Personal']['username']}</td></tr>"; echo "<tr><th>タイトル</th><td>{$data[0]['Board']['title']}</td></tr>"; echo "<tr><th>内容</th><td>{$data[0]['Board']['content']}</td></tr>"; $id = $data[0]['Board']['id']; ?> </table> <br /><a href="/cake/sample/boards/edit/<?php echo $id; ?>"> ※この投稿を編集する</a><br />
次の二つのヘルパーで表を作ったほうがよいように思いました。
cake/sample/app/View/Boards/show2.ctp
<h1>掲示板</h1> <br /><a href="/cake/sample/boards/index">※一覧に戻る</a> <br /><br /> <table> <?php echo "<tr><th>投稿者</th><td>{$data[0]['Personal']['username']}</td></tr>"; echo "<tr><th>コメント</th><td>{$data[0]['Personal']['comment']}</td></tr>"; ?> </table> <br />
このビューも、本来であれば HTML ヘルパーを使ったほうがよいと思いました。