なんかできたよー。

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

CakePHP / カテゴリーデータとショップデータを紐づけて管理してみる(HABTM)

さいしょに

TreeBehaviorを使ってシンプルなカテゴリー管理画面(CRUD)を作る にショップデータを紐づけて、管理を出来るようにしました。

完成図

action: index

f:id:tuki0918:20130507180913p:plain
# 店舗情報がわかる
# 所属カテゴリがわかる
# ソート機能
# 店舗の登録へ
# 店舗の編集へ

action: add or edit

f:id:tuki0918:20130507181358p:plain
# 店舗の登録 or 変更ができる
# カテゴリの登録 or 変更ができる(1〜2つ)
※カテゴリ数増えたら流すぎてやばそう…

# バリデーション チェック

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

action: delete

f:id:tuki0918:20130507193638p:plain
# 店舗の削除ができる
# 紐付いてるカテゴリーも削除する

準備:DBテーブルを登録

CREATE TABLE IF NOT EXISTS `categories_shops` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `shop_id` int(8) unsigned zerofill NOT NULL,
  `category_id` int(11) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  KEY `shop_id` (`shop_id`),
  KEY `category_id` (`category_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=15 ;

INSERT INTO `categories_shops` (`id`, `shop_id`, `category_id`) VALUES
(1, 00000001, 3),
(2, 00000002, 4),
(3, 00000002, 5),
(4, 00000003, 7),
(5, 00000003, 11),
(6, 00000004, 15),
(12, 00000005, 1),
(13, 00000005, 3),
(14, 00000006, 5);


CREATE TABLE IF NOT EXISTS `shops` (
  `id` int(8) unsigned zerofill NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `kana` varchar(255) DEFAULT NULL,
  `priority` int(5) NOT NULL DEFAULT '99999',
  `display` int(1) NOT NULL DEFAULT '0',
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `priority` (`priority`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=9 ;

INSERT INTO `shops` (`id`, `name`, `kana`, `priority`, `display`, `created`, `modified`) VALUES
(00000001, '焼肉きんきん', 'ヤキニクキンキン', 99999, 0, '2013-05-07 17:14:30', '2013-05-07 17:14:30'),
(00000002, '京京亭', 'キョウキョウテイ', 99999, 0, '2013-05-07 17:16:04', '2013-05-07 17:16:04'),
(00000003, 'くら川', 'クラヤマ', 99999, 0, '2013-05-07 17:16:36', '2013-05-07 17:16:36'),
(00000004, 'カラテの空', 'カラテノソラ', 99999, 0, '2013-05-07 17:17:20', '2013-05-07 17:17:20'),
(00000005, 'とみ次郎', 'トミジロウ', 99999, 0, '2013-05-07 17:20:18', '2013-05-07 17:43:37'),
(00000006, 'いかつぼ', 'イカツボ', 99999, 0, '2013-05-07 17:26:17', '2013-05-07 17:44:23');

 

準備:コード

# app/Model/Category.php

<?php
class Category extends AppModel {

	public $displayField = 'name';

	public $actsAs = array('Tree');

	public $validate = array(
		'name' => array(
			array(
				'rule' => 'notEmpty',
				'message' => 'カテゴリー名を入力して下さい', 
			),
			array(
				'rule' => array('maxLength', 30), 
				'message' => 'カテゴリー名を30文字以内で入力して下さい。', 
			), 
		),
	);
}


# app/Controller/ShopsController.php

<?php
class ShopsController extends AppController {

	public $uses = array('Shop');

	public $paginate = array(
		'Shop' => array(
			'order' => array(
				'modified' => 'DESC',
			),
			'limit' => 30,
		),
	);


	public function beforeRender() {
		$this->set('categoryList', $this->Shop->Category->find('list',
			array('order' => array('Category.lft ASC'))));
	}


	public function index(){

		$shopList = $this->paginate();		
		$this->set(compact('shopList'));

	}


	public function delete($id) {

		if($this->request->isDelete()) {
			if ($this->Shop->delete($id)) {
				$this->Session->setFlash('店舗を削除しました');
			} else {
				$this->Session->setFlash('店舗の削除に失敗しました');
			}
			$this->redirect(array('action'=> 'index'));
		}

		$this->request->data = $this->Shop->findById($id);
		if (empty($this->request->data)) {
			$this->Session->setFlash('店舗が見つかりませんでした');
			$this->redirect(array('action'=> 'index'));
		}

	}


	public function add() {

		return $this->edit();

	}


	public function edit($id = null) {

		if($this->request->isPost() || $this->request->isPut()) {
			if($this->Shop->save($this->request->data)) {
				$this->Session->setFlash('店舗情報を保存しました');
				$this->redirect(array('action' => 'index'));
			} else {
				$this->Session->setFlash('入力に間違いがあります');
			}
		} else {
			if($id !== null) {
				$this->request->data = $this->Shop->findById($id);
				if(empty($this->request->data)) {
					$this->Session->setFlash('店舗が見つかりませんでした');
					$this->redirect(array('action'=> 'index'));
				}
			}
		}

		$this->render('edit');

	}

}


# app/Model/Shop.php

<?php
class Shop extends AppModel {

	public $displayField = 'name';
	
	public $hasAndBelongsToMany = array(
		'Category',
	);

	public $validate = array(
		'name' => array(
			array(
				'rule' => 'notEmpty',
				'message' => '店舗名を入力して下さい', 
			),
			array(
				'rule' => array('maxLength', 255), 
				'message' => '店舗名を255文字以内で入力して下さい。', 
			), 
		),
		'kana' => array(
			array(
				'rule' => array('maxLength', 255), 
				'message' => 'フリガナを255文字以内で入力して下さい。', 
			), 
		),
		'priority' => array(
			array(
				'rule' => array('range', -1, 100000), 
				'message' => '0 〜 99999 の数値で入力して下さい。', 
			), 
		),
	);


	public function afterValidate() {

		// カテゴリーを1〜2つ選択して下さい。
		if (empty($this->data['Category']['Category']) || count($this->data['Category']['Category']) > 2) {
			$this->validationErrors['Shop'][] = 'カテゴリーを1〜2つ選択して下さい。';
		}

	}

}


# app/View/Shops/index.ctp

<div>
	<h2>メニュー</h2>
	<ul>
		<li><?php echo $this->Html->link('店舗登録', array('action' => 'add')); ?></li>
	</ul>
</div>


<div>
	<h2>店舗一覧</h2>
	<table>
		<tr>
			<th><?php echo $this->Paginator->sort('id', 'ID'); ?></th>
			<th><?php echo $this->Paginator->sort('name', '店舗名'); ?></th>
			<th><?php echo $this->Paginator->sort('Category.name', 'カテゴリー名'); ?></th>
			<th><?php echo $this->Paginator->sort('modified', '最終更新時間'); ?></th>
			<th>操作</th>
		</tr>

		<?php foreach($shopList as $shop): ?>

		<tr>
			<td><?php echo h($shop['Shop']['id']); ?></td>
			<td><?php echo h($shop['Shop']['name']); ?></td>

			<td>
			<?php foreach($shop['Category'] as $category): ?><?php echo $category['name']; ?><?php endforeach; ?>
			</td>

			<td><?php echo h($shop['Shop']['modified']); ?></td>

			<td>
				<?php echo $this->Html->link('編集', array('action' => 'edit', $shop['Shop']['id'])); ?>
			</td>
		</tr>

		<?php endforeach; ?>

	</table>
</div>


<div class="paginateLinks">
<?php echo $this->Paginator->prev(); ?>&nbsp;
<?php echo $this->Paginator->numbers(); ?>&nbsp;
<?php echo $this->Paginator->next(); ?>
</div>


# app/View/Shops/edit.ctp

<?php $errorMessages = $this->validationErrors['Shop']; ?>
<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['Shop']['id']) ? '追加' : '編集'; ?></h2>
</div>

<div>
	<?php echo $this->Form->create('Shop'); ?>
	<?php echo empty($this->data['Shop']['id']) ? null : $this->Form->input('id', array('type' => 'hidden')); ?>

	<h3>店舗ID</h3>
	<p><?php echo empty($this->data['Shop']['id']) ? '新規登録' : h($this->data['Shop']['id']) ?></p>

	<h3>店舗名</h3>
	<p><?php echo $this->Form->inout('name', array('placeholder' => '店舗名を入力して下さい。')); ?></p>

	<h3>店舗名(フリガナ)</h3>
	<p><?php echo $this->Form->inout('kana', array('placeholder' => 'フリガナを入力して下さい。')); ?></p>

	<h3>優先順位(0 〜 99999 )<span> ※ 0 に近いほど上位に表示</span></h3>
	<p><?php echo $this->Form->inout('priority', array('default' => 99999)); ?></p>

	<h3>カテゴリーの選択(1〜2つ)</h3>
	<p><?php echo $this->Form->input('Category.Category', array('label' => false, 'multiple' => 'checkbox', 'options' => $categoryList)); ?></p>


	<?php echo $this->Form->end(empty($this->data['Shop']['id']) ? ' 追加 ' : ' 編集'); ?>
</div>


<div style="text-align: right;">
	<?php echo empty($this->data['Shop']['id']) ? null : $this->Html->link('削除する', array('action' => 'delete', $this->data['Shop']['id'])); ?>
</div>


<div class="pageLinks">
	<p><?php echo $this->Html->link('戻る', array('action' => 'index')); ?></p>
</div>


# app/View/Shops/delete.ctp

<div>
	<h2>店舗削除</h2>
</div>

<div>
	<?php echo $this->Form->create('Shop', array('type' => 'delete')); ?>
	<?php echo $this->Form->input('id', array('type' => 'hidden')); ?>

	<h3>店舗ID</h3>
	<p><?php echo h($this->data['Shop']['id']) ?></p>

	<h3>店舗名</h3>
	<p><?php echo h($this->data['Shop']['name']); ?>


	<h3>※解除されるカテゴリー</h3>
	<ul>
		<?php if (count($this->data['Category']) <= 0): ?>
			<li>なし</li>
		<?php else: ?>
			<?php foreach($this->data['Category'] as $deleteCategory): ?>
			<li>
				<?php echo h($deleteCategory['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['Shop']['id'])); ?></p>
</div>

最後に

ほぼ自分用のメモになってる。
カテゴリの内容入れ替えたりとか、店舗のデータ追加してみたりすればそれっぽくなるかも