cakephpでHABTMなアソシエーションが思うように動作しないので単純化テストする その1

実験環境

		//$this->subDir = 'smarty';
  • aa()関数を追記してます APP/views/smarty.php#210あたり
	function aa() {
	    $args = func_get_args();
		return call_user_func_array('aa', $args);
	}

要求仕様

  • 商品「りんご」「みかん」「いちご」はカテゴリ「果物」に属している
  • 商品「レタス」「きゅうり」「キャベツ」はカテゴリ「野菜」に属している
  • 商品「トマト」はカテゴリ「果物」と「野菜」両方に属している
  • 商品「仮登録」はaddオペレーション直後を想定し、どのカテゴリにも属してなく、隠し属性1(仮登録)をつける
  • 商品「いちご」は隠し属性2(在庫切れ)をつける
  • 商品「キャベツ」は隠し属性3(廃盤)をつける
  • 商品は複数のカテゴリを持ち、カテゴリは複数の商品を持つ(HABTM)
  • 商品は隠し属性というパラメータを持ち、数値の0〜3をが入る

データベース設計

  • productsテーブルを作る
    • id,name,hiddenフィールドを作る
  • categoriesテーブルを作る
    • id,nameフィールドを作る
  • cakephpの規約に従ってcategories_productsテーブルを作る(2つのテーブル名を名前順に_でつなげる)
    • product_id,category_idフィールドを作る(それぞれの単数形_id)
その結果のSQLダンプ
-- phpMyAdmin SQL Dump
-- version 2.10.2
-- http://www.phpmyadmin.net
-- 
-- ホスト: localhost
-- 生成時間: 2009 年 6 月 12 日 12:32
-- サーバのバージョン: 5.0.41
-- PHP のバージョン: 5.2.5

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

-- 
-- データベース: `exp`
-- 

-- --------------------------------------------------------

-- 
-- テーブルの構造 `categories`
-- 

CREATE TABLE `categories` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

-- 
-- テーブルのデータをダンプしています `categories`
-- 

INSERT INTO `categories` VALUES (1, '果物');
INSERT INTO `categories` VALUES (2, '野菜');

-- --------------------------------------------------------

-- 
-- テーブルの構造 `categories_products`
-- 

CREATE TABLE `categories_products` (
  `product_id` int(11) NOT NULL,
  `category_id` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

-- 
-- テーブルのデータをダンプしています `categories_products`
-- 

INSERT INTO `categories_products` VALUES (3, 1);
INSERT INTO `categories_products` VALUES (2, 1);
INSERT INTO `categories_products` VALUES (5, 2);
INSERT INTO `categories_products` VALUES (4, 2);
INSERT INTO `categories_products` VALUES (1, 1);
INSERT INTO `categories_products` VALUES (7, 1);
INSERT INTO `categories_products` VALUES (6, 2);
INSERT INTO `categories_products` VALUES (7, 2);

-- --------------------------------------------------------

-- 
-- テーブルの構造 `products`
-- 

CREATE TABLE `products` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  `hidden` tinyint(4) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=9 ;

-- 
-- テーブルのデータをダンプしています `products`
-- 

INSERT INTO `products` VALUES (1, 'りんご', 0);
INSERT INTO `products` VALUES (2, 'みかん', 0);
INSERT INTO `products` VALUES (3, 'いちご', 2);
INSERT INTO `products` VALUES (4, 'レタス', 0);
INSERT INTO `products` VALUES (5, 'きゅうり', 0);
INSERT INTO `products` VALUES (6, 'キャベツ', 3);
INSERT INTO `products` VALUES (7, 'トマト', 0);
INSERT INTO `products` VALUES (8, '仮登録', 1);

モデル

APP/models/product.php
<?php
class Product extends AppModel {
    var $name = 'Product';
    var $hasAndBelongsToMany = array(
        'Category' => array(
            'className' => 'Category',
            'joinTable' => 'categories_products',
            'foreignKey' => 'product_id',
            'associationForeignKey' => 'category_id'));
}
?>
APP/models/category.php
<?php
class Category extends AppModel {
    var $name = 'Category';
    var $hasAndBelongsToMany = array(
        'Product' => array(
            'className' => 'Product',
            'joinTable' => 'categories_products',
            'foreignKey' => 'category_id',
            'associationForeignKey' => 'product_id'));
}
?>

scaffoldで動作確認

APP/controllers/products_controller.php
<?php
class ProductsController extends AppController {
    var $name = 'Products';
    //var $view = 'Smarty';
    var $helpers = array('Html', 'Form');
    var $uses = array('Product','Category');
    var $scaffold;
}
?>
APP/controllers/category_controller.php
<?php
class CategoriesController extends AppController {
    var $name = 'Categories';
    //var $view = 'Smarty';
    var $helpers = array('Html', 'Form');
    var $scaffold;
}
?>
ここでアソシエーションに不備がないかをscaffoldを使ってチェック
  • http://localhost/.../productsにアクセス
  • 商品とカテゴリのテーブルを行き来して自由に編集できることを確認
  • 以降APP/models/*はほとんどいじらない

全件表示するコントローラーとビュー

APP/controllers/products_controller.php
<?php
class ProductsController extends AppController {
    var $name = 'Products';
    var $view = 'Smarty';
    var $helpers = array('Html', 'Form');
    var $uses = array('Product','Category');
    //var $scaffold;
    
    function index() {
        $products = $this->Product->find('all');
        pr($products);
        $this->set('products', $products);
    }
}
?>
APP/views/products/index.tpl
<table cellpadding="0" cellspacing="0">
<tr>
  <th>名前</th>
  <th>表示</th>
  <th>操作</th>
</tr>
{foreach from=$products item='product'}
  <tr>
    <td>
    {assign var='id' value= $product.Product.id}
    {$html->link($product.Product.name, "view/$id")}
    </td>
    <td>
    {$product.Product.hidden}
    </td>
    <td>
      {$html->link('編集', "/products/edit/$id")} 
      {$html->link('削除', "/products/delete/$id")}
    </td>
</tr>
{/foreach}
</table>
「トマト」だけ抜粋すると
...
    [6] => Array
        (
            [Product] => Array
                (
                    [id] => 7
                    [name] => トマト
                    [hidden] => 0
                )

            [Category] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [name] => 果物
                        )

                    [1] => Array
                        (
                            [id] => 2
                            [name] => 野菜
                        )

                )

        )
...