本投稿は TECOTEC Advent Calendar 2021 の14日目の記事です。
決済認証システム開発事業部の山﨑と申します。 今年の4月に新卒で入社いたしました。 未経験で入社し、研修と開発で9か月エンジニアリングに関わってきましたが、まだまだできないことばかりで周りの方々にサポートしていただきながらなんとか作業を進めています。
業務に携わるようになってからは約3か月が経ちました。 現在はPHP(Laravel)でAPI開発、AWSの各種サービスを使い医療系コミュニケーションサービスを作っています。 今回はその中で学んだことについて1つまとめたいと思います。
リレーション先のデータを取得する
業務の中で、部署ごとの従業員の一覧を取得する機能を実装することがありました。
- 従業員(employee)
seq | user_id | department |
---|---|---|
1 | 3 | 営業 |
2 | 1 | 総務 |
3 | 2 | 人事 |
- ユーザー(user)
id | name | password | |
---|---|---|---|
1 | 山田太郎 | test@example.jp | password |
2 | 松岡桂 | matuoka@test.co.jp | password |
3 | 小山直美 | koyama@test.jp | password |
- ユーザープロフィール(user_profile)
seq | user_id | birthday | gender |
---|---|---|---|
1 | 2 | 1990-10-13 | 0 |
2 | 1 | 1987-03-28 | 0 |
3 | 3 | 1995-04-02 | 1 |
データは『部署に所属する従業員IDを管理するテーブル』と『従業員のプロフィールを管理するテーブル』に登録されています。 プロフィールも含めてその部署の従業員一覧を取得したい時、下記のようにデータを取得していました。
public function getEmployee (int $employeeSeq) { return Employee::with(‘user’) ->get(); }
$userInfoList = $employeeService->getEmployee ($employeeSeq); foreach ($userInfoList as $userInfo) { $user = $userInfo ->user; $userProfile = $userInfo ->user->userProfile; }
なんとなく既存のものを真似してwith関数を使っておけばいいかなと思っていたのですが、下記のほうが処理が速いということを教えていただきました。
Employee::with(‘user.userProfile’)- >get();
実際にコードを修正しAPIを実行したところ3870msから1811msになっており、先輩すごい!と思ってその場は終えてしまったのですが、どうして速くなったのか、その理由を知りたくなりました。
Laravelのwith関数
そもそも仕組みがわかっていなかったので、with関数について調べてみました。
with関数は、Eager Loadingという機能でクエリの発行回数を減らすことができるそうです。
Eager LoadingとはプロパティとしてEloquentリレーションにアクセスする際にリレーションデータが「遅延ロード」されることだそうです。
つまり、
$userInfoList = Employee::with(‘user.userProfile’)- >get();
この段階ではまだクエリは発行されていない状態
$userInfo ->user->userProfile;
ここでリレーション先のデータを取得しているということだそうです。
foreachで複数回同じ処理を行っても、クエリが発行されるのは最初の1回のみで、2回目以降はセットされたデータを参照しているだけになります。 ネストしたリレーション先をwith関数で指定しないとforeachで回した分だけクエリが発行されるため、遅くなるということでした。
まとめ
とりあえず既存のものを真似してみたり、なんとなく実装してしまっている部分が多いのですが、関数は理解したうえで使用しないとあまり意味がなくなってしまうことがわかりました。 今後は仕組みを理解したうえで実装を進めていけたらいいなと思います。