{"id":17875723,"url":"https://github.com/cjmellor/level-up","last_synced_at":"2025-05-14T11:09:40.723Z","repository":{"id":168325528,"uuid":"623126190","full_name":"cjmellor/level-up","owner":"cjmellor","description":"Level-Up is a Laravel package introducing gamification into your applications. Users earn experience points (XP) and levels through interactions, while also unlocking achievements. It promotes engagement, competition, and fun through its dynamic leaderboard feature. Customisable to fit your specific needs","archived":false,"fork":false,"pushed_at":"2025-02-26T07:01:49.000Z","size":236,"stargazers_count":603,"open_issues_count":0,"forks_count":47,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-05-10T19:14:13.287Z","etag":null,"topics":["achievements","engagment","experience","gamification","laravel","leaderboard","levelling"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cjmellor.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-03T18:45:43.000Z","updated_at":"2025-05-07T15:14:27.000Z","dependencies_parsed_at":"2023-12-25T18:01:39.517Z","dependency_job_id":"376ea3d5-b3bc-41a7-ab2c-2903192ea56d","html_url":"https://github.com/cjmellor/level-up","commit_stats":null,"previous_names":["cjmellor/level-up"],"tags_count":26,"template":false,"template_full_name":"cjmellor/package-skeleton-laravel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjmellor%2Flevel-up","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjmellor%2Flevel-up/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjmellor%2Flevel-up/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjmellor%2Flevel-up/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cjmellor","download_url":"https://codeload.github.com/cjmellor/level-up/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253466807,"owners_count":21913085,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["achievements","engagment","experience","gamification","laravel","leaderboard","levelling"],"created_at":"2024-10-28T11:24:51.266Z","updated_at":"2025-05-14T11:09:40.660Z","avatar_url":"https://github.com/cjmellor.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Latest Version on Packagist](https://img.shields.io/packagist/v/cjmellor/level-up?color=rgb%2856%20189%20248%29\u0026label=release\u0026style=for-the-badge)](https://packagist.org/packages/cjmellor/level-up)\n[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/cjmellor/level-up/run-tests.yml?branch=main\u0026label=tests\u0026style=for-the-badge\u0026color=rgb%28134%20239%20128%29)](https://github.com/cjmellor/level-up/actions?query=workflow%3Arun-tests+branch%3Amain)\n[![Total Downloads](https://img.shields.io/packagist/dt/cjmellor/level-up.svg?color=rgb%28249%20115%2022%29\u0026style=for-the-badge)](https://packagist.org/packages/cjmellor/level-up)\n![Packagist PHP Version](https://img.shields.io/packagist/dependency-v/cjmellor/level-up/php?color=rgb%28165%20180%20252%29\u0026logo=php\u0026logoColor=rgb%28165%20180%20252%29\u0026style=for-the-badge)\n![Laravel Version](https://img.shields.io/badge/laravel-^10-rgb(235%2068%2050)?style=for-the-badge\u0026logo=laravel)\n\nThis package allows users to gain experience points (XP) and progress through levels by performing actions on your site. It can provide a simple way to track user progress and implement gamification elements into your application\n\n![Banner](https://banners.beyondco.de/Level%20Up.png?theme=dark\u0026packageManager=composer+require\u0026packageName=cjmellor%2Flevel-up\u0026pattern=ticTacToe\u0026style=style_1\u0026description=Enable+gamification+via+XP%2C+levels%2C+leaderboards%2C+achievements%2C+and+dynamic+multipliers\u0026md=1\u0026showWatermark=0\u0026fontSize=100px\u0026images=puzzle\u0026widths=auto)\n\n# Installation\n\nYou can install the package via composer:\n\n```\ncomposer require cjmellor/level-up\n```\n\nYou can publish and run the migrations with:\n\n```\nphp artisan vendor:publish --tag=\"level-up-migrations\"\nphp artisan migrate\n```\n\nYou can publish the config file with:\n\n```\nphp artisan vendor:publish --tag=\"level-up-config\"\n```\n\nThis is the contents of the published config file:\n\n```php\nreturn [\n    /*\n    |--------------------------------------------------------------------------\n    | User Foreign Key\n    |--------------------------------------------------------------------------\n    |\n    | This value is the foreign key that will be used to relate the Experience model to the User model.\n    |\n     */\n    'user' =\u003e [\n        'foreign_key' =\u003e 'user_id',\n        'model' =\u003e App\\Models\\User::class,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Experience Table\n    |--------------------------------------------------------------------------\n    |\n    | This value is the name of the table that will be used to store experience data.\n    |\n     */\n    'table' =\u003e 'experiences',\n\n    /*\n    |-----------------------------------------------------------------------\n    | Starting Level\n    |-----------------------------------------------------------------------\n    |\n    | The level that a User starts with.\n    |\n    */\n    'starting_level' =\u003e 1,\n\n    /*\n    |-----------------------------------------------------------------------\n    | Multiplier Paths\n    |-----------------------------------------------------------------------\n    |\n    | Set the path and namespace for the Multiplier classes.\n    |\n    */\n    'multiplier' =\u003e [\n        'enabled' =\u003e env(key: 'MULTIPLIER_ENABLED', default: true),\n        'path' =\u003e env(key: 'MULTIPLIER_PATH', default: app_path(path: 'Multipliers')),\n        'namespace' =\u003e env(key: 'MULTIPLIER_NAMESPACE', default: 'App\\\\Multipliers\\\\'),\n    ],\n\n    /*\n    |-----------------------------------------------------------------------\n    | Level Cap\n    |-----------------------------------------------------------------------\n    |\n    | Set the maximum level a User can reach.\n    |\n    */\n    'level_cap' =\u003e [\n        'enabled' =\u003e env(key: 'LEVEL_CAP_ENABLED', default: true),\n        'level' =\u003e env(key: 'LEVEL_CAP', default: 100),\n        'points_continue' =\u003e env(key: 'LEVEL_CAP_POINTS_CONTINUE', default: true),\n    ],\n\n    /*\n    | -------------------------------------------------------------------------\n    | Audit\n    | -------------------------------------------------------------------------\n    |\n    | Set the audit configuration.\n    |\n    */\n    'audit' =\u003e [\n        'enabled' =\u003e env(key: 'AUDIT_POINTS', default: false),\n    ],\n\n    /*\n    | -------------------------------------------------------------------------\n    | Record streak history\n    | -------------------------------------------------------------------------\n    |\n    | Set the streak history configuration.\n    |\n    */\n    'archive_streak_history' =\u003e [\n        'enabled' =\u003e env(key: 'ARCHIVE_STREAK_HISTORY_ENABLED', default: true),\n    ],\n\n    /*\n     | -------------------------------------------------------------------------\n     | Default Streak Freeze Time\n     | -------------------------------------------------------------------------\n     |\n     | Set the default time in days that a streak will be frozen for.\n     |\n     */\n    'freeze_duration' =\u003e env(key: 'STREAK_FREEZE_DURATION', default: 1),\n];\n```\n\n# Usage\n\n## 💯 Experience Points (XP)\n\nAdd the `GiveExperience` trait to your `User` model.\n\n```php\nuse LevelUp\\Experience\\Concerns\\GiveExperience;\n\nclass User extends Model\n{\n    use GiveExperience;\n\n    // ...\n}\n```\n\n**Give XP points to a User**\n\n```php\n$user-\u003eaddPoints(10);\n```\n\nA new record will be added to the `experiences` table which stores the Users’ points. If a record already exists, it will be updated instead. All new records will be given a `level_id` of `1`.\n\n\u003e [!NOTE]\n\u003e If you didn't set up your Level structure yet, a default Level of `1` will be added to get you started.\n\n**Deduct XP points from a User**\n\n```php\n$user-\u003edeductPoints(10);\n```\n\n**Set XP points to a User**\n\nFor an event where you just want to directly add a certain number of points to a User. Points can only be ***set*** if the User has an Experience Model.\n\n```php\n$user-\u003esetPoints(10);\n```\n\n**Retrieve a Users’ points**\n\n```php\n$user-\u003egetPoints();\n```\n\n### Multipliers\n\nPoint multipliers can be used to modify the experience point value of an event by a certain multiplier, such as doubling or tripling the point value. This can be useful for implementing temporary events or promotions that offer bonus points.\n\nTo get started, you can use an Artisan command to crease a new Multiplier.\n\n```bash\nphp artisan level-up:multiplier IsMonthDecember\n```\n\nThis will create a file at `app\\Multipliers\\IsMonthDecember.php`.\n\nHere is how the class looks:\n\n```php\n\u003c?php\n\nnamespace LevelUp\\Experience\\Tests\\Fixtures\\Multipliers;\n\nuse LevelUp\\Experience\\Contracts\\Multiplier;\n\nclass IsMonthDecember implements Multiplier\n{\n    public bool $enabled = true;\n    \n    public function qualifies(array $data): bool\n    {\n        return now()-\u003emonth === 12;\n    }\n\n    public function setMultiplier(): int\n    {\n        return 2;\n    }\n}\n```\n\nMultipliers are enabled by default, but you can change the `$enabled` variable to `false` so that it won’t even run.\n\nThe `qualifies` method is where you put your logic to check against and multiply if the result is true.\n\nThis can be as simple as checking that the month is December.\n\n```php\npublic function qualifies(array $data): bool\n{\n    return now()-\u003emonth === 12;\n}\n```\n\nOr passing extra data along to check against. This is a bit more complex.\n\nYou can pass extra data along when you're adding points to a User. Any enabled Multiplier can then use that data to check against.\n\n```php\n$user\n    -\u003ewithMultiplierData([\n        'event_id' =\u003e 222,\n    ])\n    -\u003eaddPoints(10);\n\n//\n\npublic function qualifies(array $data): bool\n{\n    return isset($data['event_id']) \u0026\u0026 $data['event_id'] === 222;\n}\n```\n\n**Conditional Multipliers**\n\nIf you don't want to use the class based method to check conditionals to add multipliers, you can do this inline by giving the method a callback with the conditional. When using this method, make sure you have the multiplier set as an argument in the `addPoints` method, otherwise an error will occur. See example below:\n\n```php\n$user\n    -\u003ewithMultiplierData(fn () =\u003e true)\n    -\u003eaddPoints(amount: 10, multiplier: 2);\n```\n\nThe `setMultiplier` method expects an `int` which is the number it will be multiplied by.\n\n**Multiply Manually**\n\nYou can skip this altogether and just multiply the points manually if you desire.\n\n```php\n$user-\u003eaddPoints(\n    amount: 10, \n    multiplier: 2\n);\n```\n\n### Events\n\n**PointsIncrease** - When points are added.\n\n```php\npublic int $pointsAdded,\npublic int $totalPoints,\npublic string $type,\npublic ?string $reason,\npublic Model $user,\n```\n\n**PointsDecreased** - When points are decreased.\n\n```php\npublic int $pointsDecreasedBy,\npublic int $totalPoints,\npublic ?string $reason,\npublic Model $user,\n```\n\n## ⬆️ Levelling\n\n\u003e [!NOTE]\n\u003e If you add points before setting up your levelling structure, a default Level of `1` will be added to get you started.\n\n### Set up your levelling structure\n\nThe package has a handy facade to help you create your levels.\n\n```php\nLevel::add(\n    ['level' =\u003e 1, 'next_level_experience' =\u003e null],\n    ['level' =\u003e 2, 'next_level_experience' =\u003e 100],\n    ['level' =\u003e 3, 'next_level_experience' =\u003e 250],\n);\n```\n\n**Level 1** should always be `null` for the `next_level_experience` as it is the default starting point.\n\nAs soon as a User gains the correct number of points listed for the next level, they will level-up.\n\n\u003e [!TIP]\n\u003e a User gains 50 points, they’ll still be on Level 1, but gets another 50 points, so the User will now move onto Level 2\n\n**See how many points until the next level**\n\n```php\n$user-\u003enextLevelAt();\n```\n\n**Get the Users’ current Level**\n\n```php\n$user-\u003egetLevel();\n```\n\n### Level Cap\n\nA level cap sets the maximum level that a user can reach. Once a user reaches the level cap, they will not be able to gain any more levels, even if they continue to earn experience points. The level cap is enabled by default and capped to level `100`. These\noptions can be changed in the packages config file at `config/level-up.php` or by adding them to your `.env` file.\n\n```\nLEVEL_CAP_ENABLED=\nLEVEL_CAP=\nLEVEL_CAP_POINTS_CONTINUE\n```\n\nBy default, even when a user hits the level cap, they will continue to earn experience points. To freeze this, so points do not increase once the cap is hit, turn on the `points_continue` option in the config file, or set it in the `.env`.\n\n### Events\n\n**UserLevelledUp** - When a User levels-up\n\n```php\npublic Model $user,\npublic int $level\n```\n\n## 🏆 Achievements\n\nThis is a feature that allows you to recognise and reward users for completing specific tasks or reaching certain milestones.\nYou can define your own achievements and criteria for earning them.\nAchievements can be static or have progression.\nStatic meaning the achievement can be earned instantly.\nAchievements with progression can be earned in increments, like an achievement can only be obtained once the progress is 100% complete.\n\n### Creating Achievements\n\nThere is no built-in methods for creating achievements, there is just an `Achievement` model that you can use as normal:\n\n```php\nAchievement::create([\n    'name' =\u003e 'Hit Level 20',\n    'is_secret' =\u003e false,\n    'description' =\u003e 'When a User hits Level 20',\n    'image' =\u003e 'storage/app/achievements/level-20.png',\n]);\n```\n\n### Gain Achievement\n\nTo use Achievements in your User model, you must first add the Trait.\n\n```php\n// App\\Models\\User.php\n\nuse LevelUp\\Experience\\Concerns\\HasAchievements;\n\nclass User extends Authenticable\n{\n\tuse HasAchievements;\n\t\n\t// ...\n}\n```\n\nThen you can start using its methods, like to grant a User an Achievement:\n\n```php\n$achievement = Achievement::find(1);\n\n$user-\u003egrantAchievement($achievement);\n```\n\nTo retrieve your Achievements:\n\n```php\n$user-\u003egetUserAchievements();\n```\n\n### Revoke Achievement\n\nYou can revoke an achievement from a user using the `revokeAchievement` method:\n\n```php\n$user-\u003erevokeAchievement($achievement);\n```\n\nThe method will throw an exception if you try to revoke an achievement that the user doesn't have. You can revoke both standard and secret achievements, and it will also remove any associated progress.\n\nWhen an achievement is revoked, a `AchievementRevoked` event is dispatched.\n\n### Add progress to Achievement\n\n```php\n$user-\u003egrantAchievement(\n    achievement: $achievement, \n    progress: 50 // 50%\n);\n```\n\n\u003e [!NOTE]\n\u003e Achievement progress is capped to 100%\n\n### Check Achievement Progression\n\nCheck at what progression your Achievements are at.\n\n```php\n$user-\u003eachievementsWithProgress()-\u003eget();\n```\n\nCheck Achievements that have a certain amount of progression:\n\n```php\n$user-\u003eachievementsWithSpecificProgress(25)-\u003eget();\n```\n\n### Increase Achievement Progression\n\nYou can increment the progression of an Achievement up to 100.\n\n```php\n$user-\u003eincrementAchievementProgress(\n    achievement: $achievement, \n    amount: 10\n);\n```\n\nA `AchievementProgressionIncreased` Event runs on method execution.\n\n### Secret Achievements\n\nSecret achievements are achievements that are hidden from users until they are unlocked.\n\nSecret achievements are made secret when created. If you want to make a non-secret Achievement secret, you can just update the Model.\n\n```php\n$achievement-\u003eupdate(['is_secret' =\u003e true]);\n```\n\nYou can retrieve the secret Achievements.\n\n```php\n$user-\u003esecretAchievements;\n```\n\nTo view *********all********* Achievements, both secret and non-secret:\n\n```php\n$user-\u003eallAchievements;\n```\n\n### Events\n\n**AchievementAwarded** - When an Achievement is attached to the User\n\n```php\npublic Achievement $achievement,\npublic Model $user,\n```\n\n\u003e [!NOTE]\n\u003e This event only runs if the progress of the Achievement is 100%\n\n**AchievementRevoked** - When an Achievement is detached from the User\n\n```php\npublic Achievement $achievement,\npublic Model $user,\n```\n\n**AchievementProgressionIncreased** - When a Users’ progression for an Achievement is increased.\n\n```php\npublic Achievement $achievement,\npublic Model $user,\npublic int $amount,\n```\n\n## 📈 Leaderboard\n\nThe package also includes a leaderboard feature to track and display user rankings based on their experience points.\n\nThe Leaderboard comes as a Service.\n\n```php\nLeaderboard::generate();\n```\n\nThis generates a User model along with its Experience and Level data and ordered by the Users’ experience points.\n\n\u003e The Leaderboard is very basic and has room for improvement\n\u003e\n\n## 🔍 Auditing\n\nYou can enable an Auditing feature in the config, which keeps a track each time a User gains points, levels up and what level to.\n\nThe `type` and `reason` fields will be populated automatically based on the action taken, but you can overwrite these when adding points to a User\n\n```php\n$user-\u003eaddPoints(\n    amount: 50,\n    multiplier: 2,\n    type: AuditType::Add-\u003evalue,\n    reason: \"Some reason here\",\n);\n```\n\n\u003e [!NOTE]\n\u003e Auditing happens when the `addPoints` and `deductPoints` methods are called. Auditing must be enabled in the config file.\n\n**View a Users’ Audit Experience**\n\n```php\n$user-\u003eexperienceHistory;\n```\n\n## 🔥 Streaks\n\nWith the Streaks feature, you can track and motivate user engagement by monitoring consecutive daily activities. Whether it's logging in, completing tasks, or any other daily activity, maintaining streaks encourages users to stay active and engaged.\n\nStreaks are controlled in a Trait, so only use the trait if you want to use this feature. Add the Trait to you `User` model\n\n```php\nuse LevelUp\\Experience\\Concerns\\HasStreaks;\n\nclass User extends Model\n{\n\tuse HasStreaks;\n\n\t// ...\n}\n```\n\n### Activities\n\nUse the `Activies` model to add new activities that you want to track. Here’s some examples:\n\n- Logs into a website\n- Posts an article\n\n### Record a Streak\n\n```php\n$activity = Activity::find(1);\n\n$user-\u003erecordStreak($activity);\n```\n\nThis will increment the streak count for the User on this activity. An `Event is ran on increment.\n\n### Break a Streak\n\nStreaks can be broken, both automatically and manually. This puts the count back to `1` to start again. An Event is ran when a streak is broken.\n\nFor example, if your streak has had a successful run of 5 days, but a day is skipped and you run the activity on day 7, the streak will be broken and reset back to `1`. Currently, this happens automatically.\n\n### Reset a Streak\n\nYou can reset a streak manually if you desire. If `level-up.archive_streak_history.enabled` is true, the streak history will be recorded.\n\n```php\n$activity = Activity::find(1);\n\n$user-\u003eresetStreak($activity);\n```\n\n### Archive Streak Histories\n\nStreaks are recorded, or “archived” by default. When a streak is broken, a record of the streak is recorded. A Model is supplied to use this data.\n\n```php\nuse LevelUp\\Experience\\Models\\StreakHistory;\n\nStreakHistory::all();\n```\n\n### Get Current Streak Count\n\nSee the streak count for an activity for a User\n\n```php\n$user-\u003egetCurrentStreakCount($activity); // 2\n```\n\n### Check User Streak Activity\n\nCheck if the User has performed a streak for the day\n\n```php\n$user-\u003ehasStreakToday($activity);\n```\n\n### Events\n\n**StreakIncreased** - If an activity happens on a day after the previous day, the streak is increased.\n\n```php\npublic int $pointsAdded,\npublic int $totalPoints,\npublic string $type,\npublic ?string $reason,\npublic Model $user,\n```\n\n**StreakBroken** - When a streak is broken and the counter is reset.\n\n```php\npublic Model $user,\npublic Activity $activity,\npublic Streak $streak,\n```\n\n## 🥶 Streak Freezing\n\nStreaks can be frozen, which means they will not be broken if a day is skipped. This is useful for when you want to allow users to take a break from an activity without losing their streak.\n\nThe freeze duration is a configurable option in the config file.\n\n```php\n'freeze_duration' =\u003e env(key: 'STREAK_FREEZE_DURATION', default: 1),\n```\n\n### Freeze a Streak\n\nFetch the activity you want to freeze and pass it to the `freezeStreak` method. A second parameter can be passed to set the duration of the freeze. The default is `1` day (as set in the config)\n\nA `StreakFrozen` Event is ran when a streak is frozen.\n\n```php\n$user-\u003efreezeStreak(activity: $activity);\n\n$user-\u003efreezeStreak(activity: $activity, days: 5); // freeze for 5 days\n```\n\n### Unfreeze a Streak\n\nThe opposite of freezing a streak is unfreezing it. This will allow the streak to be broken again.\n\nA `StreakUnfrozen` Event is run when a streak is unfrozen.\n\n```php\n$user-\u003eunfreezeStreak($activity);\n```\n\n### Check if a Streak is Frozen\n\n```php\n$user-\u003eisStreakFrozen($activity);\n```\n\n### Events\n\n**StreakFrozen** - When a streak is frozen.\n\n```php\npublic int $frozenStreakLength,\npublic Carbon $frozenUntil,\n```\n\n**StreakUnfrozen** - When a streak is unfrozen.\n\n```\nNo data is sent with this event\n```\n\n# Testing\n\n```\ncomposer test\n```\n\n# Changelog\n\nPlease see [CHANGELOG](notion://www.notion.so/CHANGELOG.md) for more information on what has changed recently.\n\n# License\n\nThe MIT Licence (MIT). Please see [Licence File](notion://www.notion.so/LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcjmellor%2Flevel-up","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcjmellor%2Flevel-up","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcjmellor%2Flevel-up/lists"}