CakePHPの場合
- CakePHP2.0からモデルに追加された機能
- モデルの初期化時ではなく、
find
操作が発生する時にアソシエーション先を読み込むSQLが実行される仕組み
Railsの場合
- ActiveRecordのデフォルトの機能
-
find
した時点ではアソシエーション先は読み込まれず、アソシエーション先の値が必要になった場合に初めてSQLが実行される
両者の違い
CakePHPはfindした時点でアソシエーション先が読み込まれる
- CakePHPでは、デフォルトでは
find
した時点で1次アソシエーション先を全て読み込むSQLを発行する。そのため、めっちゃbelongs_to
してるモデル多いとかだと処理時間が半端なくかかる - そのため、CakePHPのfindには
recursive
というプロパティがあり、これに-1
を指定するとカレントテーブルのみ取得される
Railsはfindした時点では読み込まれず、値が必要になった時に読み込まれる
- デフォルトがCakePHPの
recursive:-1
の状態になっており、カレントテーブルのみ読み込まれる - とてもスマートだと思うが、逆に
n+1
問題という別の問題が発生する場合がある
N+1問題 / Eager Loading とは - Rails Webook http://ruby-rails.hatenadiary.com/entry/20141108/1415418367
N+1問題は1+N問題 - Qiita http://qiita.com/hisonl/items/763b9d6d4e90b1606635
- 例えば、prefecture(県)とshop(店)という1:多の関係があったとして、View側で
shop.prefecture.name
というように県名を取得しようとすると、県とのアソシエーションを取得するためのSQL文が店舗数だけ発行される - 店舗数が100件だったら、最初の全件取得のSQLと合わせて101件(n+1)
- これを解決するには
Eager Loading
という仕組みがあり、@post = Post.all.includes(:user)
のようにすると、必要なアソシエーション先を指定して先に読み込むことが出来る - つまり、CakePHPのrecursiveの動作をRailsは明示的に指定して初めて行うということですね。しかもモデル名をシンボルで指定できるとか、さすがRailsはイケてる。
- ちなみにRailsの場合、
where
メソッドを使うとこれは条件をActiveRecord::Relationオブジェクトを返すだけで、値が必要になった時にSQLが実行されることも遅延ロードと呼ぶらしい?(うろ覚え)
さいごに
- CakePHPはデータが全て配列だから、Viewでアソシエーション先のオブジェクトを取得するとか気のきいたこと出来んのです。全て最初にやっておかないといけない子。
- 最初にn+1問題を聞いた時、CakePHPならループの前に
<?php $prefectureName = $shops[0]['prefecture'];
みたいに書けば解決するかなと考えてしまった。自然に配列脳になってしまってますね。Rails側のコードもたまに読むようにすると、非常に勉強になると思った。