なんかできたよー。

Web系Tipsを適当につづるBlog.

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

f:id:tuki0918:20130507083936p:plain
# カテゴリの階層がわかる
# [↑][↓]クリックでカテゴリ内で並び替えが出来る
# カテゴリーの登録へ
# カテゴリーの編集へ

※ id = 1 の「カテゴリ」だけ編集出来ないようにしてる。 「categories/edit/1」ってURL打てば変更できるけど…

action: add or edit

f:id:tuki0918:20130507084826p:plain
# カテゴリの登録 or 変更ができる
# 親カテゴリの登録 or 変更ができる
# バリデーション チェック

※ カテゴリーの削除へ(editの場合のみ、右側にリンク表示)

action: delete

f:id:tuki0918:20130507085038p:plain
# カテゴリの削除ができる
# 一緒に削除される下位カテゴリーがわかる

準備: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ビューコーディング入門

WebデザイナーのためのCakePHPビューコーディング入門


# 誤字結構多くてスムーズに行かないけど、最初の本としては良かった。進めていけば概要がわかる感じ、あとヘルパーなど詳しく書いてる。


CakePHP2 実践入門 (WEB+DB PRESS plus)

CakePHP2 実践入門 (WEB+DB PRESS plus)


# CakePHPについて掘り下げて書いてくれてるので、二冊目としては良さそう。コントロラー、モデルで使うプロパティとかメソッドのチートシートがある。


即戦力になるための PHPシステム開発の教科書

即戦力になるための PHPシステム開発の教科書


# 応用みたいな感じで顧客管理のアプリケーション作る。結構早い段階で読んじゃったので正直頭に入ってない…。