CakePHPの4つのアソシエーションのうち、hasOne(1対1)、hasMany(1対多)、belongsTo(多対1)の3つに関しては理解が容易。だけど初心者としては、残りの1つ、hasAndBelongsToMany(HABTM)(多対多)のアソシエーションに関しては更なる理解が必要だなと思ったので、少し突っ込んで調べてみる。
HABTMの特徴
まずはクックブックを読んでHABTMの特徴を頭に入れる。
「このアソシエーションは、結合される2つのモデルがある場合に使われます。」
言い換えると、2つのテーブルを結合するために使われるということ。
「hasManyとHABTMの大きな違いはHABTMモデル間のリンクは排他的ではない、ということです。」
レシピと材料のテーブルがある場合、hasManyだと、あるレシピで使われている材料は他のレシピには使えない(排他的)けど、HABTMだと、材料が既にあるレシピで使われていても他のレシピでも使える(排他的ではない)ということ。
アソシエーション: モデル同士を繋ぐ — CakePHP Cookbook 2.x ドキュメント
http://book.cakephp.org/2.0/ja/models/associations-linking-models-together.html
HABTMとhasMany throughの使い分け
クックブックによると、多対多のリレーションを定義できるのはHABTMに限らない。
多対多のリレーションを定義するには2つの方法がある。
- hasAndBelongToMeny (HABTM)
- hasMany through
その使い分けの基準については、以下のページが参考になる。
“HABTMの中間モデルの利用について” フォーラム – CakePHP Users in Japan
http://cakephp.jp/modules/newbb/viewtopic.php?topic_id=2819&forum=6
1234 仮定A <- C -> B|AB上記仮定において話を進めます。A, BはCの中間テーブルを元にしたhabtmな関係ですが、今回のようにCに中間データ以外の情報を載せるときには、$hasAndBelongsToManyを使用すると、用意されているひどく単純な処理から外れるのが難しいか、または細かいデータ取得の制御に向いていません。
これは重い足枷となります。よってhasMany throughという方法をとるのが一般的なプラクティスです。
詳細は http://book.cakephp.org/2.0/ja/models/associations-linking-models-together.html#hasmany-through を見てください。
クックブックにも以下の説明がある。
- HABTMの中間テーブルは付加データを持つことをサポートしていない。
- なぜなら、hasAndBelongsToManyアソシエーションはデータを一旦削除してから、そのあとでデータを保存するため。
- そのため、新しいレコードが挿入されるとき、外部キーID以外の追加フィールドのデータが失われてしまう。
ただし、最近のバージョンでは改良されているみたいだけど。
バージョン 2.1 で変更.
unique に keepExisting を指定すれば、追加フィールドのデータを失うことなく保存できます。 unique キーについてはHABTM association arrays を参照してください。
ということで、とりあえず僕の結論としては、多対多のリレーション定義は以下のルールに従うことにする。
- アソシエーションに付加データが無い場合はHABTM
- アソシエーションに付加データがある場合はhasMany though