christiankuri/laravel-favorite
A Laravel package that adds favorite functionality to Eloquent models. Use a simple trait to let users favorite, unfavorite, toggle, and check favorites on any model, with counts, polymorphic relationships, and query scopes.
Architecture fit. Trait-based bolt-on to Eloquent, consistent with standard Laravel package ergonomics: a service provider, a publishable migration, a Favorite polymorphic pivot model, and two traits (HasFavorites on the actor, CanBeFavorited on the target). It stores all four behaviors (favorite, like, remember, follow) in a single favorites table discriminated by a type column. That's a design choice, not a requirement — one table serves multiple product features, which is convenient for a single "engagement" concept but couples unrelated features' schemas together.
Integration feasibility. Low friction: composer require, publish migration, add traits, done. No frontend assumptions, no queue or event infrastructure required. Works with Blade, Livewire, Inertia, or an API-only build.
Technical risk. High, dominated by staleness. Last release 2019-10-28 and the repository is reported as unknown/unresolved. Expect Composer constraint friction on Laravel 10/11 and PHP 8.2+. Specific risks: (a) no unique index on (user_id, favoriteable_type, favoriteable_id, type) means duplicate rows under concurrent toggle; (b) polymorphic rows orphan on hard delete since foreign keys can't cascade across morph columns; (c) morph-map changes silently break lookups; (d) the type column adds an index dimension that every query path must include.
Key questions.
User, or does it accept the Favoriteable contract for teams/orgs?Stack fit. Good for a Laravel monolith with Eloquent models and no existing engagement table. Poor fit if we already run a polymorphic reactions table or need multi-tenant scoping, since the package's queries are not tenant-aware.
Migration path. Two paths. Greenfield: publish the migration, add the unique index and a type index before the first write, register morph map entries, annotate models with the traits. Brownfield: backfill from an existing likes/follows table into favorites with an explicit type value, dual-write during transition, cut reads over, then drop the old table.
Compatibility. Inspect composer.json constraints before committing. A fork pinned to dev-main with widened illuminate/* ranges is the likely outcome; budget a day for that plus rewriting any deprecated Laravel API calls in the trait.
Sequencing. (1) Spike on a branch covering toggle, count, eager-load, and concurrent toggle. (2) Decide build vs. buy vs. swap against a maintained alternative. (3) If kept, vendor/fork and add the indexes. (4) Ship one feature (e.g., "like" on posts) behind a flag. (5) Expand to other types only after counts are proven correct.
Maintenance. Effectively unowned upstream. Treat it as vendored code: our fork, our CI, our changelog. Budget one to two engineer-days per Laravel major upgrade and near-zero between them, plus a named owner on the engagement team.
Support. No vendor SLA and community issues are likely stale. MIT permits the fork, which is the realistic support model; document that decision so a future engineer doesn't assume upstream fixes will arrive.
Scaling. The table grows at one row per user-action, so it will be among the largest in the schema. Indexes on (favoriteable_type, favoriteable_id, type) and (user_id, type) are mandatory. Read paths should use withCount or denormalized counters, never per-item count(). Toggle is a write per click; a hot post concentrates writes on one index range, so expect lock contention under load and plan a queue or counter cache if engagement volume is high.
Failure modes. Duplicate favorites from races; drift between cached counts and the pivot; orphaned rows after target deletion; silent zero-results if morph aliases change; N+1 explosions on listing pages; a broken fork blocking a framework upgrade.
Ramp-up. Small API surface — roughly favorite, unfavorite, toggleFavorite, isFavorited, favorites, plus query scopes — so a mid-level Laravel developer is productive in half a day. Onboarding must explicitly cover the fork status, the unique index, and the counting convention, since none of that is discoverable from the package README.
How can I help you explore Laravel packages today?