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

Level Up Laravel Package

cjmellor/level-up

Level Up is a Laravel gamification package for adding experience points, levels, achievements, and badges to your app. Award XP for user actions, define level thresholds, unlock achievements, and track progress with simple traits, models, and events.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Package: cjmellor/level-up v3.0.0 — a Laravel gamification layer (experience points, levels, tiers, achievements, challenges, streaks) that now also ships a metric-driven leaderboard, snapshot engine, and leagues/divisions system.

Architecture fit

  • v2 was a write-side concern: points in, level/tier/achievement records out, bolted onto the host User model via traits. v3 keeps that surface but adds a read-side ranking engine — Leaderboard::by(), SQL window-function ranks, time-windowed scoring off an experience_audits ledger, periodically persisted snapshots, and cohort-based league standings.
  • That changes the architectural classification: this is no longer just an event-emitting trait pack, it is a stateful subsystem with its own scheduler, ledger, and derived-data lifecycle. Treat it like an analytics component with a write path, not a utility library.
  • It remains Laravel-native (Eloquent, migrations, config, Artisan commands, events implementing ShouldDispatchAfterCommit), so it fits an existing Laravel monolith or modular monolith cleanly. It is a poor fit for anything that cannot expose a schedulable process (multi-tenant queues per-tenant, or a stack where the User model is not the canonical actor).
  • The introduction of RankingMetric contracts and named Boards declared in config is a genuinely good extension point: TPM can scope product asks (e.g. "top 10 by weekly XP per guild") to config + a small custom metric class rather than a fork.

Integration feasibility

  • Feasible but not a drop-in upgrade. v3.0.0 is a hard major with several classes of breakage:
    • Schema/config: multiplier_scopes polymorphic table replaced by typed multiplier_user / multiplier_tier pivots; MultiplierScope model and three config keys removed; legacy level-up.table key removed. Any host code reading those keys or joining the old table silently breaks.
    • Code contracts: Multiplier::scopeTo() gone (typed replacements), UserForeignKey helper class gone (Blueprint macro instead), trait method aliasing helpers removed — host models with their own challenges() / streaks() / experience() / experienceHistory() methods will now collide and need wrapper models.
    • Return types: Leaderboard::generate() returns LeaderboardEntry objects, not User collections. Any Blade view, API resource, or JSON contract consuming it breaks at runtime, not at install time.
    • Behavioural: setPoints() now fires UserLevelledUp / UserTierUpdated; audit defaults to true; excess points cap at the top level instead of throwing. All three are silent semantic changes that can double-count rewards or mask misconfigured level thresholds.
  • Database requirement is a gating item. Rank numbers require window functions: MySQL 8+ or SQLite 3.25+; MySQL 5.7 (EOL) is explicitly unsupported. Any estate still on RDS/Aurora MySQL 5.7 must remediate before the leaderboard feature is usable at all — the point/level core may still work, so it can be a phased rollout, but it must be surfaced as a dependency, not a footnote.
  • Low-risk, high-value items: transactions around addPoints/deductPoints/setPoints, ShouldDispatchAfterCommit on all package events (eliminates phantom mail/HTTP on rolled-back transactions), relation-cache clearing after achievement mutations, the PostgreSQL down() fix, and removal of the v2.1 MorphToManyWithTextCast Postgres workaround.

Technical risk

  • High — data migration. A backfill migration runs automatically during php artisan migrate. There is no documented batching, dry-run, or resumability. On a large multiplier_scopes table this is an unbounded production migration with no down() story for the pivots.
  • Medium-High — query cost. Window-function ranking over the users/experiences join, per metric, per period, per restricted set, per board. around() and rankOf() imply per-user rank computation. Cost scales with user count, not board size, unless indexes and materialisation are right.
  • Medium — new operational surface. level-up:snapshot-boards and level-up:league-rollover are now correctness-critical scheduled jobs. Missed runs mean missing rank events, wrong promotions/relegations, and broken leaderboard_rank challenge progress.
  • Medium — audit data growth and compliance. audit.enabled now defaults to true because periodic XP boards read from experience_audits. That is a new always-on high-volume table. Retention policy for audits is not described (snapshots have retention_days; audits appear to be yours to manage). Audit rows are effectively a per-user activity log — GDPR erasure and PII review are required.
  • Low-Medium — supply chain. Single-maintainer package with a large blast radius and a history of rapid major iterations (v2.1 revert, v3.0 rewrite). Budget for upgrade tax on each major, and pin exactly.

Key questions

  1. What is our production DB engine and version, and is MySQL 5.7 anywhere in the estate (including read replicas and CI)?
  2. Row counts for multiplier_scopes and experiences today, plus 12-month projections — what is the expected migration duration and index footprint?
  3. Do any host User models define challenges(), streaks(), experience(), or experienceHistory()? How many call sites consume Leaderboard::generate()?
  4. Who owns the scheduler, and what is the alerting story for a missed snapshot-boards or league-rollover run?
  5. What is the retention and erasure policy for experience_audits, and does it fall under an existing data-retention schedule?
  6. Do we need leaderboards scoped per tenant/community? restrictTo() covers population restriction, but tenant isolation is a design decision we must make explicitly.
  7. Which of the new capabilities map to roadmap commitments (seasonal leagues? friend boards? achievement counters?) versus which are capability we merely unlock?
  8. Minimum PHP/Laravel versions supported by v3 — does that force a framework bump?

Integration Approach

Stack fit

  • Strong fit for a Laravel monolith or modular monolith with a canonical User model and a queue + scheduler already in place. The RankingMetric contract and config-declared Boards mean most product variants are configuration, not custom code.
  • Weak fit if the app has no reliable scheduler, runs multi-tenant with shared tables and no scoping convention, or cannot guarantee a window-function-capable DB.
  • The v3 event semantics (ShouldDispatchAfterCommit) make this materially safer to bolt onto existing transactional flows (orders, subscriptions, check-ins) than v2 was.

Migration path

  1. Freeze and pin. Lock the current 2.x constraint, record the exact version in production, and open the v3 upgrade as a tracked work item rather than a composer update side effect.
  2. Read UPGRADE.md end-to-end and convert it into a checklist keyed to our repo: config key renames (level-up.tables.experiences, tables.multiplier_user / multiplier_tier), removed model/config keys, UserForeignKey → $table->userForeignId(), scopeTo() → typed methods.
  3. Inventory collisions by grepping host models for the four trait method names; decide wrapper-model vs. rename per case before touching the upgrade.
  4. Inventory consumers of Leaderboard::generate() and of any points/level change path, especially anywhere setPoints() is used as a raw write (those now emit events).
  5. Staging rehearsal on a production-shaped snapshot. Measure backfill duration, lock behaviour, and post-migration row-count parity between multiplier_scopes and the two new pivots. This is the gate, not the code review.
  6. Production migration in a change window, with the app in maintenance or the write path quiesced, and a verified restore point.
  7. Turn on the new surfaces in sequence, not all at once (see below).

Compatibility

  • Test the migration down() paths, not just up(). The PostgreSQL down() fix in this release signals that rollback paths have historically been under-tested; assume others are too.
  • CI should run against the same engine family as production. SQLite satisfies the 3.25+ window-function requirement but will not surface the driver-specific behaviours (JSON casts, ALTER COLUMN TYPE) that have bitten this package before.
  • Watch for behavioural drift rather than type errors: capped excess points, setPoints() side effects, and audit-on-by-default are all changes a green test suite can miss.
  • Verify addPoints() first-time-experience branch and PointsIncreasedListener agree on starting level in our data (thresholds are not guaranteed monotonic in our fixtures).

Sequencing (recommended release train)

  • R0 — Prerequisites (no user-visible change): DB engine/version remediation if needed; scheduler ownership and alerting confirmed; audit retention policy agreed.
  • R1 — Core upgrade: v3 installed, schema migrated, config keys renamed, collisions resolved, generate() consumers updated. Feature parity with v2 behaviour; no leaderboards or leagues surfaced yet.
  • R2 — Auditing and ledger validation: confirm audit writes on, verify experience_audits volume and retention job. Do not enable any periodic board before this is stable.
  • R3 — Leaderboards: built-in metrics first (level, streak, all-time XP), then time-windowed XP, then restrictTo() populations. Ship rankOf()/around() UI only after query-cost validation.
  • R4 — Named Boards + snapshots: declare Boards in config, schedule level-up:snapshot-boards, wire LeaderboardRankChanged / UserEnteredTrackedDepth / UserLeftTrackedDepth to notifications. Set track_top deliberately — it drives both snapshot write volume and the leaderboard_rank challenge's valid depth.
  • R5 — Leagues: divisions, cohort enrollment, level-up:league-rollover, UserDivisionChanged. Ship last; it is the highest-consequence automation and depends on R3/R4 being trustworthy.
  • R6 — Achievement counters: adopt the optional absolute count (grantAchievement() + incrementAchievementProgress()) where the product wants "12 of 50 articles" style framing. Cheapest, most isolated addition — safe to pull forward if roadmap demands.

Operational Impact

Maintenance

  • Upgrade tax is real: v3 removes an entire model, a table, three config keys, a helper class, and a method alias mechanism. Each future major should be budgeted as a project, not a patch. Pin exactly, and re-read UPGRADE.md on every bump.
  • Reduced long-term maintenance from the deletions themselves: no more MorphToManyWithTextCast Postgres workaround, no polymorphic scope table, one canonical way to express a multiplier scope.
  • The userForeignId() Blueprint macro is a net win: it centralises UUID/ULID/int user key handling instead of duplicating a helper class per app.

Support

  • Community-supported package; expect to be your own first line. Budget internal ownership of the metric/board/league domain, not just the composer constraint.
  • Failure modes surface as package exceptions (MetricNotFoundException, MetricDisabledException, MetricRequiresAuditingException), which is good for debuggability but means misconfiguration reaches production as a thrown exception on a user-facing page. Wrap leaderboard endpoints with graceful degradation.
  • Support load will spike around season boundaries and rollover: "why did I get demoted" is a product-support question that will route to engineering unless standings are explainable.

Scaling

  • Three scaling curves, not one: (a) experience_audits write volume, always-on with audit defaulting to true; (b) window-function rank query cost, driven by user count and index quality; (c) snapshot persistence, driven by number of declared Boards × track_top × snapshot frequency × retention_days.
  • track_top default of 100 is a lever with two effects: it caps snapshot write amplification but also caps the depth at which leaderboard_rank challenges can validate. Set it from product intent, not default.
  • Snapshot diffing plus three dispatched events can produce event bursts on volatile boards. Route these through queues and consider coalescing notifications per user per period.
  • Rank queries belong on a read replica or a materialised path once boards are on high-traffic pages; plan for that before launch, not after.

Failure modes and mitigations

  • Migration backfill stalls or locks on production → rehearse on prod-shaped data, take a restore point, run in a window, verify row-count parity. Gate R1 on measured duration.
  • Trait method collision after alias removal → boot-time fatal. Grep host models first; resolve with wrapper models.
  • setPoints() now emits level/tier events → duplicate reward grants if downstream listeners already applied rewards manually. Audit every setPoints() caller before cutover.
  • Excess points silently capped instead of throwing → misconfigured level thresholds become invisible. Add an assertion/monitor comparing computed level against expected band.
  • AUDIT_POINTS=false with a periodic XP board → MetricRequiresAuditingException in production. Codify the pairing as a config test.
  • Missed snapshot-boards or league-rollover run → stale ranks, missing events, wrong promotions. Alert on absence, not just on failure; make commands idempotent-safe and re-runnable.
  • generate() returns entries, not users → contract break in views/API. Update serializers and add contract tests.
  • Window-function query timeout under load → degrade gracefully (cached last snapshot) rather than 500.
  • Audit table growth and erasure requests → retention job from day one; ensure user deletion cascades to audits.

Ramp-up

  • Engineering: ~1–2 person-weeks for R1 (upgrade, collisions, consumers, migration rehearsal) for a team already familiar with v2; ~2–3 person-weeks for the full R1–R4 surface including Boards, metrics, and snapshot wiring. Leagues adds roughly another week plus product design time.
  • New mental model required: metric → period → Board → snapshot → rank event, plus division → cohort → rollover. This is a domain vocabulary the team must share with product, not just code.
  • Product/TPM: the config-declared Boards pattern is the leverage point — most leaderboard experiments become a config change plus a metric class. Budget a short design session to define the initial Board set and track_top per board rather than letting engineers pick defaults.
  • Data/analytics: expect a one-time effort to instrument experience_audits retention, dashboard the snapshot job's health, and decide whether rank history feeds analytics or stays in-app.
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