LoginSignup
3
4

More than 3 years have passed since last update.

cakephp3のアソシエーションについて

Last updated at Posted at 2021-04-23

最近使い方を知ったのでメモ

アソシエーションとは

https://book.cakephp.org/3/ja/orm/associations.html
テーブル間に依存関係を定義し、contain('model名')のメソッドでまとめてデータを持ってくるもの。
裏側でjoinをしてる形になる。(多分)

実装するメリット

毎回joinするようなテーブルはいちいち条件を書くのが面倒臭い。
join条件が決まってるのであればアソシエーションを組んだ方がすっきりするし、条件やカラムの変更があったときの手間が少ない

種別

アソシエーションには4つの種別があり、それぞれでオプションとかが異なる。
違いはcookbookに言葉で書いてはあるが、正直個人的にはわかりづらい…

の方がわかりやすく図式してくれてるのでおすすめ
(laravelなので微妙に違ってるかもだけど)

具体例

1、authorsテーブル

id name
1 tanaka
2 satou
3 ota

2、articlesテーブル

id author_id url
1 1 hoge.html
2 2 fuga.html
3 1 piyo.html

3、author_profileテーブル

id author_id profile
1 1 すごい人
2 2 偉い人
3 3 エンジニアの人

上記の例の場合、

1=>3の参照

一対一対応でなければならないのでhasOne。この場合、1のidカラムと2のauthor_idカラムが対応している。
1のカラムにprofileも含めれば?と思うかもしれないけど私も思う。
ていうか現実でhasOneのアソシエーションがあったら大抵カラム追加しようってなる気がするんだけど実際どうなのでしょう…

1=>2の参照

1のidと2のauthor_idを対応させてクエリを組むと、複数レコードが返ってくる。
かつ、1のidカラムはユニークなのでhasMany

2=>1の参照

2のauthor_idカラムと1のidカラムを対応させてクエリを組むと単一のレコードが返ってくる。
かつ、2のauthor_idカラムはユニークではないのでbelongsTo

belongsToManyについて

多対多のアソシエーションだが、こちらはちょっと複雑。
というのも、中間テーブルが必要になるため。
説明のために追加で下記のテーブルを考える

4、tagsテーブル

id name
1 php
2 java
3 C#

5、author_responsible_tagsテーブル

id author_id tag_id
1 1 1
2 1 2
3 2 1
4 3 3

この場合、authorは複数の担当tagを持つことがあり、かつtagは複数のauthorに共有されることがあるためアソシエーションとしてはtags <=> authorsの多対多になる。
が、その二つには関連するカラムが存在しないため、このままだとアソシエーションを組めない。
そこで関連づけるための中間テーブルを定義する必要がある。
(この場合はauthor_responsible_tags)
ということで、belongsToManyに関しては実質3テーブルが関わるアソシエーションとなる。

実装方法

1、modelのinitializeでアソシエーションを宣言
2、findするときにcontain('model名')メソッドを呼び出す
これだけ。

アソシエーションの宣言

上記具体例のテーブル名を使って記述する。
最低限の物だけ書くので、他のオプションはcookbook参照のこと。

hasOne

Table/Authors.php
$this->hasOne('AuthorProfiles', [
    'joinType' => 'LEFT OUTER',  //join方法
    'foreignKey' => 'author_id', //相手側のテーブルの参照カラム(model名_idなら省略できる。)
    'bindingKey' => 'id',        //自分側のテーブルの参照カラム(主キーなら省略できる。)
]);

hasMany

Table/Authors.php
$this->hasMany('Articles', [
    'joinType' => 'LEFT OUTER',  //join方法
    'foreignKey' => 'author_id', //相手側のテーブルの参照カラム(model名_idなら省略できる。)
    'bindingKey' => 'id',        //自分側のテーブルの参照カラム(主キーなら省略できる。)
]);

belongsTo

Table/Articles.php
$this->belongsTo('Authors', [
    'joinType' => 'LEFT OUTER',  //join方法
    'foreignKey' => 'author_id', //自分側のテーブルの参照カラム(相手側のmodel名_idなら省略できる。)
    'bindingKey' => 'id',        //相手側のテーブルの参照カラム(主キーなら省略できる。)
]);

belongsToMany

中間テーブルがある分少し煩雑になる
これだけ実際に試してないので間違ってたらごめんなさい

Table/Authors.php
$this->belongsToMany('Tags', [
    'joinType' => 'LEFT OUTER',               //join方法
    'joinTable' => 'author_responsible_tags', //中間テーブルの名前
    'foreignKey' => 'author_id',              //中間テーブル=>このmodelの参照カラム(自分のmodel名_idなら省略できる。)
    'bindingKey' => 'id',                     //foreignKeyに使われる自分側のテーブルの参照カラム(主キーなら省略できる。)
    'targetForeignKey' => 'tag_id',           //中間テーブル=>相手側のmodelの参照カラム(相手のmodel名_idなら省略できる。)
]);

contain('model名')メソッドを呼び出す

例えばauthorカラムにアソシエーションしているarticlesも結果に含めたいのであれば、

Table/Authors.php
$this->find()->contain('Articles')->all();

のような形で呼び出せば、結果のオブジェクトにarticlesプロパティ が増えており、そこにarticlesの結果が格納されているはず。
複数レコードがあるならちゃんとリストにもなる。
hasManyとbelongsToは片方が成り立てばもう一方も成り立つ関係だが、上記を呼び出すmodelにのみ定義すれば問題ない。

注意点

上記の場合もwhereとかorderは当然できるが、joinする場合と同じくModel名.カラムで記述しないとエラーになるので注意。
例えば上記のクエリに加え、articleのid=1でwhereする場合は

Table/Authors.php
$this->find()->contain('Articles')->where(['Articles.id' => 1])->all();

という書き方になる。
これはアソシエーションされているテーブルの全てのカラムでwhereするカラムを探すためで、たとえ片方にしかないカラムで検索する場合でも必要になる(はず)。

3
4
0

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
3
4