DEV Community

Cover image for Laravel Performance Tips: Count Optimization, Avoiding N+1, and Polymorphic Relationships
Tahsin Abrar
Tahsin Abrar

Posted on

Laravel Performance Tips: Count Optimization, Avoiding N+1, and Polymorphic Relationships

If you've been working with Laravel and MySQL for a while, you've probably hit some common performance pitfalls—like slow queries, N+1 problems, and inefficient use of relationships.

Today, let's walk through some real-world strategies to optimize your Laravel app using:

  1. Count optimizations in Eloquent
  2. Avoiding the dreaded N+1 problem with withCount
  3. Structuring withCount queries
  4. Using polymorphic relationships effectively

Let’s dive in. 🏊‍♂️


1️⃣ Count Optimization in MySQL with Laravel

Many Laravel developers tend to do this:

$post = Post::find(1);
$commentCount = $post->comments()->count();
Enter fullscreen mode Exit fullscreen mode

While this works fine, if you're doing it inside a loop or multiple times, it executes a separate SQL query each time, which adds up quickly. Imagine a page with 10 posts—yep, that’s 11 queries: one for posts and one per post’s comments.

Better approach: Use withCount() to optimize this.


2️⃣ Avoid the N+1 Problem with withCount

The N+1 problem is when one query is used to get the initial dataset, but additional queries are executed for related data. Laravel gives us a simple fix:

$posts = Post::withCount('comments')->get();

foreach ($posts as $post) {
    echo $post->comments_count; // no extra queries here!
}
Enter fullscreen mode Exit fullscreen mode

✅ Result: 1 query instead of N+1.
Laravel adds a comments_count attribute to each post behind the scenes.


3️⃣ Using withCount with Conditions

Want to get counts with specific conditions? You can pass a closure to withCount():

$posts = Post::withCount(['comments' => function ($query) {
    $query->where('is_approved', true);
}])->get();

foreach ($posts as $post) {
    echo $post->comments_count; // Only approved comments
}
Enter fullscreen mode Exit fullscreen mode

This is super useful for dashboards, analytics, or anywhere you need conditional counts.


4️⃣ Polymorphic Relationships: One Table to Rule Them All

What if you want to associate images with multiple models like Post, Comment, and User?

Laravel makes this easy with polymorphic relationships.

Step 1: Create the images table with imageable_id and imageable_type

Schema::create('images', function (Blueprint $table) {
    $table->id();
    $table->string('url');
    $table->morphs('imageable'); // Adds imageable_id & imageable_type
    $table->timestamps();
});
Enter fullscreen mode Exit fullscreen mode

Step 2: Define the relationship in your models

In Image.php:

public function imageable()
{
    return $this->morphTo();
}
Enter fullscreen mode Exit fullscreen mode

In Post.php, Comment.php, and User.php:

public function images()
{
    return $this->morphMany(Image::class, 'imageable');
}
Enter fullscreen mode Exit fullscreen mode

Example Usage:

$post = Post::find(1);
$post->images()->create(['url' => 'example.jpg']);

$user = User::find(1);
$user->images()->create(['url' => 'avatar.png']);
Enter fullscreen mode Exit fullscreen mode

You now have a flexible way to attach images to any model, without multiple image tables.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.