生まれて初めて、CakePHPのHABTMを書いてみたの♪
Bakeに頼らず自分でHABTAMのアソシエーションを書いたのは初めてだったかも。
正直わかりにくいのでどうしても避けてきたのですが、比較的仕事に余裕があったので、ゆっくりやってみて、
今日やっとこさ理解することができました
HABTMの構造
HABTAMとはHasAndBelongToMany(HABTM..ハビタムで呼び方あってる?)の略で、多対多のデータ構造です。
多対多のデータ構造の例
多対多のデータ構造の典型的な例はブログですかね。記事はたくさんあって、必ずどれかのカテゴリーに入っているけれど、一つの記事は複数のカテゴリーに分類されるケースもある。
そういうのが多対多のHABTMです。
・ブログの記事(Post 多のデータ)とカテゴリー(Category 多のデータ)とその中間テーブル(CategoryPost 中間テーブルのデータ)
HABTMアソシエーションをPostModelに書いてみる
Post.php
class Post extends AppModel {
public $name = 'Post';
public $hasAndBelongsToMany = array(
'Category' => array(
'className' => 'Category',
'join_table' => 'categories_posts',//中間テーブルの名称(Model名ではなくテーブル名)
'foreignKey' => 'post_id',//categories_postsの中のCategoryとのforeignKey
'associationForeignKey' => 'category_id',//アソシエーションしている中のforeignKey.ここではCategoryPostのcategory_id
),
);
}
categories_postsという中間テーブル、当初はposts_categoriesでした。でもなぜかこれだとcategories_postsっていうテーブルが無いよ!というエラーが発生してしまい、
結局中間テーブルの名称そのものをposts_categories→categories_postsに変更しました。ナゼ?why?
recursiveで深度を設定していなくても中間データまで一発で取得できているところが非常にナイスです!
PostsControllerで、Postデータをfind Allした結果
hasAndBelongsToManyのアソシエーションをPostModelにしただけで、投稿した記事(Post)が、どのカテゴリーに属しているか、さらにその中間データであるCategoriesPostのデータも取得ができました
PostModelにちょっと書いただけで、複雑なアソシエーションをしてくれるなんて、ちょっとうれしい。。。
array(
(int) 0 => array(
'Post' => array(
'id' => '9e8c4ef7-33d0-11e4-a724-3417eba238af',
'title' => '記事タイトル',
'text' => '記事本文',
'created' => '2014-09-04 10:12:45',
'modified' => '2014-09-04 10:12:47'
),
'Category' => array(
(int) 0 => array(
'id' => '4b056148-33d0-11e4-a724-3417eba238af',
'slug' => 'category_1',
'name' => 'カテゴリー1',
'created' => '2014-09-04 10:10:29',
'modified' => '2014-09-04 10:10:32',
'CategoriesPost' => array(
'id' => 'e6dd311a-33f3-11e4-a724-3417eba238af',
'post_id' => '9e8c4ef7-33d0-11e4-a724-3417eba238af',
'category_id' => '4b056148-33d0-11e4-a724-3417eba238af',
'created' => '2014-09-04 14:25:32',
'modified' => '2014-09-04 14:25:35'
)
),
(int) 1 => array(
'id' => '4b055c33-33d0-11e4-a724-3417eba238af',
'slug' => 'data_input',
'name' => 'category_2',
'created' => '2014-09-04 10:10:24',
'modified' => '2014-09-04 10:10:27',
'CategoriesPost' => array(
'id' => 'fd4f31a0-33d0-11e4-a724-3417eba238af',
'post_id' => '9e8c4ef7-33d0-11e4-a724-3417eba238af',
'category_id' => '4b055c33-33d0-11e4-a724-3417eba238af',
'created' => '2014-09-04 10:15:36',
'modified' => '2014-09-04 10:15:38'
)
)
)
),
今度は逆に、もう一方の多のデータであるCategoryModelをHABTMしてみる
foreignKeyとassociationForeignKeyが逆になっているだけで後は全く同じことに注目してください。
class Category extends AppModel {
public $hasAndBelongsToMany = array(
'Post' => array(
'className' => 'Post',
'join_table' => 'categories_posts',
'foreignKey' => 'category_id',//categories_postsの中のPostとのforeignKey
'associationForeignKey' => 'post_id',//アソシエーションしている中のforeignKey.ここではCategoryPostのpost_id
),
);
}
Categoryデータをfind Allした結果
先ほどと同様に、PostModelの下にCategoriesPost(中間データ)がアソシエーションされたデータが返ってきました。
array(
(int) 0 => array(
'Category' => array(
'id' => '4b055c33-33d0-11e4-a724-3417eba238af',
'slug' => 'data_input',
'name' => 'データ入力',
'created' => '2014-09-04 10:10:24',
'modified' => '2014-09-04 10:10:27'
),
'Post' => array(
(int) 0 => array(
'id' => '9e8c4ef7-33d0-11e4-a724-3417eba238af',
'title' => '記事タイトル',
'text' => '記事本文',
'created' => '2014-09-04 10:12:45',
'modified' => '2014-09-04 10:12:47',
'CategoriesPost' => array(
'id' => 'fd4f31a0-33d0-11e4-a724-3417eba238af',
'post_id' => '9e8c4ef7-33d0-11e4-a724-3417eba238af',
'category_id' => '4b055c33-33d0-11e4-a724-3417eba238af',
'created' => '2014-09-04 10:15:36',
'modified' => '2014-09-04 10:15:38'
)
),
(int) 1 => array(
'id' => 'c672c872-33d0-11e4-a724-3417eba238af',
'title' => '記事タイトル',
'text' => '記事本文',
'created' => '2014-09-04 10:13:52',
'modified' => '2014-09-04 10:13:54',
'CategoriesPost' => array(
'id' => 'fd4f3512-33d0-11e4-a724-3417eba238af',
'post_id' => 'c672c872-33d0-11e4-a724-3417eba238af',
'category_id' => '4b055c33-33d0-11e4-a724-3417eba238af',
'created' => null,
'modified' => null
)
)
)
),
(int) 1 => array(
'Category' => array(
'id' => '4b056148-33d0-11e4-a724-3417eba238af',
'slug' => 'category_2',
'name' => 'カテゴリー2',
'created' => '2014-09-04 10:10:29',
'modified' => '2014-09-04 10:10:32'
),
'Post' => array(
(int) 0 => array(
'id' => '9e8c4ef7-33d0-11e4-a724-3417eba238af',
'title' => '記事タイトル',
'text' => '記事本文',
'link' => 'http://www.atsoho.com/jobinfo/detail/no-59201.html',
'created' => '2014-09-04 10:12:45',
'modified' => '2014-09-04 10:12:47',
'CategoriesPost' => array(
'id' => 'e6dd311a-33f3-11e4-a724-3417eba238af',
'post_id' => '9e8c4ef7-33d0-11e4-a724-3417eba238af',
'category_id' => '4b056148-33d0-11e4-a724-3417eba238af',
'created' => '2014-09-04 14:25:32',
'modified' => '2014-09-04 14:25:35'
)
)
)
)
)
わかり難いからいやだとずっと避けて別々にデータを取得していたのですが、recursiveを設定しなくても、一発で中間データまで取ってきてくれるのはいいですね。
ちょっとわかり難い部分もありましたが、今後は使って生きたいと思います。