LoginSignup
87
102

More than 5 years have passed since last update.

CakePHPのアソシエーションがわからなくなったとき用

Last updated at Posted at 2015-12-09

http://book.cakephp.org/2.0/ja/models/associations-linking-models-together.html
http://blog.ecworks.jp/archives/268
http://qiita.com/kazu56/items/eaaa0c2d7294a28ac21c
http://qiita.com/moriyant/items/b57ee4fc45af2fc03194

hasとbelongが、すぐどっちがどっちかわからなくなる。
「持っている」とかアバウトな言葉じゃなくてテーブル定義で教えてくれ。

結論

hasMany
相手テーブルに、自分テーブル.idへの外部キーがある。
hasOne
hasManyだけど外部キーがUNIQUE。
belongsTo
自分のテーブルに、相手テーブル.idの外部キーがある。
hasAndBelongsToMany
中間テーブルでくっついてるやつ。

hasMany

ユーザは複数の趣味を持っている。任意入力。

User.php
class User{
    $hasMany = [
        'Hobby'=>[
            'className' => 'Hobby',
        ],
    ];
}
usersテーブル
特に趣味に関する項目はない。
hobbiesテーブル
id
user_id ← users.idに外部キー
name

趣味をスペース区切りとかで複数入力してもらい、それをそのまま保存するタイプのユーザ登録。
よくある構造のテーブルです。

hasOne

ユーザはひとつのプロフィールを持っている。

User.php
class User{
    $hasOne = [
        'Profile'=>[
            'className' => 'Profile',
        ],
    ];
}
usersテーブル
プロフィールに関する項目はない。
profilesテーブル
id
user_id ← users.idに外部キー、かつUNIQUE制約
family_name
first_name

この構造は公式の例なのですが、でもこういう場合普通はusersテーブルに個人情報入れるよな。
hasOneを使うということはつまりテーブルの垂直分割なので、あまり使う必要性が感じられない。

belongsTo

ユーザは職業IDを持っている。

User.php
class User{
    $belongsTo = [
        'Employment'=>[
            'className' => 'Employment',
        ],
    ];
}
usersテーブル
id
employment_id ← employments.idに外部キー
employmentsテーブル
id
name

employmentsテーブルには「学生」「会社員」「無職」などのデータが入っていて、ユーザ登録時にその項目を選択してIDを登録するというよくある形態。

hasAndBelongsToMany

ユーザは複数の趣味を持っている。選択式。

User.php
class User{
    $hasAndBelongsToMany = [
        'Hobby'=>[
            'className' => 'Hobby',
            'with'      => 'UserHobby',
            'foreignKey' => 'hobby_id',
            'associationForeignKey' => 'user_id',
        ],
    ];
}
usersテーブル
特に趣味に関する項目はない。
hobbiesテーブル
id
name
user_hobbiesテーブル
id
user_id ← users.idに外部キー
hobby_id ← hobbies.idに外部キー

hasManyの趣味は直接入力でしたが、こちらは予め項目が表示されており、その中からチェックボックスで複数選択するタイプの登録フォーム。

UserとHobbyにUserHobbyへのhasMany、UserHobbyからUserとHobbyへのbelongsToを書けばだいたい同じ意味になると思うけど、めんどうなので一気に書けるとかそういうかんじですかね。

その他

リレーションがあるだけひたすら書いておくだけで、findすると勝手に拾ってきてくれて便利なのですが、SQLの発行数がえらいことになります。
辿る深さはrecursiveで制御できますが、何故か特定の先だけ制御する方法がありません。

A→B→C→D
↓
E→F→G
↓
H→I
↓
J

なんてテーブルがあったとして、Dが欲しいんだと$this->A->recursive = 3と設定するとSQLが何十個も発行されます。
や、Bの先だけ欲しいんだと$this->A->B->recursive=2;$this->A->E->recursive=-1;とか書いても、どうも効かないみたいです。
そういう記事が全く見当たらないんだけどできないんじゃろうか。

あとrecursive深くするとA→B→A→Bなんてのも辿るみたいなんだけどこれは必要なのか?

87
102
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
87
102