assada/laravel-achievements
Add achievements and badges to your Laravel app with a simple, database-backed API. Define achievements, track user progress, unlock rewards automatically, and listen for unlock events with clean, customizable Eloquent models.
Verdict: Adopt only as a thin, replaceable persistence layer behind an internal abstraction — or skip it entirely. Do not treat it as a strategy dependency; the opportunity score (0.38) correctly signals commodity infrastructure with no differentiation.
What the package actually is
Achievement model, a locale-aware AchievementDetail model (name/description/image per language), an AchievementProgress model keyed by achiever + achievement + points/achieved, and an Achiever trait that adds achievements(), unlockedAchievements(), inProgressAchievements(), addProgress(), setProgress(), resetProgress(), and unlockAchievement() to the User model. Public API is largely inherited from the upstream Gstt lineage — verify exact signatures on the pinned tag before writing tickets.Architecture fit
Integration feasibility
User, seed achievement rows. A spike lands in days, not weeks.Technical risk
addProgress() on the same (achiever, achievement) row is a classic lost-update race under concurrent requests. Confirm it uses atomic increments or lockForUpdate; if not, wrap every call site ourselves. This is the most likely production incident.Key questions for engineering / the maintainer
addProgress() perform an atomic SQL increment, and is unlock emission inside the same transaction? Is there exactly-once unlocking under concurrent calls?Gstt\Achievements, and are there breaking changes between the last two tags?--ignore-platform-reqs?AchievementDetail for the request locale is missing?Stack fit
AchievementService / RewardGateway interface in our domain layer and let the package be one implementation. Every call site touches our interface, never Gstt\Achievements\* directly. This single decision converts abandonment from a project risk into a one-sprint swap.Migration path (both directions)
addProgress()/setProgress(). Confirm it is idempotent before running it twice — that is the single highest-risk operation in the whole rollout.Compatibility
composer.json (no caret range) and add a CI job that installs on our current Laravel + PHP and runs migration + a smoke test for unlock/progress. Treat any constraint widening as a reviewed change, since minor bumps here are unlikely to be semver-disciplined.Sequencing
Maintenance
Support
Scaling
(achiever_type, achiever_id), achievement_id, and a unique constraint on (achiever_type, achiever_id, achievement_id). Without the unique constraint, double-unlock is a data-integrity bug that surfaces in rewards, not just UI.Failure modes
Ramp-up
How can I help you explore Laravel packages today?