Loyalty by Elvin Qulizada
CommunityAttach points earning, tiers, and redeemable rewards to any Eloquent model — not just customers. Earning rules are config, not code: list a model class, a condition, and how many points to award, and the package listens for you. No observers to register, no Facade calls to sprinkle through your app.
filament/
namespace. Review the source and install at your own risk. Found
malware or an unresolved security issue the author won't
address?
Report it
.
Author:
Elvin Qulizada
Documentation
Attach points earning, tiers, and redeemable rewards to any Eloquent model — not just customers. Earning rules are config, not code: list a model class, a condition, and how many points to award, and the package listens for you. No observers to register, no Facade calls to sprinkle through your app.
#Installation
composer require elvin-qulizade/filament-loyalty
php artisan vendor:publish --tag="filament-loyalty-migrations"
php artisan migrate
php artisan vendor:publish --tag="filament-loyalty-config"
#Usage
- Add the trait and contract to any Eloquent model you want to earn/spend points:
use ElvinQulizade\Loyalty\Concerns\HasLoyaltyPoints;
use ElvinQulizade\Loyalty\Contracts\LoyaltyMember;
class Customer extends Model implements LoyaltyMember
{
use HasLoyaltyPoints;
}
- Configure an earning rule — no listener, no observer:
// config/filament-loyalty.php
'rules' => [
\App\Models\Order::class => [
'member' => fn (\App\Models\Order $order) => $order->customer,
'when' => fn (\App\Models\Order $order) => $order->status === 'paid',
'points' => fn (\App\Models\Order $order) => (int) floor($order->total),
],
],
'tiers' => [
'bronze' => 0,
'silver' => 500,
'gold' => 2000,
],
[!WARNING] Rules use closures, so this config file cannot be cached:
php artisan config:cachefails with "Your configuration files are not serializable" as soon as arulesentry exists. This is a known v1 limitation — don't runconfig:cache(oroptimize) in a deployment that uses this package's rules yet.
- Show the ledger on the member's own resource:
use ElvinQulizade\Loyalty\Filament\RelationManagers\LoyaltyRelationManager;
public static function getRelations(): array
{
return [
LoyaltyRelationManager::class,
];
}
That's it — every order that matches your when condition earns points automatically, the
Customer's "Loyalty" tab shows the full ledger with the current balance/lifetime
points/tier, and staff can manually adjust points (with a required reason) or redeem a
reward on the member's behalf from that same tab.
Tiers are based on lifetime earned points, not current balance — redeeming a reward never demotes a member's tier.
[!NOTE] "Adjust points" and "Redeem reward" are visible to anyone who can already open the member's resource page in your panel — there's no separate permission for them in v1. If you need finer control, subclass
LoyaltyRelationManagerand overridetable(), or gate access to the member resource itself.
- Manage the reward catalog by adding the resource to your panel:
use ElvinQulizade\Loyalty\Filament\Resources\LoyaltyRewardResource;
public function panel(Panel $panel): Panel
{
return $panel
->resources([
LoyaltyRewardResource::class,
]);
}
- Call the engine directly wherever code, not a panel action, should earn/spend points:
$customer->earnLoyaltyPoints(50, reason: 'Referral bonus');
$customer->redeemLoyaltyPoints(20, reason: 'Manual redemption');
$customer->adjustLoyaltyPoints(-10, 'Correcting a duplicate award'); // reason required
Drop ElvinQulizade\Loyalty\Filament\LoyaltyTierColumn::make() into any table, or
ElvinQulizade\Loyalty\Filament\LoyaltyTierEntry::make() into any infolist (e.g. the
member's own list view or view page), to show the tier badge outside the Loyalty tab too.
Other public pieces you may want directly:
use ElvinQulizade\Loyalty\Events\TierChanged; // fired by earn/adjust when loyaltyTier() changes
use ElvinQulizade\Loyalty\Exceptions\InsufficientPointsException;
use ElvinQulizade\Loyalty\Exceptions\RewardUnavailableException;
use ElvinQulizade\Loyalty\Support\LoyaltyRewardRedeemer; // LoyaltyRewardRedeemer::redeem($member, $reward)
$member->loyaltyBalance(), $member->loyaltyLifetimePoints() and $member->loyaltyTier()
are plain read methods — safe to call anywhere, including outside the panel.
v1 limitation: a rule only ever earns once per source row — editing a paid order's shipping address later won't double-award, but there's no "re-earn when the points amount changes" support yet either.
#Testing
composer test
#Changelog
Please see CHANGELOG for more information on what has changed recently.
#Contributing
Please see CONTRIBUTING for details.
#Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
#Credits
#License
The MIT License (MIT). Please see License File for more information.
The author
Full-stack developer specializing in Laravel, Filament. I build modern web applications, SaaS platforms, and developer tools, with a strong focus on clean architecture, reusable components, and great developer experience. I enjoy contributing to the Filament ecosystem by creating plugins and tools that solve real-world development needs.
From the same author
GDPR
Data retention policies, automatic anonymization and personal data exports for Filament v5 panels.
Author:
Elvin Qulizada
Versions
Adds WordPress-style version history to any Filament resource, with a field-level diff viewer and one-click restore of past versions.
Author:
Elvin Qulizada
Featured Plugins
A selection of plugins curated by the Filament team
Soft Theme
A theme that gives panels a warm, approachable look with rounded shapes, gentle colors, and serif headings.
Filament
Advanced Tables (formerly Filter Sets)
Supercharge your tables with powerful features like user-customizable views, quick filters, multi-column sorting, advanced table searching, convenient view management, and more. Compatible with Resource Panel Tables, Relation Managers, Table Widgets, and Table Builder!
Kenneth Sese
Spotlight Pro
Browse your Filament Panel with ease. Filament Spotlight Pro adds a Spotlight like Command Palette to your Filament Panel.
Dennis Koch