thezeroday/laravel-likeable
A Laravel package that adds likeable behavior to Eloquent models. Attach likes from users or any model, toggle and count likes, check whether a record is liked, and query liked items through simple traits and relationships, with migrations included.
Architecture fit. The package implements the canonical Laravel "likeable" pattern: a trait applied to consumer models plus a polymorphic pivot table (likeable_id, likeable_type, user_id). That is idiomatic Eloquent and low-coupling — it does not impose a service provider hierarchy, event bus, or caching layer on the host application. The consequence is that its architectural value is small: the pattern is roughly 40–80 lines of migration, trait, and relationship methods that most Laravel teams write themselves. Fit is high; differentiation is near zero. Confirm before proceeding whether it assumes integer primary keys, a User model tied to auth()->id(), and no tenancy scoping — each of those is a common mismatch in our target products.
Integration feasibility. Mechanically trivial: composer require, publish the migration, add the trait, call $post->like(). Realistic effort is under a day for a first working slice. The constraint is not effort but evidence — zero stars, zero dependents, and a single release means there is no field validation of any kind. We would be the first production consumer. Treat the dependency as an unreviewed code sample, not a vetted library.
Technical risk. Ranked:
illuminate/support are likely to conflict on our next framework upgrade, at which point we fork or block.Key questions for the maintainer (or for our own source read, if unanswered).
(user_id, likeable_type, likeable_id)?like/unlike idempotent, and does it use transactions or insertOrIgnore?User authenticatables?deleted_at/audit path, or is unlike destructive?Stack fit. Suitable for a Laravel 10/11 monolith or modular monolith on MySQL 8 or Postgres, with standard session or token auth. It is not a fit where an engagement/reaction system already exists, where likes feed a feed-ranking or notification pipeline (the package provides storage, not the eventing we would need), or where multi-tenant isolation must be enforced at the query layer.
Migration path. Do not adopt directly. Sequence:
Internal\Likeable) on day one. This is the single highest-leverage decision — it converts an abandonment risk into a routine code-ownership cost.morphs()/nullableMorphs() indexes, timestamps(), and a deleted_at column if engagement analytics matter.Relation::enforceMorphMap() entry so likeable_type stores stable aliases rather than FQCNs. This prevents silent data corruption if a model is renamed or moved.ReactionService interface so callers never depend on the package's trait signatures and a future swap is a one-file change.Compatibility. Verify the PHP constraint against our 8.2/8.3 baseline and the illuminate/* constraint against our locked framework version before any code is written — a conflict here ends the evaluation in ten minutes. Confirm no reliance on deprecated $dates/str_* helpers.
Sequencing. (1) Two-day spike: install, read source, run the duplicate-like and concurrency probes. (2) Decision gate — fork or discard. (3) Apply to a single low-traffic model behind a feature flag. (4) Instrument write volume and query plans for two weeks. (5) Only then generalize across content types and expose HTTP endpoints, with rate limiting and authorization at the controller layer.
Maintenance. Assume full ownership post-fork. Budget roughly 1–2 engineer-days per framework major upgrade to validate trait and migration compatibility, plus ongoing merge discipline if we track upstream (which, at zero activity, we should not). Total cost of ownership over 24 months likely exceeds writing the feature natively — the honest counterargument to adoption is that this package saves two days and costs four.
Support. No SLA, no community, no issue triage. Escalation path is "read the source." Acceptable only because the source is small enough to fully audit; unacceptable if the team expects vendor-like responsiveness.
Scaling. The polymorphic pivot is write-heavy and grows one row per like — plan for tens of millions of rows on any consumer product. Requirements: a unique composite index on (user_id, likeable_type, likeable_id) for dedupe, a reverse index on (likeable_type, likeable_id) for count-by-parent, and a counter cache column on parent models so read paths never issue COUNT(*). Viral content creates hot-row contention on the counter update; mitigate with queued increments or sharded counters if we see lock waits. Feed rendering must eager-load like state (withExists) to avoid N+1.
Failure modes to monitor. Duplicate likes under concurrency (no unique constraint); orphaned pivot rows after parent deletion (polymorphic relations cannot use foreign keys — schedule a reconciliation job); morph-type drift if the morph map is not enforced; destructive unlikes erasing engagement history; migration filename collision with any future in-house likes table; composer resolution failure at the next Laravel bump.
Ramp-up. Low cognitive load — a mid-level Laravel engineer is productive in half a day. The real cost is review friction: every senior reviewer will ask why we are taking a 0-star dependency for a pattern we can write in an afternoon. Pre-empt that with the fork decision and a short written rationale.
Recommendation. Do not adopt as a live dependency. Treat it as a reference implementation: fork it, harden the migration and indexes, add tests, and own it — or write the ~80 lines natively and skip the supply-chain and abandonment risk entirely. The opportunity score (19.5) and adoption signal (0 dependents) do not support production reliance without those mitigations.
How can I help you explore Laravel packages today?