![見出し画像](https://assets.st-note.com/production/uploads/images/55197709/rectangle_large_type_2_c1e16906e7eac8fbfed37f4684b23673.png?width=1200)
[laravel8] 再びN+1問題を解決
前回の記事でブロガーごとの記事の一覧を表示することができました。
しかし数カ所にN+1問題が発生してしまっているので1つずつ修正していきます。
Clockwork
TOPページのviewにも使っている
resources/views/posts.blade.php
のviewにブロガーの名前を表示するようにしたためにN+1問題が起きています。
上の記事でも紹介したClockworkを使い確認してみましょう。
5つの記事を取得するのに7つのクエリが実行されています。
問題の箇所
routes/web.php
$posts = Post::latest()->with('category')->get();
カテゴリはwithを使いeager loadingされているのですが、authorはlazy loadingになってしまっていることが原因です。authorもeager loadingするために次のように変更します。
$posts = Post::latest()->with(['category', 'author'])->get();
実行されたクエリの数を確認します。
7つだったクエリが3つに減っていますね。
モデルのリレーションのN+1
ブロガーの記事一覧のページを見てみましょう。
/authors/ebert (http://localhost:8000/authors/ebert)
johnの5つの記事を取得するのに12のクエリを実行しています。
ソースコードを確認してみると
routes/web.php
Route::get('/authors/{author:username}', function (User $author){
return view('posts', [
'posts' => $author->posts
]);
});
リレーションを使って記事を取得しています。
このような場合はloadメソッドを使用しeager loadingすることが可能です。
Route::get('/authors/{author:username}', function (User $author){
return view('posts', [
'posts' => $author->posts->load(['category', 'author'])
]);
});
ページをリロードしてみると
12のクエリは4つまで減ることができました。
しかしこのようにeager loadするために、様々な箇所で
['category', 'author']
と記述していくのは大変です。
他の方法を紹介します。
with プロパティ
モデルのクラスでwithプロパティを設定することでeager loadingを設定することができます。実際にモデルのクラスに設定してみます。
app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $guarded = [];
protected $with = ['category', 'author']; // withプロパティ追加
public function category()
{
return $this->belongsTo(Category::class);
}
public function author(){
return $this->belongsTo(User::class, 'user_id');
}
}
protected $with = ['category', 'author'];
を設定することで、記事を取得する度に毎回categoryとauthorをデフォルトでeager loadingします。
ただこれだとcategoryやauthorが必要ない時でも自動的に取得してきてしまいます。
例えばauthorが必要ない時は
App\Models\Post::without('author')->first();
withoutメソッドを利用することでeager loadされないようになります。
最後に
今回のソースコードは