Web制作、Web開発の歩き方

バックエンドのスタンダード:Laravel入門

■第7話:高度なEloquentの使用法

(最終更新日:2024.06.30)

フレームワークのイメージ
この記事は7分で読めます!
(絵が小さい場合はスマホを横に)

「ORMを使いこなそう!」

EloquentはLaravelのORM(オブジェクトリレーショナルマッピング)であり、データベース操作を簡素化する強力なツールだ。 この回では、Eloquentの高度な使用法に焦点を当て、クエリビルダの強化、モデルスコープの活用、パフォーマンスの最適化、 Eloquentイベントの利用などについて詳しく解説する。 より効率的で効果的なデータ操作を行い、アプリケーションの性能と保守性を向上させる方法を学ぼう。


1.高度なクエリビルダ

Eloquent ORMは、Laravelのデータベース操作を簡素化するための強力なツールだ。 高度なクエリビルダの使用方法を理解することで、複雑なデータベースクエリを効率的に実行できる。

1.1 スコープの使用
Eloquentでは、モデルのスコープを定義して再利用可能なクエリロジックを作成できる。 下記では、activeフィールドが1のユーザーのみを取得できるものと、 typeが引数で指定した値であるものを取得できるロジックを定義している。

スコープの定義(上部のClass)と使用(下2行)

1.2 リレーションの動的スコープ
リレーションのスコープを使用して、リレーションに基づくクエリを簡素化できる。 下記は1対多のリレーションを持つモデルに対しての操作である。具体的にはidが1の投稿、 そして承認されたもの(approvedフィールドが1)を取得している。

1対多の動的スコープ

1.3 高度なクエリビルダのメソッド
Eloquentクエリビルダは、データベースクエリの構築を簡単にする多くのメソッドを提供する。 下記のクエリでは、ユーザーの数をカウントしたり、最高金額を取得している。

集計クエリ

下記は条件付きクエリで、activeカラムが有効な場合、active=1(アクティブ)なユーザーだけを取得し、 activeカラムが有効でない場合、クロージャーを無視し、全てのユーザーを取得する。

条件付きクエリ

また、下記はサブクエリを有するもので、内側がサブクエリにあたる。 ユーザーid1の最近の投稿よりも(サブクエリ)後(本クエリ)の投稿を全て取得するという命令になる。 最近よりも後の投稿は存在しないので、この場合は取得できるデータはない。

サブクエリを用いた書き方


1.4 リレーションのロード
Eloquentでは、リレーションのデータを効率的にロードする方法を提供する。 下記の上は遅延ロードでN+1問題(for文で都度クエリを実行)が発生する。 下の方のやり方だと、即時ロードが可能なため、2回のクエリで済む。 詳しくは後ほど説明する。

遅延ロードと即時ロード

ここでは詳しく説明しないが、他にも、Eloquentにはカスタムクエリビルダというものがあり、 独自のクエリメソッドを追加でき、そのクエリを再利用することが可能になる。

高度なクエリビルダの使用方法を理解することで、Eloquentの強力な機能を最大限に活用できるようになる。 スコープ、リレーションの動的スコープ、集計クエリ、条件付きクエリ、リレーションのロード、 カスタムクエリビルダなどを活用することで、複雑なデータベース操作を効率的に実行できる。 これにより、コードの再利用性が向上し、アプリケーションのパフォーマンスも向上する。

2.モデルスコープの活用

モデルスコープを使用すると、Eloquentモデルのクエリをより簡潔かつ再利用可能にすることができる。 スコープは、頻繁に使用するクエリロジックをモデルメソッドにカプセル化し、簡単に呼び出せるようにするための仕組みだ。

2.1 基本的なスコープの定義と使用
スコープを定義するには、モデル内にscopeプレフィックスを付けたメソッドを作成する。 このメソッドは、Builderインスタンスを受け取り、クエリを変更する。 基本的な使い方は1.1で説明した通りなので、そちらを見てほしい。

2.2 複数のスコープの組み合わせ
スコープはチェーンして使用することができる。 これにより、複数の条件を簡単に組み合わせることができる。 下記では、admin権限があるユーザーでactiveなものを取得している。

複数スコープをチェーンする

2.3 グローバルスコープ
グローバルスコープは、すべてのクエリに自動的に適用されるスコープだ。 特定のモデルに対してグローバルスコープを定義することで、モデルのクエリに常に適用される条件を設定できる。

ユーザー登録ビュー

また、下記の命令で一時的にグローバルスコープを無効にすることができる。

グローバルスコープの無効化

2.4 動的スコープ
動的スコープを使用することで、柔軟なクエリを構築することができる。 例えば、条件に基づいてスコープを適用するかどうかを決定することができる。 下記では、statusがactiveなものだけを取得するか、全てのstatusを取得するかを後から選んでいる。

動的スコープの定義と使用

3.パフォーマンスの最適化

Eloquent ORMは強力なデータベース操作機能を提供する。 そして、パフォーマンスの最適化を行うことで、アプリケーションの効率性をさらに向上させることができる。 以下は、Eloquentを使用する際のパフォーマンス最適化の方法について詳しく説明する。

3.1 N+1クエリ問題の回避
N+1クエリ問題とは、関連するデータを取得する際に大量のクエリが発生する問題だ。 これを回避するためには、早期ロードを使用する。 下記の最初の方法(遅延ロード)では、「SELECT * FROM users;」を実行した後で、 「SELECT * FROM posts WHERE user_id = 1;」「SELECT * FROM posts WHERE user_id = 2;」と存在するユーザーの数だけSQLを実行してしまう。 これを解消するには、その下の書き方で行う。これにより「SELECT * FROM users;」を実行した後で、 「SELECT * FROM posts WHERE user_id IN (1, 2, 3, ...);」と計2回のSQL実行だけで終わらすことができる。 これにより、パフォーマンスを大幅に改善することができる。

早期ロード(下)の使用

3.2 クエリのキャッシュ
クエリ結果をキャッシュすることで、同じクエリを再実行する際のパフォーマンスを向上させることができる。 ここでの60は60分キャッシュが有効であることを示している。 ユーザーデータの変更は頻繁ではないので、もう少し長くても構わない。

クエリのキャッシュ

3.3 インデックスの活用
データベースのインデックスを適切に使用することで、クエリの実行速度を向上させることができる。 インデックスは頻繁に検索や結合に使用されるカラムに対して設定する。 下記ではusersテーブルのemailフィールドに対してインデックスを設定している。 他にも「$table->unique('email');」というuniqueなフィールドに対するインデックスを設定する方法などがある。

インデックスの使用

3.4 バルクインサート
バルクインサート(Bulk Insert)では、一度に複数の行を挿入するためのSQL文が実行される。 これにより、パフォーマンスが向上し、複数の個別のINSERT文を使用するよりも効率的だ。 下記のように記述すると、一回のINSERT文で全てのインサートを実行してくれる。

バルクインサートによる一括実行

3.5 プライマリーキーの使用
クエリでプライマリキーを使用することで、データベースのパフォーマンスを最適化できる。 プライマリキーは一意でインデックスが付けられているため、検索が高速になる。 「$user = User::find(1);」という検索はidで検索しているため、非常に速い。

3.6 必要なカラムのみのロード
不要なカラムを避け、必要なデータのみを取得するようにクエリを最適化することで、データベースの負荷を軽減できる。 「$users = User::select('name', 'email')->get();」というように欲しいカラムのデータだけに絞る。

Eloquentを使用する際にパフォーマンスを最適化するための方法について説明した。 N+1クエリ問題の回避、クエリのキャッシュ、インデックスの活用、バルクインサート、プライマリキーの利用、不要なデータのロードを避けるなど、 これらの最適化手法を組み合わせることで、Laravelアプリケーションのパフォーマンスを大幅に向上させることができる。 これにより、ユーザーに対して検索や登録などの待ち時間を減らすことができる。

4.Eloquentイベント

Eloquentイベントは、モデルのライフサイクルにおける特定のアクションにフックするための仕組みだ。 これにより、モデルの作成、更新、削除などの際に特定の処理を実行することができる。

4.1 Eloquentイベント
Eloquentモデルは、以下のような多くのイベントをサポートしている。 これらのイベントに対してリスナーを登録することで、特定のアクションが発生したときに処理を実行できる。

  1. retrieved:モデルがデータベースから取得された後に実行される
  2. creating:モデルがデータベースに保存される前に実行される
  3. created:モデルがデータベースに保存された後に実行される
  4. updating:モデルが更新される前に実行される
  5. updated:モデルが更新された後に実行される
  6. saving:モデルが保存される前に実行される(作成および更新時)
  7. saved:モデルが保存された後に実行される(作成および更新時)
  8. deleting:モデルが削除される前に実行される
  9. deleted:モデルが削除された後に実行される
  10. restoring:ソフトデリートされたモデルが復元される前に実行される
  11. restored:ソフトデリートされたモデルが復元された後に実行される

4.2 イベントリスナーの定義
Eloquentイベントリスナーを定義するには、モデルのbootedメソッド内でイベントリスナーを登録する。 下記では、savingイベントとsavedイベントを用いて、 モデルが保存される前にハッシュ化を行い、後にウェルカムメールを送るイベントを登録している。 フォームによるユーザー登録は、定型的な一連の処理を行うため、イベント化すると良い。

イベントリスナーの定義

Eloquentイベントを使用することで、モデルのライフサイクルにフックして特定の処理を実行することができる。 また、ここでは記述しなかったが、標準のEloquentイベントに加え、カスタムイベントを定義することで、さらに柔軟な処理を実装することも可能だ。 このようなイベントを用いると、コードの可読性と再利用性が向上し、メンテナンス性の高いアプリケーションを構築することができる。

5.まとめ

今回はEloquentの高度な使用法について学んだ。 クエリビルダを活用して複雑なデータ操作を簡単に行う方法や、モデルスコープで再利用可能なクエリを作成する方法を紹介した。 また、パフォーマンス最適化のためのテクニックや、Eloquentイベントを利用してモデルのライフサイクルにフックする方法も学んだ。 これらの技術を駆使することで、効率的で保守性の高いアプリケーションを構築できるようになる。

▼参考図書、サイト

 「改定2版 速習Laravel」 山田祥寛 WINGSプロジェクト
 「1週間で基礎から学ぶLaravel入門」 Minatomi