An way of implementing achievement on Laravel.
Execute this command on the Laravel root project folder:
composer require edulazaro/larawards
Configure the database details on the .env
file and execute the migrations using:
php artisan migrate
To create a new award (achievement, trophy, badge...) use this command, replacing FooAchievement
for the ward class name:
php artisan make:award FooAchievement
This will create a new award on the app/Awards/FooAchievement.php
.
An award can be of different types, like an achievement
or a badge
. To set the type, use thje $type
attribute:
/** @var string The award type. */
public $type = 'achievement';
An award can have one or more tiers. Each tier si something which can be awarded, like an achievement. Tiers allow to define similar achievements on a single file:
/** @var protected The award tiers. */
protected array $tiers = [
'comment_written' => [
'score' => 1,
'title' => 'First Comment Written',
],
'3_comments_written' => [
'score' => 3,
'title' => '3 Comments Written',
],
'5_comments_written' => [
'score' => 5,
'title' => '5 Comments Written',
],
'10_comments_written' => [
'score' => 10,
'title' => '10 Comments Written',
],
'20_comments_written' => [
'score' => 20,
'title' => '20 Comments Written',
],
];
Each tier has a title
, which is the achievement/badge name and also a score
,` which is the score needed to reward the users with the achievement/badge.
Finally, the score
method is the place to code the logic which retrieves the current award score for the awardablep
user:
/**
* Get the awardable score a user
*
* @return int
*/
public function score(): int
{
return $this->rewardable->comments()->count();
}
If you ant an model to accept rewards, you will need to use the HasWards
trait into it, like on this example:
namespace App\Models;
use EduLazaro\Larawards\Concerns\HasRewards;
class User
{
use HasRewards;
}
The achievements should be registered with the User model or another model including the HasRewards
trait using the AwardProvider
:
User::awardable(AchievementsBadge::class);
Or grouped:
User::awardableGroup('achievements', [
CommentsAchievement::class,
LessonsWatchedAchievement::class
]);
Awards can be registed to any model which uses the HasRewards
trait.
When you need to check an award, you can do this:
FooAchievement::scope($user)->check();
As you can see, the award needs to be scoped first to the user This will add the reward if it matches the requirement of any tier. awardables.
You can also perform queries to check many awards at a time. This will check all awards assigned to a user:
User::awardables()->check();
You can also select a specific group:
User::awardables()->group('top_awards')->check();
Or a specific type:
User::awardables()->where('type', 'achievement')->check();
Once an award is awarded, it will be added into the rewards
table.
If you want, you can specify an event to be fired using the $event
attribute:
namespace App\Awards;
use EduLazaro\Larawards\Concerns\IsAward;
use EduLazaro\Larawards\Contracts\AwardInterface;
use App\Events\AchievementUnlocked;
class CommentsAchievement implements AwardInterface
{
use IsAward;
// ...
protected string $event = AchievementUnlocked::class;
// ...
}
This event will be fired each time a user gets a reward.
To get the rewards for a user you can do:
$rewards = $user->rewards;
To check if a specific award and tier has been rewarded:
$isRewarded = $user->rewards()->where('name', 'comment_written')->exists();
You can query the rewards for many users:
use EduLazaro\Larawards\Models\Reward;
// ...
Reward::where('award_id', 'comments_achievement')->get();
This works in the same way as the standard Laravel morph maps.
When a reward is inserte dinto the database, the award id will be the class where the award is defined. However this looks ugly. To fix it, use the Award::enforceMap
method on any service provider:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use EduLazaro\Larawards\Collections\Awards;
use App\Awards\CommentsAchievement;
use App\Awards\LikesAchievement;
class AwardServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Awards::enforceMap([
'comments_achievement' => CommentsAchievement::class,
'likes_achievement' => LikesAchievement::class,
]);
}
}
This will assign the specified alias to the desided Award classes. For example, the App\Awards\CommentsAchievement
will be represented as comments_achievement
which is more readable.
The Laravel framework is open-sourced software licensed under the MIT license.