CakePHP1.3.6:HABTM(hasAndBelongsToMany)とscaffold
モデルのアソシエーション(リレーション)として、これまで、hasOne、hasMany、belongsTo検証したので、HABTM(hasAndBelongsToMany)、つまり、N:Mの関係を検証してみる。
scaffoldをやってびっくり。とっても簡単に定義できる。N:M(複数:複数)のリレーションは、設計上、どうしても敬遠したくなってしまうが、これなら躊躇無くモデリングできる。
テーブル
実験するために、以下の2つのテーブル(itemsとcategories)を用意する。
CREATE TABLE IF NOT EXISTS `items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `memo` text COLLATE utf8_unicode_ci NOT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ; CREATE TABLE IF NOT EXISTS `categories` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `memo` text COLLATE utf8_unicode_ci NOT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
HABTMでは、中間テーブルを利用して、itemsとcategoriesの関係を保存する。
そのためのテーブルスキーマが以下。
REATE TABLE IF NOT EXISTS `category_items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `item_id` int(11) NOT NULL, `category_id` int(11) NOT NULL, `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`,`item_id`,`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
モデル。
categoriesとitemsのモデルを以下のように作成する。
category.php
<?php class Category extends AppModel { public $name = 'Category'; public $hasAndBelongsToMany = array( "Item" => array( 'className' => 'Item', 'joinTable' => 'category_items', 'foreignKey' => 'category_id', 'associationForeignKey' => 'item_id', 'unique' => true, 'order' => 'item_id' ) ); public $validate = array( 'name'=>array( array( 'rule' => 'notEmpty', 'message' =>'入力してください'), array( 'rule' => 'isUnique', 'message' =>'すでに登録されています'), ), ); } ?>
item.php
<?php class Item extends AppModel { public $name = 'Item'; public $hasAndBelongsToMany = array( "Category" => array( 'className' => 'Category', 'joinTable' => 'category_items', 'foreignKey' => 'item_id', 'associationForeignKey' => 'category_id', 'unique' => true, 'order' => 'category_id' ) ); public $validate = array( 'name'=>array( array( 'rule' => 'notEmpty', 'message' =>'入力してください'), array( 'rule' => 'isUnique', 'message' =>'すでに登録されています'), ), ); } ?>
scaffoldを作成する。
categories_contorller.php
まず、categoriesテーブルのためのscaffoldをつくる。
<?php class CategoriesController extends AppController{ public $name = 'Categories'; public $scaffold; } ?>
items_controllers.php
<?php class ItemsController extends AppController{ public $name = 'Items'; public $scaffold; } ?>
scaffoldを使ってみる
まず、categoriesにアクセスしてカテゴリーの登録をする。
以下がscaffoldの登録画面。相変わらず画面上部にエラーが出るが、(エラー箇所を覗いてみると)どうやらtimestampフィールドをscaffoldでハンドルしようとする場合にでるようだ。
(timestampの件、解決しました。どうもありがとうございました。http://cakephp.jp/modules/newbb/viewtopic.php?topic_id=369&forum=3)
最後のitemの欄はとりあえず無視しておいていい(後述)。
以下のように3つのカテゴリーを入力する。
それでは、itemを入力してみる。itemsにアクセスして、New Itemをクリックする。
開けてびっくり、categoryに入力したところがマルチでセレクトできる。
以下が参照画面。
この調子で商品1がカテゴリー1と3、商品2がカテゴリー2、3とアソシエートするように登録する。
それで、画面を戻してcategoriesにアクセスし、New Categoryをクリックすると以下のように、カテゴリー側の登録の際には、商品がマルチで選択できるようになっている。素晴らしい!!
では、この関係を表す情報は、というと、以下のようにcategory_itemsテーブルに保管されている(phpMyAdminでみたところ)。