[CakePHP] アソシエーション

タグ :

CakePHPの基礎をまとめるシリーズです。
今回はモデルの「アソシエーション」についてです。
過去記事「基礎(モデル)」にもまとめてありますが、今回はちょっとだけ詳しく書きたいと思います。
参考サイト「CakePHP Cookbook 2.x

概要

アソシエーションはモデル同士のデータの関連を設定することで、一度のデータ操作で複数のモデルからデータを取得したり、データを更新する機能です。
内部の処理では自動的に SQL に JOIN(結合) を追加したり、関連データを再度取得するクエリを実行するなどして必要なデータを取得します。
不必要な関連データを取得し過ぎることは処理速度の低下等につながりますし、何より無駄な処理となりカッコ悪いです。
find() メソッドの recursive オプションを設定したり、contain() を設定することで処理ごとに関連データを取得する範囲を限定することもできます。

アソシエーションは、アソシエーション名のクラス変数を生成することで定義されます。
クラス変数の内容は単純な文字列でもいいですし、アソシエーションを具体的に定義するために多次元の配列を使うこともできます。

hasOne

モデルが別のモデルのデータに関連しているという関係を持たせるアソシエーションです。
このアソシエーションを定義すれば、例えば User モデルで find した時に、関連する Profile が存在すればそのレコードも一緒に取得してくるようになります。
自分が Bake で生成したデータには、何故か hasOne では生成されず、全て hasMany で生成されています(関連が1つでも)。
hasMany でも出来るので、問題は無いかと思います。
hasOne には以下のオプションの指定が可能です。

名称 説明 記述例
className 元モデルに関連付けられるモデルのクラス名
省略時 = 設定名と同一
‘className’ => ‘User’
foreignKey アソシエーション先のモデルを検索するための外部キー
省略時 = 小文字のモデル名 + _id
‘foreignKey’ => ‘user_id’
conditions アソシエーション先のモデルを指し示す外部キー
省略時 = null
‘conditions’ => array(‘User.active’ => true)
fields アソシエーション先のモデルから取得するフィールドのリスト
省略時 = 全フィールド
‘fields’ => array(‘User.id’, ‘User.username’)
order 関連モデルに対するソート条件
省略時 = null
‘order’ => array(‘User.id ASC’)
dependent このキーに ture がセットされていて、かつモデルの delete メソッドの cascade パラメータに true がセットされて呼び出された時、アソシエーション先のモデルのレコードも一緒に削除される
省略時 = false
‘dependent’ => true

自分は Bake で生成されないので使用することは無いと思います。
hasMany で代用が効くみたいですので、特別に何か関連付けが必要になったときに試しに使ってみても良いかもしれません。
(後に複数関連付けが必要になった時 hasMany に変えるのが面倒になりそうですが、、、)

hasMany

hasOne と同様のアソシエーションですが、複数データを関連付けることができます。
belongsTo とは逆の関係になります。
大抵は belongsTo が設定されている場合は、hasMany の設定もする必要があります。

データベーステーブルが作り込まれているならば Bake することで自動的に割り当ててくれます。
自分は Bake 生成データから特に変更せず、そのまま使用しています。
hasMany には以下のオプションの指定が可能です。

名称 説明 記述例
className 所属先のデータを扱うモデルクラス
省略時 = 設定名と同一
‘className’ => ‘User’
foreignKey アソシエーション先のモデルを検索するための外部キー
省略時 = 小文字のモデル名 + _id
‘foreignKey’ => ‘user_id’
conditions アソシエーション先のモデルを指し示す外部キー
省略時 = null
‘conditions’ => array(‘User.active’ => true)
order 関連モデルに対するソート条件
省略時 = null
‘order’ => array(‘User.id ASC’)
limit アソシエーションモデルのデータの最大行数
省略時 = null
‘limit’ => 10
offset アソシエーションモデルのデータをスキップする行数
省略時 = null
‘offset’ => 10
dependent true をセットすれば、データを再帰的に削除するようになります
省略時 = false
‘dependent’ => true
exclusive true をセットすれば、deleteAll() を呼び出した時にデータを再帰的に削除するようになります
省略時 = false
‘exclusive’ => true
finderQuery アソシエーションモデルのレコードを取得する時に使われる SQL クエリ
省略時 = null
‘finderQuery’ => ‘SELECT * FROM profiles as Profile WHERE user_id = {$__cakeID__$}’

finderQuery は取得結果をカスタムしたいときに使用します。
データベーステーブルがちゃんとしており、ただ単にデータを取得したいだけなら、使うことは無いと思います。
特別に絞り込みを行いたい場合などで使用するのかもしれませんが、今のところ使用するキッカケがありません。

使用できるオプションをみても hasOne よりも hasMany の方が色々と指定ができるので便利っぽいです。
hasOne の方は SQL を生成する際に軽くなるのかな?(JOINとかで)とも思いますが、今度検証してみたいと思います。

サイト作成の際、後になって追加したテーブルを Bake しても、元々あったファイルまでは変更してくれません。
例えば UserImage テーブルを生成した場合、User テーブルには hasMany は含まれていないので、手動で追加する必要があります。
忘れると取得できなくて、何でだろうと時間を無駄にすることになるので気を付けましょう。。。

belongsTo

モデルが別のモデルデータに所属しているという関係のアソシエーションです。
belongsTo は hasOne、hasMany アソシエーションと対になり、hasOne、hasMany とは逆方向からデータを参照することになります。
ここも基本的に Bake で生成された状態のままです。
belongsTo には以下のオプションの指定が可能です。

名称 説明 記述例
className 元モデルに関連付けられるモデルのクラス名
省略時 = 設定名と同一
‘className’ => ‘User’
foreignKey アソシエーション先のモデルを検索するための外部キー
省略時 = 小文字のモデル名 + _id
‘foreignKey’ => ‘user_id’
conditions アソシエーション先のモデルを指し示す外部キー
省略時 = null
‘conditions’ => array(‘User.active’ => true)
type SQL クエリで使われるテーブル結合種別
省略時 = LEFT
‘type’ => ‘left’
fields アソシエーション先のモデルから取得するフィールドのリスト
省略時 = 全フィールド
‘fields’ => array(‘User.id’, ‘User.username’)
order 関連モデルに対するソート条件
省略時 = null
‘order’ => array(‘User.id ASC’)
counterCache true をセットすれば、アソシエーション先のモデルで save() または delete() を実行した時に、テーブルの [モデル名の単数形]_count というフィールドの値を増減
文字列を指定すれば、指定された文字列のフィールドに対してカウントの操作を行い、キーにフィールド名、値に条件、という配列で指定することも可能
省略時 = false
‘counterCache’ => true

hasAndBelongsToMany

多対多の関係でモデル同士を結び付けるアソシエーションです。
このアソシエーションは、結合される2つのモデルがある場合に使われます。

使用するにはテーブル名を2つのモデル名を「_」でつないだ形にした中間となるテーブルを作成する必要があります。
Bake にて複数の関係を持ったモデルが複数存在する場合には自動で hasAndBelongsToMany が生成されますが、中間テーブルまでは考慮していない為か(よく分かりませんが)、そのまま使用するとエラーとなってしまいます。

基本的に hasMany と belongsTo で同様の操作ができるので、使わなくても問題無いと思います。
自分は Bake で自動生成された hasAndBelongsToMany はコメントアウトして、hasMany と belongsTo に置き換えています。
その方がデータ構造的にも分かりやすいと思うのですが、これに関しては人によって意見が分かれると思います。

まとめ

データ取得に関しては、アソシエーション機能を利用すれば容易に作成できると思います。
ですが、必要以上にデータを取得してきてしまい、処理速度が異常に長くなってしまうケースがあるので、注意が必要です。
find オプションの recursive で階層を最小限に抑えたり、Containable ビヘイビアを使用したりと、手段は様々です。
Containable ビヘイビアに関しては後に記事にしたいと思います。

簡単に使えるから利用するだけではなく、それ以降の事も考えて利用するのがプログラミングだと考えます。
もっと頑張らねば。。。

Share

  • このエントリーをはてなブックマークに追加

Comment

コメントを残す

*がついている欄は必須項目です。

  • Twitter
  • Facebook
  • Google Plus
  • RSS Feed