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

Likeable Laravel Package

x-laravel/likeable

A Laravel package that adds like and dislike behavior to Eloquent models via a reusable trait, letting you attach reactions to posts, comments, or any model and query counts and liked items through a simple API.

View on GitHub
Deep Wiki
Context7

Getting Started

This package adds polymorphic "liking" to any Eloquent model. First look at the two contracts and two concerns — they define the entire surface area:

  • Contracts\CanLike + Concerns\Liker — for the model that does the liking (usually User).
  • Contracts\HasLikes + Concerns\Likeable — for models that can be liked (e.g. Post, Comment).

Minimal setup:

  1. composer require the package and publish its config + migrations.
  2. Run php artisan migrate.
  3. Add Liker to your User model (and implement CanLike).
  4. Add Likeable to any likeable model (and implement HasLikes).
  5. First use case: $user->like($post) then render $post->isLikedBy($user) or eager-load Post::withLikedBy($user)->get().

One model can implement both concerns, so a User can like posts while also being likeable by other users.

Implementation Patterns

Toggling from a controller. like() is idempotent — calling it again returns the existing like rather than duplicating. Pair with hasLiked() for a toggle endpoint:

$user->hasLiked($post) ? $user->unlike($post) : $user->like($post);

Avoiding N+1 on index pages. Do not call hasLiked() per row. Use the scope and accessor instead:

$posts = Post::withLikedBy($user)->paginate();
// per row: $post->isLikedBy($user)

Listing and counting. likes() and likers() traverse the polymorphic relation; givenLikes() and likedItems() list what a liker has touched. Use likes()->count() for like counts, but eager-load likes when you need the count alongside the collection to avoid a query per row.

Events for notifications. Events\Liked and Events\Unliked fire after commit, so they are safe to hook listeners (notifications, activity feeds, websockets) without risking a rollback firing a side effect. Registered listeners automatically respect the transaction boundary.

Pruning. The package registers a model:prune job. Set the frequency and chunk size in config, and make sure your scheduler runs model:prune (or the package's scheduled command) — otherwise orphaned likes from hard-deleted rows accumulate. Cleanup on model/liker delete is wired up, but soft-deletes and raw deletes bypass it, so pruning is the safety net.

Polymorphic flexibility. Because likers are polymorphic, a Team, User, or even a Bot model can like. A given liker may like a given model only once — the uniqueness is enforced by the package, so you don't need your own guard.

Config overrides. The connection, table, and default liker model are configurable. Change these before migrations run in production, or write a migration to rename the table if you're changing later.

Gotchas and Tips

  • Idempotency is a feature, not a bug. like() returning an existing like means you can call it defensively; don't write if (!hasLiked()) like() unless you need the branch for other reasons.
  • isLikedBy() needs the liker instance. It's a per-model check; for lists, always use withLikedBy(). Mixing them in a loop is the most common cause of query blowups.
  • Events fire after commit. If you queue listeners, confirm your queue connection handles after-commit dispatch correctly — a sync queue still runs post-commit here, but a misconfigured afterCommit on the job can double-handle.
  • Morph maps. If you use Relation::enforceMorphMap(), the likeable/liker types stored by the package must match your aliases. Add the aliases before seeding likes, or existing rows won't resolve.
  • Pruning config. Tune chunk size for large like tables; the default is fine for most apps but can lock tables on millions of rows during scheduled runs. Consider withoutOverlapping() on the scheduled task.
  • Cascade deletes vs. pruning. The package cleans likes when their model or liker is deleted through Eloquent. DB::table()->delete() or raw SQL bypasses this — rely on pruning for those cases or add DB-level foreign keys with cascade.
  • No self-like guard. Nothing prevents a user liking their own model. Add a policy or a liking gate if that matters to your product.
  • Connection config. If likes live on a separate connection, the polymorphic joins still work, but withLikedBy() subqueries run against that connection — verify it's reachable in read replicas.
  • Extension points. Override the default liker model in config if your "current liker" isn't User; bind CanLike implementations through your auth guard. The Liked/Unliked events are the cleanest place to add analytics without touching package internals.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin