2018/11/27更新

[Laravel] Eloquentのwith関数で、子テーブルの情報をまとめて取得する(Eager Loading)

このエントリーをはてなブックマークに追加            

こんにちは、@yoheiMuneです。
Laravelで実装していて、親テーブル取得時に関連する子テーブルの一覧も合わせて取得したいことがあると思います。そんな時に便利なwith関数について、今日はブログを書こうと思います。



目次




何がしたいのか

例えば、投稿を表現するpostsテーブルと、その投稿内容(=1〜N枚の画像)を表現するpost_contentsテーブルがあるとします。特定のPostを取得した際に、それにひもづくPostContentsを一覧で取得したい場合に、どうしたら良いかを記載しています。



手順1:モデル定義で関連(hasMany)を表現する

まずは、PostsPostContentsのモデルを定義し、hasManyを用いて関連も合わせて定義します。
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // PostContentsへの関連を定義します.
    public function contents()
    {
        return $this->hasMany('App\PostContent');
    }
}
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class PostContent extends Model { }
これで、それぞれのモデルと、モデル間の関連を定義できました。



with関数を用いて子テーブルから一覧を取得する

Eloquentのwith関数を用いて、Postを取得しつつ、合わせて関連するPostContentの一覧も取得します。
ここではコントローラーで記載していますが、記載箇所は任意です。
<?php

namespace App\Http\Controllers;

use App\Post;
use App\PostContent;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index()
    {
        // withを用いて、関連するPostContentsも一緒に取得する.
        $post = Post::with('contents')->find(1);
        return $post;
    }
}
これで、Postを取得しつつ、一緒にPostContentの一覧も取得できました。以下のようなJSON形式を取得することができます。
{
    "id": 1,
    "body": "投稿内容です。こんにちは!",
    "contents": [
        {
            "id": 100,
            "post_id": 1,
            "file_url": "https://xxxxxxxx/1bd9.png",
        },
        {
            "id": 101,
            "post_id": 1,
            "file_url": "https://xxxxxxxx/dl1d.png",
        },
        {
            "id": 102,
            "post_id": 1,
            "file_url": "https://xxxxxxxx/okw4.png",
        }
    ]
}
上記のようにcontentsというフィールドの中に配列で、PostContentが格納されています。素敵ですね。



with関数について詳しく

このwith関数は、Eager Loading という機能で、発行するクエリを少なく、効率的にDBからデータを取得する仕組みです(公式ドキュメントはこちら(英語))。

with関数は、以下のような使い方ができます。
// 1つだけ指定(今回紹介したのがこちら).
$books = App\Book::with('author')->get();

// 複数指定.
$books = App\Book::with(['author', 'publisher'])->get();

// ネストした先も取得.
$books = App\Book::with('author.contacts')->get();

// 指定したカラムのみ取得(注意:IDは必ず含める必要がある).
$users = App\Book::with('author:id,name')->get();

// 条件指定を追加したい場合.
$users = App\User::with(['posts' => function ($query) {
    $query->where('title', 'like', '%first%');
}])->get();

// ソートしたい場合.
$users = App\User::with(['posts' => function ($query) {
    $query->orderBy('created_at', 'desc');
}])->get();
また、上記の場合はコードを実行したら必ず子テーブルも取得しますが、load関数を用いると、特定の条件の場合にのみ子テーブルを取得することもできます。
// 遅延読み込み.
// 特定の条件の場合のみ、Eager Loading を使いたい場合には、「load」関数を利用します.
$books = App\Book::all();
if ($someCondition) {
    $books->load('author', 'publisher');
}

// 以前に取得していない場合に限り、Eager Loading を実行する.
$book->loadMissing('author');
色々と便利ですね〜。もっともっと学んでいきたい今日この頃です。



最後に

Laravelを複数の案件に投入していますが、まだまだ学ぶことが多いです。少しずつブログにアウトプットできたらと思います。

最後になりますが本ブログでは、PHP、フロントエンド、Python、サーバー、インフラ、Swift、Node.js、Java、Linux、機械学習、などの技術トピックを発信をしていきます。「プログラミングで困ったその時の、解決の糸口に!」そんな目標でブログを書き続けています。ぜひ、本ブログのRSSTwitterをフォローして貰えたら嬉しいです ^ ^

最後までご覧頂きましてありがとうございました!





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。