Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Like Laravel Package

cslant/laravel-like

A Laravel package that adds like and favorite behavior to Eloquent models. Use traits and polymorphic relationships to like, unlike, toggle, count, and query liked items with minimal setup and clean syntax.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a drop-in reaction system (like / dislike / love) backed by a polymorphic interaction model, so you can wire it up in about four commands and one trait.

composer require cslant/laravel-like
php artisan vendor:publish --tag="like-config"
php artisan vendor:publish --tag="like-migrations"
php artisan migrate

Where to look first:

  1. config/like.php — the two keys that matter are like.users.model (your App\Models\User) and like.interaction_model (the package's default interaction model, or your own subclass). In v2.1.0 the fallback resolution was fixed, so leaving either key null or unset now correctly resolves to the package defaults instead of blowing up.
  2. The published migration — check the table name and the morph columns before you build queries against it.
  3. The HasLike / HasLove traits — this is where day-to-day usage lives.

Add the traits to the models that participate:

use Csant\LaravelLike\Traits\HasLike;
use Csant\LaravelLike\Traits\HasLove;

class Post extends Model
{
    use HasLike, HasLove;
}

First use case — a like button:

use Csant\LaravelLike\Facades\Like;

Like::toggle($post, auth()->user());     // idempotent toggle, ideal for a button
Like::like($post, auth()->user());       // explicit
$post->likes()->count();

If you only need reactions, stop here. Everything below is about scaling that up.

Implementation Patterns

Facade for controllers, traits for models. Like and Love facades both proxy the same LikeManager. Use the facade in controllers, jobs and listeners where you have no model context; use the model traits in scopes, accessors and Blade to keep call sites terse. Don't mix both styles in the same class — pick one so toggle semantics stay obvious.

Toggle is the right default for UI. A like button should call Like::toggle($model, $user) and then read back state, rather than branching on hasLike() first. That removes a race class and halves your queries.

Model-centric actions. With HasLike / HasLove on the interactable model you can express intent from the subject side:

$post->like($user);
$post->unlike($user);
$post->toggleLike($user);

This reads better inside domain services and keeps the controller free of facade imports. The same pattern applies with HasLove for the love variant, including love, unlove and unDislike paths.

Separate like from love as distinct reaction types. dislike and unDislike are their own reactions, not negative likes. Model them as separate counters in your UI (likes_count, dislikes_count, loves_count) rather than summing into a single score, otherwise toggle will surprise you.

Query with scopes, not collections. The LoveScopes trait (renamed from LoveScope in this release) gives you query-level filtering. Use it for feeds — Post::whereLovedBy($user) style lookups — instead of ->get()->filter() on a relation.

Custom interaction model. If you need extra columns (e.g. source, ip), subclass the package interaction model, point like.interaction_model at it, and extend the published migration. Keep the morph columns and indexes intact.

Cleanup via ForgetsInteractions. v2.1.0 deduplicated the forgetInteractions* methods into this shared trait. Hook it into a deleting model event, or call it explicitly when purging a user, so orphaned interaction rows don't accumulate.

Enum-driven reads. The package ships an enum for reaction types (covered by the new Pest suite). Cast the type column to it in your own queries and comparisons — avoids magic strings like 'love' drifting out of sync with the package.

Testing. The package now ships testbench + Pest coverage for the manager, facades, traits, enum, helper and config. Mirror that in your app: use RefreshDatabase, bind a fake User in like.users.model via config()->set() in setUp, and assert on the interaction table rather than on counts where possible.

Gotchas and Tips

Config cache after changing like.users.model. This is the single most common failure. Setting the key in .env or the config file and seeing the old model still in use means cached config: run php artisan config:clear. In production, always config:cache after deploy.

The fallback fix changes behaviour for unset keys. Pre-2.1.0, leaving like.interaction_model or like.users.model unset produced inconsistent resolution. After upgrading, unset keys resolve to package defaults. If your app relied on the old broken behaviour — for example you intentionally left it null because resolution was failing silently — re-verify your tests against the interaction table.

LoveScope → LoveScopes is a rename. Not a deprecation shim, an outright rename. If you used the trait directly, extended it, or aliased it, update the import. Code that only calls the scopes through HasLove is unaffected.

ForgetsInteractions deduplication. If you overrode forgetInteractions, forgetInteractionsByUser or similar in your own model, the shared trait may now collide with your method signatures. Check for Trait method ... has not been applied or silent precedence issues after upgrading.

Facade aliases. Registering Like and Love in config/app.php aliases is optional; the class imports work fine. But if you have a Like model or a like() helper already in your app, you'll get collisions — the package also exposes a helper function, so check composer.json autoload files and your own helpers.php before declaring both.

Morph maps bite you on model renames. Interactions store the morph alias, not the class name. If you introduce Relation::enforceMorphMap(), existing rows still hold FQCNs. Either add the mapping before you have production data, or write a migration that rewrites the interactable_type column.

Counters and N+1. $post->likes()->count() in a loop is a query per row. Use withCount(['likes', 'loves']) on the index query and cache the aggregate on the parent model if the feed is hot.

toggle is not unique-safe under concurrency. Two simultaneous clicks can attempt the same insert. Add a unique index on (interactable_type, interactable_id, user_id, type) in your migration and catch the constraint violation, or wrap the toggle in a transaction with a row lock.

Debugging missing reactions. When a like "doesn't stick", check in this order: trait present on the model, config cache, morph type stored in the row, and finally the users.model key. The forgetInteractions path is also worth logging during development, since a careless deleting hook can wipe reactions you expected to keep.

Extension point. Rather than forking, resolve LikeManager out of the container and decorate it, or listen for the model events the traits fire. That keeps you upgrade-safe through minor releases like 2.1.x.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core