Looking to hire Laravel developers? Try LaraJobs

laravel-badges maintained by insynnia

Description
Badge, trigger and award engine for Laravel. Award badges to entities via named triggers or manually — idempotent and framework-native.
Author
Insynnia
Last update
2026/06/18 02:54 (dev-main)
License
Links
Downloads
0

Comments
comments powered by Disqus

No multi-tenancy, no billing, no dashboard. Just the badge logic — drop it into any Laravel app and start awarding.

✨ Features

  • 🎖️ Badges with name, description, image, category and tier (bronzeplatinum).
  • Triggers — award a badge by firing a named slug (first-login, 100-sales, …).
  • 🪪 Entities keyed by your external_id, auto-created on first award.
  • ♻️ Idempotent awards — firing twice never duplicates.
  • 🗂️ Metadata — attach arbitrary JSON to badges, entities and awards.
  • 🧩 Bring your own HTTP — ships the domain logic, stays unopinionated about routes & auth.
  • 🧪 Fully tested, portable across SQLite / MySQL / PostgreSQL.

📦 Requirements

  • PHP 8.2+
  • Laravel 11, 12 or 13

🚀 Installation

composer require insynnia/laravel-badges
php artisan migrate

Publish config / migrations if you want to tweak them:

php artisan vendor:publish --tag=badges-config
php artisan vendor:publish --tag=badges-migrations

🧠 Concepts

Model What it is
Badge A named award (name, description, image, category, tier, metadata).
Trigger A slug that, when fired, awards a specific badge.
Entity A recipient, identified by your own external_id (e.g. a user id).
EntityBadge The award record linking an entity to a badge (unique per pair).
fireTrigger('first-login', 'user_123')
        │
        ▼
   Trigger(slug) ──▶ Badge ──▶ EntityBadge ◀── Entity(external_id)
                                   (unique entity + badge)

⚡ Quick start

use Insynnia\Badges\BadgeService;
use Insynnia\Badges\Models\Badge;
use Insynnia\Badges\Models\Trigger;

$badge = Badge::create(['name' => 'First Login', 'tier' => 'bronze']);
Trigger::create(['badge_id' => $badge->id, 'slug' => 'first-login']);

$badges = app(BadgeService::class);

// Fire a trigger — auto-creates the entity, awards the badge once.
$result = $badges->fireTrigger('first-login', externalEntityId: 'user_123');
// ['awarded' => true, 'badge' => Badge, 'reason' => 'Badge awarded successfully.']

// Award directly by badge id (bypasses triggers).
$badges->awardManually($badge->id, 'user_123');

// Revoke.
$badges->revoke($badge->id, 'user_123');

📖 API reference

BadgeService is the entry point. fireTrigger() and awardManually() both return array{awarded: bool, badge: ?Badge, reason: string} and are idempotent — a repeat award returns awarded => false.

Method Description
fireTrigger(string $slug, string $entityId, array $metadata = []) Fire a trigger by slug and award its badge.
awardManually(string $badgeId, string $entityId, array $metadata = []) Award a badge by id, bypassing triggers.
revoke(string $badgeId, string $entityId): bool Remove an award. Returns true if a record was deleted.

Querying

Badge::active()->byCategory('streak')->byTier('gold')->get();
$entity->badges;                       // badges an entity holds
Badge::find($id)->entities;            // entities that hold a badge
$badge->image_url;                     // resolved via the configured disk

Badge images

image_url resolves image_path through the disk in config/badges.php (defaults to public). Store the file yourself and persist its path:

$badge->update(['image_path' => $path]); // $badge->image_url is now resolvable

🌐 HTTP layer

This package ships the domain logic only — no routes or controllers — so it stays unopinionated about your API shape, auth and pagination. Wire BadgeService into your own controllers. A typical mapping:

Method & path Call
POST /trigger/{slug} fireTrigger($slug, $entityId, $meta)
POST /entities/{id}/badges awardManually($badgeId, $id, $meta)
DELETE /entities/{id}/badges/{badge} revoke($badgeId, $id)

🧪 Testing

composer install
composer test

🤝 Contributing

PRs welcome. Please add a test for any behaviour change and run composer test before opening a pull request.

🔒 Security

If you discover a security issue, please email the maintainers instead of using the issue tracker.

📝 Changelog

See CHANGELOG.md for what has changed recently.

📄 License

The MIT License (MIT). See LICENSE.