Skip to main content

level-up-development

Build and work with cjmellor/level-up features, including XP, levels, tiers, achievements, streaks, multipliers, challenges, leaderboards, leagues, and auditing.

Source facts

Repository
cjmellor/level-up
Last source activity
June 18, 2026 at 21:01
Detected SKILL.md language
English
Stars
676
Forks
55

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
level-up-development
description
Build and work with cjmellor/level-up features, including XP, levels, tiers, achievements, streaks, multipliers, challenges, leaderboards, leagues, and auditing.
## When to use this skill Use this skill when working with gamification features — adding experience points, levels, tiers, achievements, streaks, multipliers, leaderboards, leagues, or auditing — using cjmellor/level-up. ## Core Concepts - **XP and Levels** — Users earn experience points (XP) and automatically progress through a defined level structure. - **Tiers** — Named status brackets (e.g. Bronze, Silver, Gold) based on XP thresholds, independent of numeric levels. - **Achievements** — Unlockable rewards, optionally with progress tracking, optionally gated by tier. - **Streaks** — Track consecutive daily activities with freeze support. - **Multipliers** — Database-backed point modifiers with scoping, scheduling, and configurable stacking strategies. - **Leaderboard** — Rank users by any metric (XP by default) with competition-style rank numbers, optionally scoped to a tier or a time period (day/week/month/custom). - **Leagues** — A competitive cycle on one periodic Board: active users are grouped into small Cohorts within a Division each period and ranked within their Cohort. A Division is competition history, NOT a Tier (which is XP status) — a user holds both independently. - **Auditing** — Automatic history of all XP changes, level-ups, and tier changes. ## Setup ### Install ```bash composer require cjmellor/level-up php artisan vendor:publish --tag="level-up-migrations" php artisan migrate php artisan vendor:publish --tag="level-up-config" ``` ### Add Traits to the User Model Add only the traits you need: ```php use LevelUp\Experience\Concerns\GiveExperience; use LevelUp\Experience\Concerns\HasAchievements; use LevelUp\Experience\Concerns\HasChallenges; use LevelUp\Experience\Concerns\HasLeagues; use LevelUp\Experience\Concerns\HasStreaks; use LevelUp\Experience\Concerns\HasTiers; class User extends Authenticatable { use GiveExperience; // Required — XP and levels use HasAchievements; // Optional — achievements use HasStreaks; // Optional — streaks use HasTiers; // Optional — tiers use HasChallenges; // Optional — challenges use HasLeagues; // Optional — leagues (divisions/cohorts) } ``` `GiveExperience` is the foundation. The others are opt-in. ## Levels ### Define Levels ```php use LevelUp\Experience\Models\Level; Level::add( ['level' => 1, 'next_level_experience' => null], ['level' => 2, 'next_level_experience' => 100], ['level' => 3, 'next_level_experience' => 250], ['level' => 4, 'next_level_experience' => 500], ['level' => 5, 'next_level_experience' => 1000], ); ``` Level 1 must have `next_level_experience` set to `null` — it is the default starting point. Users level up automatically when their XP reaches the threshold. Throws `LevelExistsException` if a level number already exists. ### Level Queries ```php $user->getLevel(); // Current level number (int), 0 if no experience $user->getPoints(); // Current XP total (int), 0 if no experience $user->nextLevelAt(); // XP remaining until next level (int) $user->nextLevelAt(checkAgainst: 5); // XP remaining until level 5 $user->nextLevelAt(showAsPercentage: true); // Progress as 0-100 percentage ``` ### Manual Level Up ```php $user->levelUp(to: 5); // Jump to level 5 ``` Throws `InvalidArgumentException` if the level does not exist. Fires `UserLevelledUp` for each intermediate level gained. Respects the level cap. ### Level Cap Configured in `config/level-up.php`: ```php 'level_cap' => [ 'enabled' => env('LEVEL_CAP_ENABLED', true), 'level' => env('LEVEL_CAP', 100), 'points_continue' => env('LEVEL_CAP_POINTS_CONTINUE', true), ], ``` When the cap is reached, the user stops levelling. If `points_continue` is `true`, XP still accumulates. If `false`, XP stops accumulating too. ## Experience Points (XP) ### Add Points ```php $user->addPoints(50); $user->addPoints(50, reason: 'Completed tutorial'); $user->addPoints(50, multiplier: 2); $user->addPoints(50, type: AuditType::Add->value, reason: 'Bonus'); ``` Creates an experience record if none exists, otherwise increments. Automatically levels up if the threshold is crossed. Points past the highest level's `next_level_experience` cap the user at the top level (no exception). ### Deduct Points ```php $user->deductPoints(30); $user->deductPoints(30, reason: 'Penalty'); ``` Throws `Exception` if the user has no experience record. ### Set Points ```php $user->setPoints(500); ``` Directly overwrites the XP total, then recalculates level and tier from the new total (firing `UserLevelledUp` / `UserTierUpdated` as needed). Throws `Exception` if the user has no experience record. Writes no audit record — it is an administrative override, invisible to periodic leaderboards. ### Get Points ```php $user->getPoints(); // int, returns 0 if no experience record ``` ## Multipliers Multipliers are database-backed records that modify point calculations. They can be scoped to specific users or tiers, scheduled with time windows, and configured to stack using different strategies. ### Create Multipliers ```php use LevelUp\Experience\Models\Multiplier; Multiplier::create([ 'name' => 'Weekend Bonus', 'multiplier' => 2.0, 'is_active' => true, 'starts_at' => now()->startOfWeekend(), 'expires_at' => now()->endOfWeekend(), ]); ``` Active multipliers are automatically applied when `addPoints()` is called. The `multiplier` value must be at least `0.01`. If both `starts_at` and `expires_at` are set, `starts_at` must be before `expires_at`. ### Scope Multipliers Multipliers with no scopes apply to all users. Use the typed `scopeToUser` / `scopeToTier` methods to restrict: ```php $multiplier->scopeToUser($user); // Specific user only $multiplier->scopeToTier($goldTier); // Gold tier users only $multiplier->scopeToUser($user)->scopeToTier($tier); // Both — chains via fluent return // Variadic — attach multiple of the same type in one call $multiplier->scopeToUser($alice, $bob, $carol); $multiplier->scopeToTier($silver, $gold, $platinum); // Reverse the scope $multiplier->unscopeFromUser($user); $multiplier->unscopeFromTier($goldTier); // Check whether a multiplier has any scopes attached $multiplier->isGlobal(); // true when no users or tiers attached ``` `scopeToUser` and `scopeToTier` are idempotent (they use `syncWithoutDetaching` internally) — calling them twice with the same model does not create duplicates. ### Inline Multiplier ```php $user->addPoints(amount: 10, multiplier: 3); // 30 points ``` Inline multipliers stack with DB multipliers according to the configured strategy. ### Stacking Strategies Configure in `config/level-up.php`: ```php 'multiplier' => [ 'enabled' => env('MULTIPLIER_ENABLED', true), 'stack_strategy' => env('MULTIPLIER_STACK', 'compound'), // 'compound' — multipliers multiply each other: 2 × 5 = 10x // 'additive' — multipliers sum: 2 + 5 = 7x // 'highest' — only the largest applies: max(2, 5) = 5x ], ``` ### Query Multipliers ```php Multiplier::active()->get(); // Currently active Multiplier::active()->forUser($user)->get(); // Active for a specific user Multiplier::scheduled()->get(); // Future (starts_at > now) Multiplier::expired()->get(); // Past (expires_at < now) ``` ### MultiplierApplied Event Fires when multipliers modify a point calculation. Properties: `Model $user`, `Collection $multipliers`, `int $originalAmount`, `int $finalAmount`, `string $strategy`. ## Tiers ### Define Tiers ```php use LevelUp\Experience\Models\Tier; Tier::add( ['name' => 'Bronze', 'experience' => 0], ['name' => 'Silver', 'experience' => 500], ['name' => 'Gold', 'experience' => 2000], ['name' => 'Platinum', 'experience' => 5000, 'metadata' => ['color' => '#E5E4E2', 'icon' => 'crown']], ); ``` Tier names and experience values must be unique. Throws `TierExistsException` on duplicates. The `metadata` column is a flexible JSON field for any extra data (colours, icons, descriptions). The entire `add()` call is wrapped in a database transaction — if any tier fails, none are created. ### Automatic Tier Promotion Tiers update automatically when XP changes. When `addPoints()` causes the user to cross a tier threshold, `experience.tier_id` is updated and a `UserTierUpdated` event fires with `TierDirection::Promoted`. ### Query Tiers ```php $user->getTier(); // Current Tier model or null $user->getNextTier(); // Next tier above current (Tier or null) $user->tierProgress(); // Percentage 0-100 through current bracket $user->nextTierAt(); // XP remaining until next tier $user->isAtTier('Gold'); // Exact match (bool) $user->isAtOrAboveTier('Silver'); // At or above (bool) ``` `getTier()` returns `null` if the user has no experience record or tiers are disabled. ### Demotion By default, tiers use a **high-water mark** — once earned, they persist even if points decrease. Enable demotion to allow tier drops: ``` TIER_DEMOTION=true ``` When enabled, `deductPoints()` checks if the user should drop and fires `UserTierUpdated` with `TierDirection::Demoted`. The `newTier` property is nullable — it will be `null` if the user drops below all tier thresholds. ### Tier-Scoped Multipliers Create a multiplier and scope it to a tier so it only applies to users at that tier: ```php $multiplier = Multiplier::create([ 'name' => 'Gold Bonus', 'multiplier' => 2.0, 'is_active' => true, ]); $goldTier = Tier::where('name', 'Gold')->first(); $multiplier->scopeToTier($goldTier); ``` When a Gold-tier user calls `addPoints()`, this multiplier is automatically included. ### Tier-Gated Achievements Restrict achievements so only users at a certain tier can earn them: ```php $goldTier = Tier::where('name', 'Gold')->first(); Achievement::create([ 'name' => 'Golden Streak', 'tier_id' => $goldTier->id, ]); ``` Attempting to grant to a user below Gold throws `TierRequirementNotMet`. ### Tier-Scaled Streak Freezes Higher tiers get longer freeze durations: ```php // config/level-up.php 'tiers' => [ 'streak_freeze_days' => [ 'Bronze' => 1, 'Silver' => 2, 'Gold' => 3, 'Platinum' => 7, ], ], ``` Falls back to the global `freeze_duration` if the tier is not listed or tiers are disabled. ### Tier-Scoped Leaderboards ```php use LevelUp\Experience\Facades\Leaderboard; Leaderboard::forTier('Gold')->generate(); Leaderboard::forTier($tierModel)->generate(); ``` ### Tier Config ```php 'tiers' => [ 'enabled' => env('TIERS_ENABLED', true), 'demotion' => env('TIER_DEMOTION', false), 'streak_freeze_days' => [], ], ``` ## Achievements ### Create Achievements ```php use LevelUp\Experience\Models\Achievement; Achievement::create([ 'name' => 'First Login', 'is_secret' => false, 'description' => 'Log in for the first time', 'image' => 'storage/app/achievements/first-login.png', ]); // Secret achievement (hidden until earned) Achievement::create([ 'name' => 'Hidden Gem', 'is_secret' => true, ]); // Tier-gated achievement Achievement::create([ 'name' => 'Gold Member Badge', 'tier_id' => $goldTier->id, ]); ``` ### Grant Achievement ```php $user->grantAchievement($achievement); // With progress (0-100) $user->grantAchievement($achievement, progress: 50); ``` Throws `Exception` if progress exceeds 100, or if the user already has the achievement. Throws `TierRequirementNotMet` if tier-gated and user does not meet the tier requirement. `AchievementAwarded` event fires only when progress is `null` or `100`. ### Revoke Achievement ```php
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub