CakePHPのデータベースアプリケーションで、「id」以外のカラムを使って複数テーブルを紐付けたいと思った時に少しつまづいたので、メモしておきます。
例として下記のようなテーブル構成を想定します。
脊椎動物に魚類や鳥類があって、無脊椎動物に甲殻類などがあるというような階層構造ですね。
| フィールド名 | 型 | インデックス | 備考 |
|---|---|---|---|
| id | int | primary | auto_increment |
| code | varchar(32) | unique | |
| large_code | varchar(32) | 外部キー:large_category.code | |
| name | varchar(64) | ||
| created | datetime | ||
| modified | datetime |
| フィールド名 | 型 | インデックス | 備考 |
|---|---|---|---|
| id | int | primary | auto_increment |
| code | varchar(32) | unique | |
| name | varchar(64) | ||
| created | datetime | ||
| modified | datetime |
特定の分類コード(category.code)が渡されると、大分類名称/分類名称という形で情報を返すメソッドを作成したいと思います。
分類クラスに下記のようなアソシエーションを定義します。
// 分類クラス定義の冒頭
class Category extends AppModel {
var $name = 'Category';
// バリデーション定義
var $validate = array();
// アソシエーション定義
var $belongsTo = array(
"LargeCategory" => array(
"className" => "LargeCategory",
"foreignKey" => "large_code"
)
);
/* 続く */
で、このようにメソッド本体を作ります。
/**
* 指定カテゴリーの情報を返す
* @param string 分類コード
* @return mixed 配列
返す配列の形式
return = array(
'large_code' => '大分類コード'
'code' => '分類コード',
'name' => '大分類/カテゴリー名',
*/
public function getSingle($code){
$record = $this->find(
'first',array(
'fields' => array(
'LargeCategory.code',
'Category.code',
'LargeCategory.name',
'Category.name'
),
'conditions' => array(
'Category.code' => $code
)
)
);
if( $record ) {
return array(
'large_code' => $record['LargeCategory']['code'],
'code' => $record['Category']['code'],
'name' => $record['LargeCategory']['name'].'/'.$record['Category']['name'],
);
} else {
return false;
}
}
}
これだけでは、実は大分類(large_categories)と紐づいた情報を取得することはできませんでした。
生成されたSQLを見ると分かるのですが、大分類テーブルのレコードを探すときに「id」のカラムを参照してしまっています。
SELECT `LargeCategory`.`code`, `Category`.`code`, `LargeCategory`.`name`, `Category`.`name` FROM `categories` AS `Category` LEFT JOIN `large_categories` AS `LargeCategory` ON ( `Category`.`large_code` = `LargeCategory`.`id` ) WHERE ... /* 以下略 */
CakePHPでは「id」カラムを手がかりに検索を行うという設計が徹底されています。
大分類テーブルのレコードを検索する時に「code」のカラムを使うよう、主キーの設定を変更します。
// 大分類クラス定義
class DaiBunrui extends AppModel {
public $name = 'LargeCategory';
// アソシエーション用に主キー定義を変更
public $primaryKey = 'code';
}
この例ではメンバー変数の初期値を変更してしまいましたが、特定メソッドを使う場合だけ、コントローラー側で主キーの設定を変えるということも可能です。
// Controller 内の処理 // 主キーを一時的に変更 $this->LargeCategory->primaryKey = 'code'; // アソシエーションを設定 $this->Category->$belongsTo = array( /* 中略 */ ); $result = $this->Category->getSingle($code); // 主キーを元に戻す $this->LargeCategory->primaryKey = 'id';