Achievements for Laravel 5.3+

Overview

Laravel Achievements Logo

Build Status Total Downloads License

An implementation of an Achievement System in Laravel, inspired by Laravel's Notification system.

Table of Contents

  1. Requirements
  2. Installation
  3. Creating Achievements
  4. Unlocking Achievements
  5. Adding Progress
  6. Retrieving Achievements
  7. Event Listeners
  8. License

Requirements

  • Laravel 5.3 or higher
  • PHP 5.6 or higher

Installation

Default installation is via Composer.

composer require gstt/laravel-achievements

Add the Service Provider to your config/app file in the providers section.

'providers' => [
    ...
    Gstt\Achievements\AchievementsServiceProvider::class,

Backup your database and run the migrations in order to setup the required tables on the database.

php artisan migrate

Creating Achievements

Similar to Laravel's implementation of Notifications, each Achievement is represented by a single class (typically stored in the app\Achievements directory.) This directory will be created automatically for you when you run the make:achievement command.

php artisan make:achievement UserMadeAPost

This command will put a fresh Achievement in your app/Achievements directory with only has two properties defined: name and description. You should change the default values for these properties to something that better explains what the Achievement is and how to unlock it. When you're done, it should look like this:

<?php

namespace App\Achievements;

use Gstt\Achievements\Achievement;

class UserMadeAPost extends Achievement
{
    /*
     * The achievement name
     */
    public $name = "Post Created";

    /*
     * A small description for the achievement
     */
    public $description = "Congratulations! You have made your first post!";
}

Unlocking Achievements

Achievements can be unlocked by using the Achiever trait.

<?php

namespace App;

use Gstt\Achievements\Achiever;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Achiever;
}

This trait contains the unlock method, that can be used to unlock an Achievement. The unlock method expects an Achievement instance:

use App\Achievements\UserMadeAPost

$user->unlock(new UserMadeAPost());

Remember that you're not restricted to the User class. You may add the Achiever trait to any entity that could unlock Achievements.

Adding Progress

Instead of directly unlocking an achievement, you can add a progress to it. For example, you may have an achievement UserMade10Posts and you want to keep track of how the user is progressing on this Achievement.

In order to do that, you must set an additional parameter on your UserMade10Posts class, called $points:

<?php

namespace App\Achievements;

use Gstt\Achievements\Achievement;

class UserMade10Posts extends Achievement
{
    /*
     * The achievement name
     */
    public $name = "10 Posts Created";

    /*
     * A small description for the achievement
     */
    public $description = "Wow! You have already created 10 posts!";
    
    /*
     * The amount of "points" this user need to obtain in order to complete this achievement
     */
    public $points = 10;
}

You may now control the progress by the methods addProgress and removeProgress on the Achiever trait. Both methods expect an Achievement instance and an amount of points to add or remove:

use App\Achievements\UserMade10Posts;

$user->addProgress(new UserMade10Posts(), 1); // Adds 1 point of progress to the UserMade10Posts achievement

In addition, you can use the methods resetProgress to set the progress back to 0 and setProgress to set it to a specified amount of points:

use App\Achievements\FiveConsecutiveSRanks;

if($rank != 'S'){
    $user->resetProgress(new FiveConsecutiveSRanks());
} else {
    $user->addProgress(new FiveConsecutiveSRanks(), 1);
}
use App\Achievements\Have1000GoldOnTheBag;

$user->setProgress(new Have100GoldOnTheBag(), $user->amountOfGoldOnTheBag);

Once an Achievement reach the defined amount of points, it will be automatically unlocked.

Retrieving Achievements

The Achiever trait also adds a convenient relationship to the entity implementing it: achievements(). You can use it to retrieve progress for all achievements the entity has interacted with. Since achievements() is a relationship, you can use it as a QueryBuilder to filter data even further.

$achievements   = $user->achievements;
$unlocked_today = $user->achievements()->where('unlocked_at', '>=', Carbon::yesterday())->get();

You can also search for a specific achievement using the achievementStatus() method.

$details = $user->achievementStatus(new UserMade10Posts());

There are also three additional helpers on the Achiever trait: lockedAchievements(), inProgressAchievements() and unlockedAchievements().

Event Listeners

Listening to all Achievements

Laravel Achievements provides two events that can be listened to in order to provide "Achievement Unlocked" messages or similar. Both events receive the instance of AchievementProgress that triggered them.

The Gstt\Achievements\Event\Progress event triggers whenever an Achiever makes progress, but doesn't unlock an Achievement. The Gstt\Achievements\Event\Unlocked event triggers whenever an Achiever actually unlocks an achievement.

Details on how to listen to those events are explained on Laravel's Event documentation.

Listening to specific Achievements

The event listeners mentioned above triggers for all Achievements. If you would like to add an event listener for only a specific Achievement, you can do so by implementing the methods whenUnlocked or whenProgress on the Achievement class.

<?php

namespace App\Achievements;

use Gstt\Achievements\Achievement;

class UserMade50Posts extends Achievement
{
    /*
     * The achievement name
     */
    public $name = "50 Posts Created";

    /*
     * A small description for the achievement
     */
    public $description = "Wow! You have already created 50 posts!";
    
    /*
     * The amount of "points" this user need to obtain in order to complete this achievement
     */
    public $points = 50;
    
    /*
     * Triggers whenever an Achiever makes progress on this achievement
    */
    public function whenProgress($progress)
    {
        
    }
    
    /*
     * Triggers whenever an Achiever unlocks this achievement
    */
    public function whenUnlocked($progress)
    {
        
    }
}

License

Laravel Achievements is open-sourced software licensed under the MIT license.

Comments
  • Multiple Models as Achiever

    Multiple Models as Achiever

    I have two models that I'd like to earn achievements on, is that possible? I'm getting unexpected behavior.

    ModelOne

    use Gstt\Achievements\Achiever;
    use Illuminate\Database\Eloquent\Model;
    
    class ModelOne extends Model
    {
        use Achiever;
    }
    

    ModelTwo

    use Gstt\Achievements\Achiever;
    use Illuminate\Database\Eloquent\Model;
    
    class ModelTwo extends Model
    {
        use Achiever;
    }
    

    Achievements are working as expected for ModelOne, but ModelTwo is not. This, for example, is getting wrongly saved as being ModelOne...

    $model = ModelTwo::first();
    $model->unlockIfLocked(new ThisAchievement());
    

    And the achievements relationship is screwy, as well. App\ModelTwo::has('achievements')->first() does this...

    
    Illuminate/Database/QueryException with message 'SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'achiever_id' cannot be null (SQL: insert into `achievement_progress` (`achievement_id`, `achiever_id`, `achiever_type`, `points`, `id`, `updated_at`, `created_at`) values (1, , App/ModelTwo, 0, d319c898-482c-4833-bc3d-a2c588ba1545, 2018-08-23 12:13:28, 2018-08-23 12:13:28))'
    

    It's possible that it's related to my custom trait that I made:

    namespace App\Traits;
    
    use Gstt\Achievements\Achievement;
    
    trait Achievable
    {
        /**
         * Unlock If Locked
         * 
         * @param Achievement $achievement
         * @return void
         */
        public function unlockIfLocked(Achievement $achievement)
        {
            if(! $this->hasUnlocked($achievement))
            {
                $this->unlock($achievement);
            }
        }
    }
    
    opened by droplister 8
  • Unlock/Progress Events

    Unlock/Progress Events

    Hello. How am I able to use sweetalert2 for these events?I would like a popup like this when progress or a unlock is made.

    https://codepen.io/anon/pen/yXXGPy

    Is this possible and if not what to you recommend for alerts? I use toastr for my sites alerts but wanted something little pronounced for achievement alerts.

    Thanks.

    opened by HDVinnie 6
  • [REQUEST] Improve returned data

    [REQUEST] Improve returned data

    When doing a query for user achievements and unlocked ones, the returned data is not pretty optimized. (Personal point of view).

    Example of $user->unlockedAchievements()->all():

    Array ( [0] => Gstt\Achievements\Model\AchievementProgress Object ( [incrementing] => [table:protected] => achievement_progress [guarded:protected] => Array ( ) [casts:protected] => Array ( [unlocked_at] => datetime ) [connection:protected] => mysql [primaryKey:protected] => id [keyType:protected] => int [with:protected] => Array ( ) [withCount:protected] => Array ( ) [perPage:protected] => 15 [exists] => 1 [wasRecentlyCreated] => [attributes:protected] => Array ( [id] => 77352394-32ec-4122-a970-dc2614ae51f6 [achievement_id] => 4 [achiever_id] => 1 [achiever_type] => App\User [points] => 0 [unlocked_at] => 2018-02-21 19:02:54 [created_at] => 2018-03-02 13:32:00 [updated_at] => 2018-03-02 13:32:00 ) [original:protected] => Array ( [id] => 77352394-32ec-4122-a970-dc2614ae51f6 [achievement_id] => 4 [achiever_id] => 1 [achiever_type] => App\User [points] => 0 [unlocked_at] => 2018-02-21 19:02:54 [created_at] => 2018-03-02 13:32:00 [updated_at] => 2018-03-02 13:32:00 ) [changes:protected] => Array ( ) [dates:protected] => Array ( ) [dateFormat:protected] => [appends:protected] => Array ( ) [dispatchesEvents:protected] => Array ( ) [observables:protected] => Array ( ) [relations:protected] => Array ( ) [touches:protected] => Array ( ) [timestamps] => 1 [hidden:protected] => Array ( ) [visible:protected] => Array ( ) [fillable:protected] => Array ( ) ) [1] => Gstt\Achievements\Model\AchievementProgress Object ( [incrementing] => [table:protected] => achievement_progress [guarded:protected] => Array ( ) [casts:protected] => Array ( [unlocked_at] => datetime ) [connection:protected] => mysql [primaryKey:protected] => id [keyType:protected] => int [with:protected] => Array ( ) [withCount:protected] => Array ( ) [perPage:protected] => 15 [exists] => 1 [wasRecentlyCreated] => [attributes:protected] => Array ( [id] => 95d4e3c1-64f2-4649-be50-fa81d90e98fc [achievement_id] => 1 [achiever_id] => 1 [achiever_type] => App\User [points] => 1 [unlocked_at] => 2018-02-21 19:02:54 [created_at] => 2018-02-21 19:02:54 [updated_at] => 2018-02-21 19:02:54 ) [original:protected] => Array ( [id] => 95d4e3c1-64f2-4649-be50-fa81d90e98fc [achievement_id] => 1 [achiever_id] => 1 [achiever_type] => App\User [points] => 1 [unlocked_at] => 2018-02-21 19:02:54 [created_at] => 2018-02-21 19:02:54 [updated_at] => 2018-02-21 19:02:54 ) [changes:protected] => Array ( ) [dates:protected] => Array ( ) [dateFormat:protected] => [appends:protected] => Array ( ) [dispatchesEvents:protected] => Array ( ) [observables:protected] => Array ( ) [relations:protected] => Array ( ) [touches:protected] => Array ( ) [timestamps] => 1 [hidden:protected] => Array ( ) [visible:protected] => Array ( ) [fillable:protected] => Array ( ) ) )

    Is really all that data necessary for just 2 achievements? Could be refactored on simpler and more usefull way?

    opened by byjokese 5
  • [Question] Cron Job for achivement progress?

    [Question] Cron Job for achivement progress?

    Several Achievements use some DB data for their progress, how can I update the point progress of the achievements?

    I was thinking on a cron job every X time that checks the DB and sets the progress. How can I implement something like that in Laravel?

    Thank you, great job!

    opened by byjokese 5
  • Add Achievements (SUGGESTION)

    Add Achievements (SUGGESTION)

    Is there a way down the road to make logic/frontend so one can create/edit achievements on the fly which could be integrated into a existing admin panel or something. That way staff of site could add new Achievements from site and not have to use terminal and edit code in like sublime text?

    I know this is long shot and probably not easy todo with how the Achievements system works now but thought i would throw the idea out there as it would make it more user friendly.

    opened by HDVinnie 5
  • Unlocked Achievements Count (SUGGESTION)

    Unlocked Achievements Count (SUGGESTION)

    built in unlocked Achievements counter so can be easily displayed in master/default view. Maybe like so:

    <?php $count = Auth::user()->unlockedAchievementsCount(); ?>

    with will in return display the count of all the unlocked Achievements that user has?

    opened by HDVinnie 4
  •  Class 'Gstt\Achievements\AchievementsServiceProvider' not found

    Class 'Gstt\Achievements\AchievementsServiceProvider' not found

    Hi,

    when i run composer update i got the following error:

      [Symfony\Component\Debug\Exception\FatalThrowableError]
      Class 'Gstt\Achievements\AchievementsServiceProvider' not found
    
    Script php artisan optimize handling the post-update-cmd event returned with error code 1
    

    composer.json ... "gstt/laravel-achievements": "^0.0.3", ...

    config/app.php ... Gstt\Achievements\AchievementsServiceProvider::class, ...

    Laravel Framework 5.4.15

    Looks like php artisan optimize crashed

    opened by orlyapps 3
  • Past Timestamps / Pass a Timestamp

    Past Timestamps / Pass a Timestamp

    Is there a way to pass a timestamp to the achievements? I'm trying to add achievements to an existing site and would like to pass a timestamp, so the achievements are recorded as being earned when the old activity happened.

    opened by droplister 2
  • Reset Specific Achievement

    Reset Specific Achievement

    I'm wondering if there is a way to edit an item in the achievement_progress table specifically? The problem that I have is that I need to reset the number of points back down to 0 if a user gets something wrong in whatever unlocked achievement they currently are working on.

    If I just go into the DB table itself and edit the specific row with the code below, I'm able to reset the points, but it also loads every single achievement into the achievement_progress table for the user, and I don't want to clutter this table up with achievements that aren't valid for some time.

            $progress = $this->inProgressAchievements()->first();
            if($progress) {
                $progress->points = 0;
                $progress->save()
            }
    

    This sends all possible achievements to the achievement_progress table, even though it does reset the achievement in progress.

    opened by brjohnson4 2
  • uuid on progress_details table

    uuid on progress_details table

    Hi

    Im trying to understand why the id of table achievement_progress uses a unique identifier:

    Uuid::uuid4()->toString();

    Wouldnt a regular incremented integer suffice?

    Thanks

    opened by ghost 2
  • Locked Achievements (SUGGESTION)

    Locked Achievements (SUGGESTION)

    A way to display a list of all achievements that are locked so users can see what they have yet to accomplish.

    something like so: http://imgur.com/tJf5DQE

    Progress for Locked Achievements would always be Locked by Default. As once its not locked its removed from locked list and added to Completed Achievements list

    feature request 
    opened by HDVinnie 2
  • Hard Fork

    Hard Fork

    It seems the author has stopped supporting this repository.

    If someone wants to, you can use: https://github.com/assada/laravel-achievements

    PHP 7.2+ Laravel 6/7/8 Auto-discovery package Code style improvements

    opened by assada 3
  • how to unlock the achievement within a month

    how to unlock the achievement within a month

    Currently i am working on new project and i have to create Made 20 appointments in one month achievement. So how can i set the unlocked_at field in achievement_progress table of one month timestamp from current date of making an appointment

    opened by yogirajshetye 0
  • Primary key of Achiever

    Primary key of Achiever

    As I see - now it forced to use id attribute in getOrCreateProgressForAchiever method.

    Why just not use getKey() instead of id here: https://github.com/gstt/laravel-achievements/blob/master/src/Achievement.php#L159

    opened by KostyaChoporov 0
  • Achievement description

    Achievement description

    Hi again,

    I've seen there is only a $description property which seems, according to the examples, to be used to display a message to the user when they reach the achievement. This is misleading, IMHO, since the description should describe (sic) the achievement and, therefore, be displayed in the list of achievements, so that users know what they have to do to reach the achievement. The $description should then describe the achievement, and a $message property should be displayed when the user unlocks the achievement.

    Since the property is public, we can obviously add another property to achieve this. But the examples in the documentation are not very explicit about the contexts.

    Also, since the package is designed for Laravel, what about adding localization? Rather than using description property, we could have a getDescription() method which would return the description translated (or not, depending on configuration). Also, a getMessage() method would display the message. A getter would allow developer to add some logic before returning the message/description (maybe you want to return the text with a different vocabulary, for example for adults or children users).

    I'd be happy to submit proposals and PRs after discussing this.

    opened by Arcesilas 0
Releases(v1.1-beta1)
  • v1.1-beta1(Sep 15, 2018)

    Laravel-Achievements v1.1 Beta 1

    Included features

    Beta 1 includes the following features:

    • Achievement Chains
    • Customized Table Names

    About v1.1

    Version 1.1 will target Laravel 5.6 and is scheduled to include the following features:

    Automatic Service Provider

    Version 1.1 will contain automatic service provider detection. Thanks to everyone that pointed out this absent feature.

    Improved Documentation

    Version 1.1 will include documentation using Github Pages and will add more detailed explanation on how to use laravel-achievements both on backend and on frontend.

    Frontend Templates

    Version 1.1 will ship with blade templates to be used as a boilerplate for developers to use on their projects. The templates included with the projects will be:

    • Achievement Progress Alert - a simple alert to be displayed whenever a user unlocks an Achievement.
    • Achievement Unlock Alert - a simple alert to be displayed whenever a user unlocks an Achievement
    • Achievement Listing Page - a page that will list all achievements enabled on the site.
    • Achievement Listing by User - a page that will list all achievements enabled for a specific User.

    Achievement Chains

    Version 1.1 will ship with Achievement Chains. As described on #30, it will allow developers to join within a single class multiple Achievements that track the same functionality. Thanks to @kgenly for suggestions on additional functionalities for this feature.

    Customized Table Names

    Version 1.1 will ship with Customized Table Names. As described on #33, it allows users to customize the table names that will store achievement related data. Thanks to @vgsh for requesting.

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Mar 8, 2017)

  • v1.0.0-RC2(Mar 2, 2017)

    Changelog

    Locked achievements

    • Two settings for implementing locked achievements: synced or unsynced.
      • Synced achievements automatically updates all Achievements in the Achiever list, adding a record on achievement_progress with 0 points for all Achievements the user still didn't progressed into.
        • This method overrides the previous behavior on the achievements() relationship, which previously only returned data for Achievements where the Achiever actually unlocked or made progress to.
        • This is the default method for new installations.
      • Unsynced achievements keep the previous behavior of the achievements() relationship and returns locked achievements via a DB query.
        • This method makes the achievements() relationship not return locked achievement data.

    Event handler

    • Achievements now trigger events when an Achiever unlocks or progresses in it. These events can be listened to provide "Achievement Unlocked" notifications.
    • Previous handlers whenUnlocked and whenProgressed on the Achievement classes are now documented and can also be added in order to trigger events specific to these Achievements.

    Documentation

    • Updated documentation with the newest additions.
    • Added a docs folder for further documentation on GitHub Pages.

    Extras

    • addProgressToAchiever() now has a default value of 1 point. Previous behavior is unaltered.
    • Source is now PSR-4 compatible for better readability.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-RC1(Feb 22, 2017)

Owner
null
⚡ Laravel Charts — Build charts using laravel. The laravel adapter for Chartisan.

What is laravel charts? Charts is a Laravel library used to create Charts using Chartisan. Chartisan does already have a PHP adapter. However, this li

Erik C. Forés 31 Dec 18, 2022
Laravel Kickstart is a Laravel starter configuration that helps you build Laravel websites faster.

Laravel Kickstart What is Laravel Kickstart? Laravel Kickstart is a Laravel starter configuration that helps you build Laravel websites faster. It com

Sam Rapaport 46 Oct 1, 2022
Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

null 9 Dec 14, 2022
Laravel Segment is an opinionated, approach to integrating Segment into your Laravel application.

Laravel Segment Laravel Segment is an opinionated, approach to integrating Segment into your Laravel application. Installation You can install the pac

Octohook 13 May 16, 2022
Laravel Sanctum support for Laravel Lighthouse

Lighthouse Sanctum Add Laravel Sanctum support to Lighthouse Requirements Installation Usage Login Logout Register Email Verification Forgot Password

Daniël de Wit 43 Dec 21, 2022
Bring Laravel 8's cursor pagination to Laravel 6, 7

Laravel Cursor Paginate for laravel 6,7 Installation You can install the package via composer: composer require vanthao03596/laravel-cursor-paginate U

Pham Thao 9 Nov 10, 2022
A package that uses blade templates to control how markdown is converted to HTML inside Laravel, as well as providing support for markdown files to Laravel views.

Install Install via composer. $ composer require olliecodes/laravel-etched-blade Once installed you'll want to publish the config. $ php artisan vendo

Ollie Codes 19 Jul 5, 2021
A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic

A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic! concepts like , Hijri Dates & Arabic strings and so on ..

Adnane Kadri 49 Jun 22, 2022
Jetstrap is a lightweight laravel 8 package that focuses on the VIEW side of Jetstream / Breeze package installed in your Laravel application

A Laravel 8 package to easily switch TailwindCSS resources generated by Laravel Jetstream and Breeze to Bootstrap 4.

null 686 Dec 28, 2022
Laravel Jetstream is a beautifully designed application scaffolding for Laravel.

Laravel Jetstream is a beautifully designed application scaffolding for Laravel. Jetstream provides the perfect starting point for your next Laravel application and includes login, registration, email verification, two-factor authentication, session management, API support via Laravel Sanctum, and optional team management.

The Laravel Framework 3.5k Jan 8, 2023
Laravel Larex lets you translate your whole Laravel application from a single CSV file.

Laravel Larex Translate Laravel Apps from a CSV File Laravel Larex lets you translate your whole Laravel application from a single CSV file. You can i

Luca Patera 68 Dec 12, 2022
A Laravel package that adds a simple image functionality to any Laravel model

Laraimage A Laravel package that adds a simple image functionality to any Laravel model Introduction Laraimage served four use cases when using images

Hussein Feras 52 Jul 17, 2022
A Laravel extension for using a laravel application on a multi domain setting

Laravel Multi Domain An extension for using Laravel in a multi domain setting Description This package allows a single Laravel installation to work wi

null 658 Jan 6, 2023
Example of using abrouter/abrouter-laravel-bridge in Laravel

ABRouter Laravel Example It's a example of using (ABRouter Laravel Client)[https://github.com/abrouter/abrouter-laravel-bridge] Set up locally First o

ABRouter 1 Oct 14, 2021
Laravel router extension to easily use Laravel's paginator without the query string

?? THIS PACKAGE HAS BEEN ABANDONED ?? We don't use this package anymore in our own projects and cannot justify the time needed to maintain it anymore.

Spatie 307 Sep 23, 2022
Laravel application project as Sheina Online Store backend to be built with Laravel and VueJS

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Boas Aditya Christian 1 Jan 11, 2022
Postgis extensions for laravel. Aims to make it easy to work with geometries from laravel models.

Laravel Wrapper for PostgreSQL's Geo-Extension Postgis Features Work with geometry classes instead of arrays. $model->myPoint = new Point(1,2); //lat

Max 340 Jan 6, 2023
laravel - Potion is a pure PHP asset manager for Laravel 5 based off of Assetic.

laravel-potion Potion is a pure PHP asset manager for Laravel based off of Assetic. Description Laravel 5 comes with a great asset manager called Elix

Matthew R. Miller 61 Mar 1, 2022
Llum illuminates your Laravel projects speeding up your Github/Laravel development workflow

Llum illuminates your Laravel projects speeding up your Github/Laravel development workflow

Sergi Tur Badenas 110 Dec 25, 2022