忍者ブログ

STEP UP BLOG

Home > ブログ > > [PR] Home > ブログ > Laravel > LaravelにおけるEager Loading

LaravelにおけるEager Loading

LaravelはRuby on Railsの思想を強く受け継いでいるPHPフレームワークです。
したがって、DBからのデータ取得もRailsのActiveRecordのような強力なORマッパーが用意されています。
それがEloquentです(エロクワントと読むらしい…)。
もちろん、1対1や1対多の関連付けも対応しています。
例えば、
class Train extends Eloquent {
    public function stations()
    {
        return $this->hasMany('Station', 'train_id', 'id');
    }
}
こんな感じで路線と駅が1対多で関連付けられます。
こう設定すると、
$trains = Train::all();
foreach ($trains as $train) {
    foreach ($train->stations as $station) {
        echo $station->name."¥n";
    }
}
とか出来ます。
しかもデータが必要になるタイミングでSQLが発行されるので、
無駄なSQLが発行されません。

ただし!!
このように便利なORマッパーにも問題がありまして、
その中でも重大なものが「N+1問題」というものです。
この問題は、1対多などの場合で、最初に親を取ってきて、
ループで関連付けられている子のデータを1つずつ取ってくる、
結果としてN+1回SQLが発行される問題です。
「N+1問題」で検索するとわかると思いますが、これはORマッパーの遅延ローディングでは仕方のないことです。

ですがそこはそれ、Railsの先人達はとっくの昔に気付いており対策が施されています。
それがEager Loadingという概念です。
これだと子をまとめて1回のSQLで取ってきてしまいます。
つまり、SQL発行回数がN+1回から2回になり激減。みんな笑顔^^

同様にLaravelでもEager Loadingが取り入れられています。

http://laravel.com/docs/4.2/eloquent#eager-loading

Laravelでは、
$trains = Train::with('stations')->all();
のようにwith()に設定した関連付けを渡すことで、子もまとめて取得してくれます。
1対多でループしている場合、with()を使うと使わないとではSQL発行回数が桁違いに異なってくるので、
是非とも使用していきたい機能です。

そして、これは例えば孫がある場合も使えます。
駅Stationに駅員Memberが関連付けられているとすると、
$trains = Train::with(['stations', 'stations.members'])->all();
のようにピリオドでどんどん深い関連付けまでまとめて取得してくれます。

ORマッパーは適切に使うとビジネスロジックを直感的に扱うことが出来るので便利です。
つまり、ビジネスロジックをSQLに落としこむ苦労が軽減されます。
ORマッパーの発行するSQLに関しても、Eager Loadingなどを使い適切な実装をすればそこまで効率が悪くなることもありません。
その点に意識的になると、Laravelレベルがまたひとつ上がるのかなと思います。
PR

Comment0 Comment

Comment Form

  • お名前name
  • タイトルtitle
  • メールアドレスmail address
  • URLurl
  • コメントcomment
  • パスワードpassword

PAGE TOP