DEV Community

Discussion on: How to implement a simple like system with Laravel

Collapse
 
stleroux profile image
stleroux

Hi,
Love the code. Straight forward and works like a charm.
Is there a way to eager load the likes data on index pages?
I tried adding "likes" to the with query but it doesn't seem to reduce the amount of queries being performed.

Thanks

Collapse
 
bdelespierre profile image
Benjamin Delespierre

Can you show us what you tried?

Collapse
 
bdelespierre profile image
Benjamin Delespierre • Edited

I have just tried

$posts = Post::latest()->with('likes')->take(5)->get();
Enter fullscreen mode Exit fullscreen mode

Using the demo project and Laravel Debug bar.

From 8 queries (with the N+1) problem you mention, I now have only 4 👍

See before and after

Hint: be sure to place the ->with('...') part BEFORE ->get() or ->all().

Collapse
 
stleroux profile image
stleroux

Actually, I was using the code in the index page and doing a @can ('like') and @can ('unlike') which was querying the DB twice per record. I changed the @can ('unlike') for @else and now the duplicate queries are gone.
Thanks

Thread Thread
 
bdelespierre profile image
Benjamin Delespierre • Edited

Yes, that's because the gates in AuthServiceProvider are using User::hasLike :

    public function hasLiked(Likeable $likeable): bool
    {
        if (! $likeable->exists) {
            return false;
        }

        return $likeable->likes()
            ->whereHas('user', fn($q) =>  $q->whereId($this->id))
            ->exists();
    }
Enter fullscreen mode Exit fullscreen mode

You may change it to use a cache, like this (actual working solution) :

    public function hasLiked(Likeable $likeable): bool
    {
        if (! $likeable->exists) {
            return false;
        }

        return $this->likes->contains(
            fn($like) => $like->likeable_type == get_class($likeable) && $like->likeable_id == $likeable->id
        );
    }
Enter fullscreen mode Exit fullscreen mode

It should minimize the number of queries. It's ugly but it works!

Result before with 15 queries (one per @can('like')) and after only 6!