【CakePHP3】アソシエーションあれこれ

cakephpの機能の一つ、アソシエーション。モデルとモデルをつなぎ、データの取得や保存を行う際に接続先のデータを同時に扱えるようにする機能です。

今回は、このアソシエーションをより柔軟に使用する方法について書いていこうと思います。

※以下では会員登録を行うサイトを想定し、会員の情報を保存するUsersテーブルと、その会員のメールアドレスを保存するUserMailsテーブルが存在するとします。

1. アソシエーションそのものに接続先のテーブルとは別の名前を付ける

// Usersテーブルで
$this->hasMany('UserMails', [
  'foreignKey' => 'user_id',
]);

cakephpのアソシエーションの基本的な構文は、上記のような記述になります。「’UserMails’」のようにテーブル名を記すことで、指定したテーブルとのアソシエーションを設定できるわけです。

では、hasManyの第一引数には必ずテーブル名を設定しなければならないのかというと、そうではありません。

下記のように、オプションとして「className」に接続したいテーブルの名前を指定することにより、第一引数をテーブル名としなくてもアソシエーションが実現できます。

// Usersテーブルで
$this->hasMany('MailAssociation'/*任意の名称*/, [
  'className' => 'UserMails',
  'foreignKey' => 'user_id',
]);

2. アソシエーションに条件を指定する

UserMailsテーブルには1人の会員に対して複数のメールアドレスが保存可能であり、それらのアドレスのうち携帯電話のキャリアメールとWebメールとを区別することを目的に、UserMailsテーブルのレコードに「web_mail_flg」を持たせているとします(0:キャリアメール、1:Webメールにそれぞれ対応)。

このとき、Webメールのみを対象としたアソシエーションを作りたい場合は、アソシエーションのオプション「conditions」に接続条件を指定することで実現可能です。

// Usersテーブルで
$this->hasMany('WebMails', [
  'className' => 'UserMails',
  'foreignKey' => 'user_id',
  'conditions' => ['WebMails.web_mail_flg' => 1/*Webメール*/],
]);

このような条件指定と1.で解説したアソシエーション名をテーブル名とは別にする手法とを組み合わせると、同一テーブルのデータであっても条件によって異なるアソシエーションに振り分けることもできます。

そうしたアソシエーションの振り分けは、あるテーブルに紐づく別テーブルのレコードのうち、一定の条件を満たすもののみ取得したり、データの編集を行ったりという機能を実装したい場合に有用です。

3. アソシエーションの紐づけに主キー以外を用いる

アソシエーションを設定する際、とくに気を使わなければ接続元(以下でいうとUsersテーブル)で参照するカラムは主キー(たいていの場合、「id」)になっています。

もし接続元のテーブルで主キー以外を参照したい場合、アソシエーションのオプション「bindingKey」にそのカラムを指定することで参照先を変更することが可能です。

// Usersテーブルで
// ※Usersテーブル、UserMailsテーブルとも外部のグループに所属しており、
// 「group_id」というカラムにそのグループのIDを保存しているとする
$this->hasMany('UserMails', [
  'bindingKey' => 'group_id',
  'foreignKey' => 'group_id',
]);

こう記述すると、UsersテーブルとUserMailsテーブルがもつ共通のカラム「group_id」を条件としたアソシエーションが設定されるわけです。

また、上記の「bindingKey」「foreignKey」にそれぞれ同じ要素数の配列を設定した場合、その配列に基き複数のカラムを条件とするアソシエーションを設定することもできます。


以上、cakephpのアソシエーションについての解説でした。

願わくば、この記事がどなたかの助けにならんことを。