参考にさせて頂いたページ
HABTM(hasAndBelongsToMany)について
アソシエーション: モデル同士を繋ぐ — CakePHP Cookbook 2.x ドキュメント
あの日見たHABTMの中間テーブルの使い方を僕たちはまだ知らない | 日記の間 | あかつきのお宿
HABTM(hasAndBelongsToMany)のwith項目の設定について
php - CakePHP Fatal Error Call to a member function schema() on a non-object - Stack Overflow
HABTM(hasAndBelongsToMany)のデータ保存はsaveAllを使うことについて
cakePHPでHABTMのレコードを一括保存する方法 - Qiita
saveAllの使い方について
データを保存する — CakePHP Cookbook 2.x ドキュメント
SQLのlimitなどを指定する
CakePHP hasAndBelongsToMany でページング(SQL LIMIT)とかを設定する ← Neo Inspiration
記事タグ追加フォームの作成とデータの挿入
以下の過去記事で記事タグ追加ページを作成した。
articlesテーブルとarticletagsテーブルを紐付けてデータを効率的に取得するにあたって、まずはarticletagsテーブルにデータが必要なので、あらかじめデータを挿入しておいた。
さくらvpsとcakephp2.6.7で開発日記 その0012 記事タグ追加フォームを作る - MOTOMICHI WORKS BLOG
articlesテーブルの作成
例として以下の通り。
CREATE TABLE `articles` ( `id` int(11) NOT NULL AUTO_INCREMENT, `code` varchar(255) NOT NULL, `user_id` varchar(255) NOT NULL, `title` varchar(255) NOT NULL, `text` varchar(2000) NOT NULL, `is_publish` tinyint(1) NOT NULL, `is_deleted` tinyint(1) NOT NULL DEFAULT '0', `created` datetime NOT NULL, `modified` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
HABTMに直接関係ないカラムも沢山あります。
HABTMに必要な条件はidカラムと、それがプライマリキーである事だけかな?
あとテーブル名も中間テーブル名に関わったり、foreignKeyとかassociationForeignKeyに関わってくるから重要かな?
articletagsテーブルの作成
例として以下の通り。
CREATE TABLE `articletags` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` varchar(255) NOT NULL, `text` varchar(255) NOT NULL, `is_deleted` tinyint(1) NOT NULL DEFAULT '0', `created` datetime NOT NULL, `modified` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
HABTMに直接関係ないカラムも沢山あります。
HABTMに必要な条件はidカラムと、それがプライマリキーである事だけかな?
あとテーブル名も中間テーブル名に関わったり、foreignKeyとかassociationForeignKeyに関わってくるから重要かな?
articles_articletagsテーブルの作成
例として以下の通り。
CREATE TABLE `articles_articletags` ( `id` int(11) NOT NULL AUTO_INCREMENT, `article_id` int(11) NOT NULL DEFAULT '0', `articletag_id` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
articlesテーブルとarticletagsテーブルを紐付ける為の中間テーブルなので、アンダースコアを付けてアルファベット順に繋げてarticles_articletagsというテーブル名にする。
カラム名も紐付けする二つのテーブル名にならって、article_idとarticletag_idという単数形。
app/Model/Article.php
例として以下の通り。$validatesの部分は省いています。
<?php App::uses('AppModel', 'Model'); class Article extends AppModel{ public $hasAndBelongsToMany = array('Articletag'); }
テーブル名やカラム名、クラス名などがCakePHPの命名規則にならっている場合は、$hasAndBelongsToManyに格納する値はpublic $hasAndBelongsToMany = array('Articletag');
だけで良い。
特別にデフォルト値以外の設定をしたいときだけ、アソシエーション: モデル同士を繋ぐ — CakePHP Cookbook 2.x ドキュメントにあるような詳細設定をすることになる。
詳細に設定する場合には以下のように書くと動いた。
withには中間テーブル名を設定するのかな?
これについては、php - CakePHP Fatal Error Call to a member function schema() on a non-object - Stack Overflowを参考にさせて頂いた。
public $hasAndBelongsToMany = array( 'Articletag' => array(//この連想配列keyの文字列は紐付けたいModel名 'className' => 'Articletag',//紐付けたいModel名 'joinTable' => 'articles_articletags',//中間テーブル名 'foreignKey' => 'article_id',//このテーブルの外部キーarticlesテーブルなのでarticle_id(単数形) 'associationForeignKey' => 'articletag_id',//articletagsテーブルを紐付けたいのでarticletag_id(単数形) 'unique' => true, 'conditions' => '', 'fields' => '', 'order' => '', 'limit' => '', 'offset' => '', 'finderQuery' => '', 'with' => 'articles_articletags' ) );
app/Controller/ArticlesController.php
例えばindexアクションで単純にfindを使って、$view_datas = $this->Article->find('all');
とすれば多対多の紐付けされたデータが取得できるので、print_r($view_datas);とかで確認すると、配列構造がわかる。
例えばpostアクションでは、saveAllメソッドを使用する事と、そのsaveAllメソッドに適切な構造の配列を渡す必要があり、フォームから送信されたデータを適当に整形して、以下のような階層構造の配列を用意してやると良い。
キーは自分のテーブルに合わせて適宜変えてください。
//saveAllに渡す為のデータを準備 $params = array( array( 'Article' => array( 'code' => '文字列', 'user_id' => '文字列', 'title' => '文字列', 'text' => '文字列', 'is_publish' => '1', 'is_deleted' => '0', ), 'Articletag' => array( //articletagsテーブルのid //以下のように配列要素が3つあると、中間テーブルであるarticles_articletagsに3行追加される '1', '2', '3', ) ) );
saveAllは上記のような階層構造の配列を、以下のような感じで渡してデータベースに保存する。
$this->Article->saveAll($params);
このとき、articlesテーブルにデータが挿入されるのはもちろん、articles_articletagsにも自動的にデータが保存される。
HABTMについてまとめ
今回の例では、以下の三つのテーブルを用意して、articletagsには予めデータを挿入しておいた。
- articles
- articletags
- articles_articletags
CakePHPの命名規則に則っていれば、$hasAndBelongsToManyに格納する値は、public $hasAndBelongsToMany = array('Articletag');
だけで良い。
Controllerでは、普通に$view_datas = $this->Article->find('all');
とするだけで多対多の紐付けされたデータが取得できるし、saveAllメソッドに適切な構造の配列を渡せば、中間テーブルにもデータが挿入される。
saveAllメソッドについては、データを保存する — CakePHP Cookbook 2.x ドキュメントを参考にした。