Laravel – クエリービルダーとリレーション

概要

Laravelでデータベースの大量のデータを扱う場合、クエリービルダーでSQLを発行してDBMS側でデータを絞り込んだ方がフレームワークの負担が少なくなる。

一方でリレーションに基づいた記述は、可読性やメンテナンスの面で有利な面を持つ。

そこで、クエリービルダーとリレーションの組み合わせについて試してみた。

  • モデルクラスのall()メソッドとwhere()のチェーンによる絞り込みは、DBMSの機能を使っておらず非効率ではないか
  • モデルクラスで記述するクエリービルダーは、DBMS側でデータを絞り込んだ結果をフレームワークで扱うので効率的なようだ
  • DBファサードで記述するクエリービルダーは、得られるデータがモデルのインスタンスではなく連想配列になるため、モデルで定義したリレーションが使えない

準備

クエリービルダーで使ったものと同じデータを準備する。

all()とwhere()の組み合わせ

以下の様なコントローラーのアクションでデータを絞り込んでビューに渡す。

ビュー側では$ordersを受け取って、リレーションに基づいて表示する。

ブラウザーでの表示は以下のとおり。

  1. 2020-06-05 00:00:00: customer2
    • e-mail: customer2@mail.com
    • item: rubber sheet
  2. 2020-06-06 00:00:00: customer2
    • e-mail: customer2@mail.com
    • item: plastic plate

 

ただしこの場合の動作は、DBMSから全データをall()で取得した後にcustomer_idで絞り込んでいる。以下の様にtinkerで確認してみると、where句を含むSQLは発行されていないようだ。

実際、get()なしで直接モデルのインスタンスが得られている。tinkerで確認してみると、$ordersOrderクラスのインスタンスを要素とするコレクションとなっている。

モデルのメソッドによるクエリービルダー

コントローラーのアクションで、以下の様にOrderモデルからクエリービルダーを構成する。

ビューは上と同じ内容で、結果も同じように表示される。

tinkerで確認するとSQLが構成されて値もバインドされているので、DBMS側でデータが絞り込まれているようだ。

get()の結果得られるデータは上記のall()where()と同じで、Orderクラスのインスタンスを要素とするコレクション。

DBファサードによるクエリービルダー

クエリビルダ―をモデルクラスからではなくDBファサードから記述した場合。

同じビューを使った場合、以下のようなエラーがブラウザーに表示される。

tinkerで確認すると、SQLは同じように発行されバインディングも同じ。

ただし、get()で得られるデータが上と異なる。全体は配列だが、要素がOrderクラスのインスタンスではなく、Orderの属性をキーとする連想配列となっている。

このため、$ordersの要素からリレーションを定義したはずのcustomerを呼び出そうとしても、「定義されていない属性」としてエラーが発生する。

そこでビュー側でリレーションを用いず各要素を連想配列として取り出してみる。

こうすると以下の様に$ordersの抽出結果が表示される。

  1. id: 2
  2. customer_id: 2
  3. ordered_at: 2020-06-05 00:00:00
  4. item: rubber sheet
  1. id: 3
  2. customer_id: 2
  3. ordered_at: 2020-06-06 00:00:00
  4. item: plastic plate

 

ただし各要素のorderに関係づけられたcustomerはこのままでは得られないため、クエリーレベルで2次元配列として関連する全モデルの属性をカラムとして取り出す必要がありそうだ。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です