CakePHP / Tree Behaviorを使ってシンプルなカテゴリー管理画面(CRUD)を作る
さいしょに
HTMLの文法適当、CSSも直入れなため動けばいい人向けです。
参考
TreeBehaviorの使い方は、以下を参考にしています。
ページ | 言語 | バージョン |
---|---|---|
ツリー — CakePHP Cookbook v1.3 documentation | 日本語 | 古い |
Tree — CakePHP Cookbook v2.x documentation | 英語 | 主に参考 |
Tutoriel CakePHP : Tree Behaviour - YouTube | ? | 古い |
完成図
action: index
# カテゴリの階層がわかる
# [↑][↓]クリックでカテゴリ内で並び替えが出来る
# カテゴリーの登録へ
# カテゴリーの編集へ
※ id = 1 の「カテゴリ」だけ編集出来ないようにしてる。 「categories/edit/1」ってURL打てば変更できるけど…
action: add or edit
# カテゴリの登録 or 変更ができる
# 親カテゴリの登録 or 変更ができる
# バリデーション チェック
※ カテゴリーの削除へ(editの場合のみ、右側にリンク表示)
action: delete
# カテゴリの削除ができる
# 一緒に削除される下位カテゴリーがわかる
準備:DBテーブルを登録
CREATE TABLE IF NOT EXISTS `categories` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `parent_id` int(11) DEFAULT NULL, `lft` int(11) DEFAULT NULL, `rght` int(11) DEFAULT NULL, `name` varchar(30) NOT NULL, PRIMARY KEY (`id`), KEY `parent_id` (`parent_id`), KEY `lft` (`lft`), KEY `rght` (`rght`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=16 ; INSERT INTO `categories` (`id`, `parent_id`, `lft`, `rght`, `name`) VALUES (1, NULL, 1, 30, 'カテゴリ'), (2, 1, 2, 15, '楽しみ'), (3, 2, 3, 8, 'スポーツ'), (4, 3, 4, 5, 'サーフィン'), (5, 3, 6, 7, 'エクストリーム編み物'), (6, 2, 9, 14, '友達'), (7, 6, 10, 11, 'ジェラルド'), (8, 6, 12, 13, 'グウェンドリン'), (9, 1, 16, 29, '仕事'), (10, 9, 17, 22, '報告'), (11, 10, 18, 19, '通年'), (12, 10, 20, 21, '状態'), (13, 9, 23, 28, '出張'), (14, 13, 24, 25, '国内'), (15, 13, 26, 27, '国外');
準備:コード
# app/Controller/AppController.php
class AppController extends Controller { public $components = array( 'Session', 'DebugKit.Toolbar' ); }
# app/Model/Category.php
<?php class Category extends AppModel { public $actsAs = array('Tree'); public $validate = array( 'name' => array( array( 'rule' => 'notEmpty', 'message' => 'カテゴリー名を入力して下さい。', ), array( 'rule' => array('maxLength', 30), 'message' => 'カテゴリー名を30文字以内で入力して下さい。', ), ), ); }
# app/Controller/CategoriesController.php
<?php class CategoriesController extends AppController { public function index() { $categoryList = $this->Category->generateTreeList(null, null, null, '----> '); /* debug($categoryList); */ $this->set(compact('categoryList')); } public function add() { return $this->edit(); } public function delete($id) { if($this->request->isDelete()) { if ($this->Category->delete($id)) { $this->Session->setFlash('カテゴリーを削除しました'); } else { $this->Session->setFlash('カテゴリーの削除に失敗しました'); } $this->redirect(array('action'=> 'index')); } $this->request->data = $this->Category->findById($id); if (empty($this->request->data)) { $this->Session->setFlash('カテゴリーが見つかりませんでした'); $this->redirect(array('action'=> 'index')); } $deleteCategoryList = $this->Category->children($id); $this->set(compact('deleteCategoryList')); } public function edit($id = null) { if ($this->request->isPost() || $this->request->isPut()) { if ($this->Category->save($this->request->data)) { $this->Session->setFlash('カテゴリーを保存しました'); $this->redirect(array('action' => 'index')); } else { $this->Session->setFlash('入力に間違いがあります'); } } else { if ($id != null) { $this->request->data = $this->Category->findById($id); if (empty($this->request->data)) { $this->Session->setFlash('カテゴリーが見つかりませんでした'); $this->redirect(array('action'=> 'index')); } } } $categoryList = $this->Category->generateTreeList(null, null, null, '----'); $this->set(compact('categoryList')); $this->render('edit'); } public function movedown($id = null, $delta = null) { $this->Category->id = $id; if (!$this->Category->exists()) { throw new NotFoundException(__('Invalid category')); } if ($delta > 0) { $this->Category->moveDown($this->Category->id, abs($delta)); } else { $this->Session->setFlash('入力に間違いがあります。'); } $this->redirect(array('action' => 'index'), null, true); } public function moveup($id = null, $delta = null) { $this->Category->id = $id; if (!$this->Category->exists()) { throw new NotFoundException(__('Invalid category')); } if ($delta > 0) { $this->Category->moveUp($this->Category->id, abs($delta)); } else { $this->Session->setFlash('入力に間違いがあります。'); } $this->redirect(array('action' => 'index'), null, true); } }
# app/View/Categories/index.ctp
<div> <h2>メニュー</h2> <ul> <li><?php echo $this->Html->link('カテゴリーを登録する', array('action' => 'add')); ?></li> </ul> </div> <div style="margin-top: 30px;"> <h2>カテゴリー編集 / 一覧</h2> <ul> <?php foreach($categoryList as $id => $name): ?> <li> <?php if ($id == 1): ?> <?php echo h($name); ?> <?php else: ?> <?php echo $this->Html->link($name, array('action' => 'edit', $id)); ?> <?php echo $this->Html->link('[↑]', array('action' => 'moveup', $id, 1)); ?> <?php echo $this->Html->link('[↓]', array('action' => 'movedown', $id, 1)); ?> <?php endif; ?> </li> <?php endforeach; ?> </ul> </div>
# app/View/Categories/edit.ctp
<?php $errorMessages = $this->validationErrors['Category']; ?> <div> <ul> <?php foreach($errorMessages as $messages): ?> <?php foreach($messages as $message): ?> <li> <?php echo h($message); ?> </li> <?php endforeach; ?> <?php endforeach; ?> </ul> </div> <div> <h2>カテゴリー<?php echo empty($this->data['Category']['id']) ? '追加' : '編集'; ?></h2> </div> <div> <?php echo $this->Form->create('Category'); ?> <?php echo empty($this->data['Category']['id']) ? null : $this->Form->input('id', array('type' => 'hidden')); ?> <h3>カテゴリー名</h3> <p><?php echo $this->Form->inout('name', array('placeholder' => 'カテゴリー名を入力して下さい。')); ?></p> <h3>親カテゴリー</h3> <p><?php echo $this->Form->select('parent_id', $categoryList, array('empty' => '----')); ?></p> <?php echo $this->Form->end(empty($this->data['Category']['id']) ? ' 追加 ' : ' 編集 '); ?> </div> <div style="text-align: right;"> <?php echo empty($this->data['Category']['id']) ? null : $this->Html->link('削除する', array('action' => 'delete', $this->data['Category']['id'])); ?> </div> <div class="pageLinks"> <p><?php echo $this->Html->link('戻る', array('action' => 'index')); ?></p> </div>
# app/View/Categories/delete.ctp
<div> <h2>カテゴリー削除</h2> </div> <div> <?php echo $this->Form->create('Category', array('type' => 'delete')); ?> <?php echo $this->Form->input('id', array('type' => 'hidden')); ?> <h3>カテゴリー名</h3> <p><?php echo h($this->data['Category']['name']); ?> <h3>※削除される下位カテゴリー</h3> <ul> <?php if (count($deleteCategoryList) <= 0): ?> <li>なし</li> <?php else: ?> <?php foreach($deleteCategoryList as $deleteCategory): ?> <li> <?php echo h($deleteCategory['Category']['name']); ?> </li> <?php endforeach; ?> <?php endif; ?> </ul> <p>よろしければ「削除」ボタンを押してください</p> <?php echo $this->Form->end(' 削除 '); ?> </div> <div class="pageLinks"> <p><?php echo $this->Html->link('戻る', array('action' => 'edit', $this->data['Category']['id'])); ?></p> </div>
さいごに
おそらくコピペでキャプチャと同じような表示になると思います。
解説無しなので(いらないかもしれませんが…)、何かあればコメントにでも書いて頂ければと思います。
最近読んだ本
WebデザイナーのためのCakePHPビューコーディング入門
- 作者: 滝下真玄,原一浩
- 出版社/メーカー: 秀和システム
- 発売日: 2012/03/28
- メディア: 単行本
- 購入: 1人 クリック: 19回
- この商品を含むブログ (1件) を見る
# 誤字結構多くてスムーズに行かないけど、最初の本としては良かった。進めていけば概要がわかる感じ、あとヘルパーなど詳しく書いてる。
CakePHP2 実践入門 (WEB+DB PRESS plus)
- 作者: 安藤祐介,岸田健一郎,新原雅司,市川快,渡辺一宏,鈴木則夫
- 出版社/メーカー: 技術評論社
- 発売日: 2012/09/29
- メディア: 単行本(ソフトカバー)
- 購入: 5人 クリック: 165回
- この商品を含むブログ (7件) を見る
# CakePHPについて掘り下げて書いてくれてるので、二冊目としては良さそう。コントロラー、モデルで使うプロパティとかメソッドのチートシートがある。
- 作者: マッキーソフト株式会社,鶴田展之
- 出版社/メーカー: 技術評論社
- 発売日: 2012/09/26
- メディア: 大型本
- 購入: 1人 クリック: 3回
- この商品を含むブログ (1件) を見る
# 応用みたいな感じで顧客管理のアプリケーション作る。結構早い段階で読んじゃったので正直頭に入ってない…。