CakePHPを使ってサービスを作ろうと思いアイデアはあるしCakePHPも日常で使っているのだけれど、作るなら基本設計がしっかりしたきれいな構成で作りたかったので、今回もう一度基礎の再確認の意味でまたまたドットインストールさんのお世話になりました。動画内で説明されていた内容についてあとで振り返れるようにメモ。phpとmysqlの知識が必要です。過去の記事でも紹介しているので見てみてください。
:: ドットインストールのPHPレッスン基本編のメモ書き
:: ドットインストールのPHPレッスン応用編のメモ書き
:: phpmyadminを使ったMYSQLのSQL構文の入門編メモ書き 公式サイト APIリファレンス
※CakePHPのバージョンは2.6.4を使うため、ドットインストールさんのバージョンとは異なります。
※Modelは単数形、ControllerとViewは複数形(覚書)
PS. ドットインストールさんいつもありがとうございます!!
目次
必要な用語
MVC
Model – データ
View – 見た目
Controller – かけはし
CoC
Convention over Configuration(設定より規約)
ディレクトリ構造
- Config – アプリケーションの設定
- Console
- Controller – データの架け橋
- Lib
- Locale
- Model – データベースへのアクセス
- Plugin – debug kitとか
- Test
- tmp - 一時ファイルを保管
- Vender
- View – 見た目のファイルを保管 .ctp
- webroot – css, Javascript、jpg、gifなど
- index.php
※tmpフォルダは初期設定として書き込み権限を変える必要がある。
データベースの設定
MACのMAMP環境で、ターミナル経由でmysqlでコマンドを実行する。 ターミナルの実行
1 |
$ cd /Applications/MAMP/Library/bin/ |
rootでログイン
1 |
$ ./mysql -u root -p |
パスワードを入力(MAMPのrootパスワード)後、接続完了。
※表示が「mysql>」になっていればOK。
データベースを作成。
1 2 3 4 5 |
mysql> create database cake_blog -> ; Query OK, 1 row affected (0.00 sec) mysql> |
ユーザーの設定をする (以下はrootユーザー)
1 2 3 4 5 |
mysql> grant all on cake_blog.* to root@localhost identified by '********' -> ; Query OK, 0 rows affected (0.00 sec) mysql> |
********は任意のパスワード
データベースの設定ファイルを変更
/app/Config/database.phpを開く
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class DATABASE_CONFIG { public $default = array( 'datasource' => 'Database/Mysql', 'persistent' => false, 'host' => 'localhost', 'login' => '********', 'password' => '********', 'database' => 'cake_blog', 'prefix' => '', //'encoding' => 'utf8', ); } |
********箇所は先ほど設定したログインユーザー名とパスワードを入力する
アプリケーションの開発範囲
ブログアプリを作るための機能要件、作成ファイル
開発内容
記事
– 一覧を表示
– 個別記事を表示
– 追加
– 編集
– 削除
具体的な開発ファイル
Model(Model/Post.php)
— mysql table
Controller(Controller/PostsController.php) ※複数形 Posts
– index – /cake_blog/
– view – /cake_blog/posts/view/(id)
– add
– edit
– delete
View(View/Posts/) ※複数形 Posts
– index.ctp
– view.ctp
– add.ctp
– edit.ctp
postsテーブルの作成
ターミナルで使うデータベースを設定
1 2 3 |
mysql> use cake_blog Database changed mysql> |
テーブルの作成、テストデータの挿入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
mysql> create table posts ( -> id int not null auto_increment primary key, -> title varchar(50), -> body text, -> created datetime default null, -> modified datetime default null -> ); Query OK, 0 rows affected (0.28 sec) mysql> mysql> insert into posts (title, body, created, modified) values -> ('title 1', 'body 1', now(), now()), -> ('title 2', 'body 2', now(), now()), -> ('title 3', 'body 3', now(), now()); Query OK, 3 rows affected (0.06 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> |
テーブル、テストデータの確認
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
mysql> desc posts; +----------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | title | varchar(50) | YES | | NULL | | | body | text | YES | | NULL | | | created | datetime | YES | | NULL | | | modified | datetime | YES | | NULL | | +----------+-------------+------+-----+---------+----------------+ 5 rows in set (0.01 sec) mysql> select * from posts; +----+---------+--------+---------------------+---------------------+ | id | title | body | created | modified | +----+---------+--------+---------------------+---------------------+ | 1 | title 1 | body 1 | 2015-05-10 16:26:12 | 2015-05-10 16:26:12 | | 2 | title 2 | body 2 | 2015-05-10 16:26:12 | 2015-05-10 16:26:12 | | 3 | title 3 | body 3 | 2015-05-10 16:26:12 | 2015-05-10 16:26:12 | +----+---------+--------+---------------------+---------------------+ 3 rows in set (0.00 sec) mysql> |
PostのModelを作る
Modelフォルダに「Post.php」を作る
1 2 3 4 5 |
<?php class Post extends AppModel { } |
Controllerフォルダに「PostsController.php」を作る
1 2 3 4 5 |
<?php class PostsController extends AppController { } |
Scaffoldを使う
CakePHPであらかじめ用意されている、Controllerに1行追加するだけで超簡易版の管理画面できる機能。(初めて見た時は本当に感動しました。)
1 2 3 4 5 6 |
<?php class PostsController extends AppController { public $scaffold; } |
アクセスすると管理画面が出来ている。
記事の一覧を表示する
PostsContollerにindexメソッドを追加
1 2 3 4 5 6 7 8 9 10 11 |
<?php class PostsController extends AppController { // public $scaffold; public $helper = array('HTML', 'Form'); public function index() { $this->set('posts', $this->Post->find('all')); //$this->setでPosts変数へ代入。 $this->Post->fin('all')で記事を全て持ってくる。 } } |
View直下に、Postsフォルダを作成。その中にindex.ctpファイルを作成
1 2 3 4 5 6 7 8 9 10 11 |
<h2>記事一覧</h2> <ul> <?php foreach ($posts as $post) : ?> <li> <?php debug($post); ?> </li> <?php endforeach; ?> </ul> |
※debugで$postにセットされている値を表示。
$Postに渡されている配列の中身が分かる
$postのtitleを表示させる
1 2 3 4 5 6 7 8 9 10 11 12 |
<h2>記事一覧</h2> <ul> <?php foreach ($posts as $post) : ?> <li> <?php // debug($post); echo h($post['Post']['title']); //hとはCakePHPが持っているHTML Special charactoresの省略形 ?> </li> <?php endforeach; ?> </ul> |
以下のようにタイトルだけ表示される
記事の一覧ページをトップ(cake_blog)でアクセスする設定
/app/Config/routes.phpを以下に変更する
1 |
Router::connect('/', array('controller' => 'posts', 'action' => 'index')); |
ちなみに、headerとfooterのパーツ箇所はView/Layouts内で指定している
タイトルを変更する
/View/Layouts/default.ctpを変更
1 2 3 |
<title> <?php echo $title_for_layout; ?> </title> |
/Controller/PostsController.phpに追加
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php class PostsController extends AppController { // public $scaffold; public $helper = array('HTML', 'Form'); public function index() { $this->set('posts', $this->Post->find('all')); //$this->setでPosts変数へ代入。 $this->Post->fin('all')で記事を全て持ってくる。 $this->set('title_for_layout', '記事一覧'); //タイトルをセット } } |
find()を使いこなす
例えば、表示件数を2件にして並び順をmodifiedの降順にしてみる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php class PostsController extends AppController { // public $scaffold; public $helper = array('HTML', 'Form'); public function index() { $param = array ( 'order' => 'modified desc', 'limit' => 2 ); $this->set('posts', $this->Post->find('all', $param)); //$this->setでPosts変数へ代入。 $this->Post->fin('all')で記事を全て持ってくる。 $this->set('title_for_layout', '記事一覧'); //タイトルをセット } } |
その他にも公式サイトのDocumentation内で命令の詳細を確認することができる
個別記事の詳細を表示する
/Controller/PostsController.phpにviewを追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php class PostsController extends AppController { // public $scaffold; public $helper = array('HTML', 'Form'); public function index() { $this->set('posts', $this->Post->find('all')); //$this->setでPosts変数へ代入。 $this->Post->fin('all')で記事を全て持ってくる。 $this->set('title_for_layout', '記事一覧'); //タイトルをセット } public function view($id = null) { $this->Post->id = $id; $this->set('post', $this->Post->read()); } } |
/View/Posts/view.ctpを新しく作成
1 2 |
<h2><?php echo h($post['Post']['title']); ?></h2> <p><?php echo h($post['Post']['body']); ?></p> |
/View/Posts/index.ctpにリンクを追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<h2>記事一覧</h2> <ul> <?php foreach ($posts as $post) : ?> <li> <?php // debug($post); // echo h($post['Post']['title']); //hとはCakePHPが持っているHTML Special charactoresの省略形 // echo $this->Html->Link($post['Post']['title'], '/posts/view/' .$post['Post']['id']); ?> </li> <?php endforeach; ?> </ul> |
/View/Layouts/default.ctp内でheaderにホームへ戻るボタンを追加
1 2 3 |
<div id="header"> <h1><?php echo $this->Html->link('Home', '/'); ?></h1> </div> |
記事を追加する
Controller/PostsControllerにaddを追加
1 2 3 4 5 6 7 8 9 10 |
public function add() { if ($this->request->is('post')) { if ($this->Post->save($this->request->data)) { $this->Session->setFlash('Success!'); $this->redirect(array('action' => 'index')); } else { $this->Session->setFlash('failed'); } } } |
VIew/Posts/add.ctpを作成
1 2 3 4 5 6 7 |
<h2>Add post</h2> <?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'); |
View/Posts/index.ctpにAdd Postのリンクを追加
1 2 |
<h2>Add Post</h2> <?php echo $this->Html->link('Add post', array('controller' => 'posts', 'action' => 'add')); |
記事一覧に記事追加リンクを追加
記事追加画面
記事投稿のエラーチェック
Model/Post.phpで値が空かどうかをチェックする
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php class Post extends AppModel { public $validate = array ( 'title' => array ( 'rule' => 'notEmpty', 'message' => '記入してください。' // 表示するメッセージを指定 ), 'body' => array ( 'rule' => 'notEmpty' ) ); } |
jQueryを読み込む
/View/Layouts/default.ctpを変更
1 |
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script> |
</body>直前にfadeOutの関数を記述
1 2 3 4 5 6 7 |
<script> $(function() { setTimeout(function() { $('#flashMessage').fadeOut("slow"); }, 800); }); </script> |
これで、メッセージが自動で消えるようになる。
記事の編集をする
/Controller/PostsController.phpに追加
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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('success!'); $this->redirect(array('action' => 'index')); } else { $this->Session->setSFlash('failed'); } } } |
View/Posts/edit.ctpを新たに作成
1 2 3 4 5 6 7 |
<h2>Edit Post</h2> <?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->end('Save!'); |
View/Posts/index.ctpのpostタイトルの横に表示されるように追加
1 |
<?php echo $this->Html->link('編集', array('action'=>'edit', $post['Post']['id'])); ?> |
編集ボタンが追加されて記事の編集が可能になった。
記事を削除する
/Controller/PostsController.phpに追加
1 2 3 4 5 6 7 8 9 |
public function delete($id) { if ($this->request->is('get')) { throw new MethodNotAllowedException(); } if ($this->Post->delete($id)) { $this->Session->setFlash('Deleted!'); $this->redirect(array('action'=>'index')); } } |
View/Posts/index.ctpのpostタイトルの横に表示されるように追加
1 |
<?php echo $this->Form->postLink('削除', array('action'=>'delete', $post['Post']['id']), array('confirm'=>'sure?')); ? |
削除処理をAjax化する
先ほど書いた/Controller/PostsController.phpを修正
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// public function delete($id) { // if ($this->request->is('get')) { // throw new MethodNotAllowedException(); // } // if ($this->Post->delete($id)) { // $this->Session->setFlash('Deleted!'); // $this->redirect(array('action'=>'index')); // } // } public function delete($id) { if ($this->request->is('get')) { throw new MethodNotAllowedException(); } if ($this->request->is('ajax')) { if ($this->Post->delete($id)) { $this->autoRender = false; $this->autoLayout = false; $response = array('id' => $id); $this->header('Content-Type: application/json'); echo json_encode($response); exit(); } } $this->redirect(array('action'=>'index')); } |
View/Posts/index.ctpの削除コード箇所を変更
1 2 |
<!--?php echo $this->Form->postLink('削除', array('action'=>'delete', $post['Post']['id']), array('confirm'=>'sure?')); ?--> <?php echo $this->Html->link('削除', '#', array('class'=>'delete', 'data-posst-id'=>$post['Post']['id'])); ?> |
View/Posts/index.ctpの一番下にjsonのファンクションコードを追加
1 2 3 4 5 6 7 8 9 10 11 12 |
<script> $(function() { $('a.delete').click(function(e) { if (confirm('sure?')) { $.post('/cake_blog/posts/delete'+$(this).data('post-id'), {}, function(res)) { $('#post_'+res.id).fadeOut(); }, "json"); } return false; }); }); </script> |
View/Posts/index.ctpの<li>にidを付ける
1 2 |
<!-- <li> --> <li id="post_<?php echo h($post['Post']['id']); ?>"> |
コメント機能を実装
コメント – 一覧を表示 – 追加 – 削除 Model – mysql table – association 記事に対して複数コメントを持つ関係性を定義 Controller index add delete テーブルを作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
mysql> create table comments ( -> id int not null auto_increment primary key, -> commenter varchar(255), -> body text, -> post_id int not null, -> created datetime default null, -> modified datetime default null -> ); Query OK, 0 rows affected (0.13 sec) mysql> mysql> insert into comments (commenter, body, post_id, created, modified) values -> ('c1', 'b1', 9, now(), now()), -> ('c2', 'b2', 9, now(), now()), -> ('c3', 'b3', 9, now(), now()); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
mysql> show tables; +---------------------+ | Tables_in_cake_blog | +---------------------+ | comments | | posts | +---------------------+ 2 rows in set (0.00 sec) mysql> desc comments; +-----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | commenter | varchar(255) | YES | | NULL | | | body | text | YES | | NULL | | | post_id | int(11) | NO | | NULL | | | created | datetime | YES | | NULL | | | modified | datetime | YES | | NULL | | +-----------+--------------+------+-----+---------+----------------+ 6 rows in set (0.01 sec) mysql> select * from comments; +----+-----------+------+---------+---------------------+---------------------+ | id | commenter | body | post_id | created | modified | +----+-----------+------+---------+---------------------+---------------------+ | 1 | c1 | b1 | 9 | 2015-05-10 23:42:14 | 2015-05-10 23:42:14 | | 2 | c2 | b2 | 9 | 2015-05-10 23:42:14 | 2015-05-10 23:42:14 | | 3 | c3 | b3 | 9 | 2015-05-10 23:42:14 | 2015-05-10 23:42:14 | +----+-----------+------+---------+---------------------+---------------------+ 3 rows in set (0.00 sec) mysql> |
アソシエーションとは Modelに$BelongsTo = “Post”;と、public $hasMany = “Comment”;とすることそれぞれが紐づく。
1 2 3 4 5 |
<?php class Comment extends AppModel { public $belongsTo = 'Post'; //全てのコメントはPostに帰属している。PostIdがあれば自動で紐づく } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php class Post extends AppModel { public $hasMany = "Comment"; //Commentと紐づく public $validate = array ( 'title' => array ( 'rule' => 'notEmpty', 'message' => '記入してください。' // 表示するメッセージを指定 ), 'body' => array ( 'rule' => 'notEmpty' ) ); } |
コメント追加・削除機能を実装
コメントの追加 コメント用のコントローラーを追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php class CommentsController extends AppController { // public $scaffold; public $helper = array('HTML', 'Form'); public function add() { if ($this->request->is('post')) { if ($this->Comment->save($this->request->data)) { $this->Session->setFlash('Success!'); $this->redirect(array('controller' => 'posts', 'action' => 'view', $this->data['Comment']['post_id'])); } else { $this->Session->setFlash('failed'); } } } } |
以下のコメント投稿パーツを追加
1 2 3 4 5 6 7 |
<h2>Add Comments</h2> <?php echo $this->Form->create('Comment', array('action'=>'add')); echo $this->Form->input('Commenter'); echo $this->Form->input('body', array('row'=>3)); echo $this->Form->input('Comment.post_id', array('type'=>'hidden', 'value'=>$post['Post']['id'])); echo $this->Form->end('post comment'); |
コメント枠が完成 コメントの削除 コントローラーで削除機能を追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public function delete($id) { if ($this->request->is('get')) { throw new MethodNotAllowedException(); } if ($this->request->is('ajax')) { if ($this->Comment->delete($id)) { $this->autoRender = false; $this->autoLayout = false; $response = array('id' => $id); $this->header('Content-Type: application/json'); echo json_encode($response); exit(); } } $this->redirect(array('controller'=>'posts', 'action'=>'index')); } |
ビューで削除パーツを追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<h2><?php echo h($post['Post']['title']); ?></h2> <p><?php echo h($post['Post']['body']); ?></p> <h2>Comments</h2> <ul> <?php foreach ($post['Comment'] as $comment): ?> <li id="comment_<?php echo h($comment['id']); ?>"> <?php echo h($comment['body']) ?> by <?php echo h($comment['commenter']); ?> <?php echo $this->Html->link('削除', '#', array('class'=>'delete', 'data-comment-id'=>$comment['Post']['id'])); ?> </li> <?php endforeach; ?> </ul> <h2>Add Comments</h2> <?php echo $this->Form->create('Comment', array('action'=>'add')); echo $this->Form->input('Commenter'); echo $this->Form->input('body', array('row'=>3)); echo $this->Form->input('Comment.post_id', array('type'=>'hidden', 'value'=>$post['Post']['id'])); echo $this->Form->end('post comment'); ?> <script> $(function() { $('a.delete').click(function(e) { if (confirm('sure?')) { $.post('/cake_blog/comments/delete'+$(this).data('comment-id'), {}, function(res)) { $('#comment_'+res.id).fadeOut(); }, "json"); } return false; }); }); </script> |
以上になります。反省も兼ねて見てきましたが、知らないこともあるしもう一度しっかりと基礎を叩き込んでまずは1つのサービスを作ってみようかなと思います。
LEAVE A REPLY