withを使用した様々なデータの取得方法
はじめに
今回は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