👀

withを使用した様々なデータの取得方法

2021/10/18に公開

はじめに

今回はLaravelのEager Loadingの中から、with()を使用したデータ取得方法に関してまとめました!
with()にも様々な手法がありますが、その中でも私が実務で実際に使用したものを解説していきます。

なぜwithを使用するのか

ネストしたテーブルのデータを取得するとき、withを使用せずにデータを取得すると、Laravelはデータの数だけクエリを発行します。
しかしwith()を使用することで、1回のクエリで全てのデータを取得することができるようになっています。

ブログの投稿サイトを例に、実際に発行されるクエリ数で比較してみたいと思います。

BlogテーブルとArticleテーブルがあり、それぞれ下記のような構成になっているとします。

Blogsテーブル

$table->id();
$table->string('title');

Articlesテーブル

$table->id();
$table->tinyInteger('blog_id');
$table->string('title');
$table->text('body');

Modelは以下のようにします。

Blog.php

public function Articles()
{
return $this->hasMany(Article::class);
}

Article.php

public function Blog()
{
return $this->belongsTo(Blog::class, 'id', 'article_id');
}

ここから、blogに紐づいているarticleデータのタイトルを取得するとします。
withを使用せずに書くと下記のように書けます。

DB::table('blogs')->join('article', 'blog.id', '=', 'article,blog_id')
                  ->select('article.title')
                  ->get();

発行されるクエリは以下のようになります。

select `articles.title`, `article.title` from `articles` where `blogs.id` = '1'
select `articles.title`, `article.title` from `articles` where `blogs.id` = '2'
select `articles.title`, `article.title` from `articles` where `blogs.id` = '3'

このように、取得するデータの数だけクエリを発行すします。
データが3件など少ない場合はいいのですが、多くなってくると処理速度の低下に繋がり、ページを開くのにかなりの時間を要する場合があります。

そこで、クエリ数を減らすために利用できるのがwith()となっています。
with()を使用し、同じデータを取得すると以下のようになります。

DB:table('blogs')->with('articles')->get();

この時、発行されるクエリは以下のように書くことができます。

select * from `articles` where `blogs`.`id` in ('1', '2', '3')

先ほどはデータの数だけ発行していたクエリ数が、1回のクエリでデータを取得することができました。

ちなみに、with('articles')のarticleは先ほどBlog.phpで指定した関数になります。

このように、with()を使用することで発行するクエリ数を減らすことできるため、ネストしたデータを使用する際にはwithを使用することが推奨されます。

そして今回は、with()を使用した様々なデータ取得方法に関しても解説していきたいと思います。

withを使用した様々なデータ取得方法

では早速、いくつかご紹介していきたいと思います!

取得するデータを選択する

ネストした先のデータを選択(select)した上でデータを取得したい場合

DB:table('blogs')->with('articles:blogs_id,title')->get();

このように、:の後に取得したいデータを指定することでデータを選択した上で取得することができます。
また、この時にもしblogs_idが必要ではない場合でも、外部キーは必須になるため注意が必要です。

また、Blog.phpのリレーションで複数のデータを取得したい場合は、1度に書くことも可能です。
例えば、Blog.phpにarticles()の他に以下のような記載があったとします。

public function hoge()
{
return $this->hasMany(Hoge::class);
}

articleテーブルのtitleとhogeテーブルのtitleのどちらからもデータを取得したい場合は、以下のように書くことができます。

DB:table('blogs')->with('articles:blogs_id,title', 'hoge:blogs_id,hoge')->get();

さらにネストしたデータを取得する

ここに、新しくcommentsテーブルを追加し、それぞれ以下のようにしたとします。

commentsテーブル

$table->id();
$table->tinyInteger('article_id');
$table->text('comment');

Article.php

public function Comments()
{
return $this->hasMany(Comment::class);
}

Comment.php

public function Article()
{
return $this->belongsTo(Article::class, 'id', 'article_id');
}

この時、commentsテーブルを取得したい場合は、.でつなぐことにより、データを取得することができます。

DB:table('blogs')->with('articles.comments')->get();

それぞれ、Blogモデルに書いたArticles()とArticleモデルに書いたComments()を記しています。

また、:を使用すれば、commentsテーブルのデータを選択(select)した上でデータを取得することもできます。

DB:table('blogs')->with('articles.comments:article_id,hoge')->get();

リレーション先のデータ数をカウントする

ブログに書かれている記事数を確認したい時には、withCount()を使用すれば簡単に記事数を表示することが可能です。

$blogs = DB::table('blogs')->withCount('articles')->get();

取得した記事数を表示する時には

$blogs->articles_count;

のように、関数名_countと書くだけで件数を表示することが可能です。

また、この時に表示するデータを選択した上で件数を表示することも可能です。
articlesテーブルにcreated_atという記事を投稿した日付が記録されるカラムがあったとします。
この時、昨日から今日にかけて書かれた記事のみを取得したい場合は、以下のように書くことができます。

$today = Carbon::now();
$yesterday = Carbon::yesterday();

$blogs = DB::table('blogs')->withCount(['articles' => function($q) use ($today, $yesterday) {
            $q->whereBetween('created_at', [$yesterday, $today]);
        }
        ]);

latest()を使用すればデータが新しい順に並び替えることも可能です。

$blogs->articles_count->latest();

さいごに

今回はwith()を使用したデータの取得方法に関してまとめてみました!
公式マニュアルを読むと、他にもたくさんの手法があるようなので、便宜に合わせて使用していきたいと思います。

最後まで読んでいただきありがとうございました!

Discussion