Downloads
Stars
Version
Install this package via Composer:
composer require frittenkeez/laravel-vouchers
Please read the upgrade guide.
Please read the release notes.
Publish config using Artisan command:
php artisan vendor:publish --tag=config --provider="FrittenKeeZ\Vouchers\VouchersServiceProvider"
Don't forget to run migrations:
php artisan migrate
Change basic configuration through config/vouchers.php
- it should be well documented, so no need to describe all options here.
This package comes with an ease-of-use facade Vouchers
with FQN FrittenKeeZ\Vouchers\Facades\Vouchers
.
Generating codes without checking if they exist; defaults from config will be used if not specified.
Vouchers::generate(string|null $mask = null, string|null $characters = null): string;
$code = Vouchers::generate('***-***-***', '1234567890');
Batch generation of codes is also possible; these will be checked against existing codes.
Vouchers::batch(int amount): array;
$codes = Vouchers::batch(10);
Generating one or more vouchers is just as simple.
Vouchers::create(int $amount = 1): object|array;
$voucher = Vouchers::create();
$vouchers = Vouchers::create(10);
Redeeming vouchers requires that one provides a redeemer entity.
Additional metadata for the redeemer can be provided.
Vouchers::redeem(string $code, Illuminate\Database\Eloquent\Model $entity, array $metadata = []): bool;
try {
$success = Vouchers::redeem('123-456-789', $user, ['foo' => 'bar']);
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherNotFoundException $e) {
// Code provided did not match any vouchers in the database.
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherAlreadyRedeemedException $e) {
// Voucher has already been redeemed.
}
Besides defaults specified in config/vouchers.php
, one can override options when generating codes or creating vouchers.
Following methods apply to Vouchers::generate()
, Vouchers::batch()
and Vouchers::create()
calls.
// Override characters list.
Vouchers::withCharacters(string $characters);
// Override code mask.
Vouchers::withMask(string $mask);
// Override code prefix.
Vouchers::withPrefix(string $prefix);
// Disable code prefix.
Vouchers::withoutPrefix();
// Override code suffix.
Vouchers::withSuffix(string $suffix);
// Disable code suffix.
Vouchers::withoutSuffix();
// Override prefix and suffix separator.
Vouchers::withSeparator(string $separator);
// Disable prefix and suffix separator.
Vouchers::withoutSeparator();
Following methods only apply to Vouchers::create()
call.
// Add metadata to voucher.
Vouchers::withMetadata(array $metadata);
// Set voucher start time.
Vouchers::withStartTime(DateTime $timestamp);
// Set voucher start time using interval.
Vouchers::withStartTimeIn(DateInterval $interval);
// Set voucher start date - time component is zeroed.
Vouchers::withStartDate(DateTime $timestamp);
// Set voucher start date using interval - time component is zeroed.
Vouchers::withStartDateIn(DateInterval $interval);
// Set voucher expire time.
Vouchers::withExpireTime(DateTime $timestamp);
// Set voucher expire time using interval.
Vouchers::withExpireTimeIn(DateInterval $interval);
// Set voucher expire date - time component is set to end of day (23:59:59).
Vouchers::withExpireDate(DateTime $timestamp);
// Set voucher expire date using interval - time component is set to end of day (23:59:59).
Vouchers::withExpireDateIn(DateInterval $interval);
// Set related entities to voucher.
Vouchers::withEntities(Illuminate\Database\Eloquent\Model ...$entities);
// Set owning entity for voucher.
Vouchers::withOwner(Illuminate\Database\Eloquent\Model $owner);
All calls are chainable and dynamic options will be reset when calling Vouchers::create()
or Vouchers::reset()
.
$voucher = Vouchers::withMask('***-***-***')
->withMetadata(['foo' => 'bar'])
->withExpireDateIn(CarbonInterval::create('P30D'))
->create();
$voucher = Vouchers::withOwner($user)->withPrefix('USR');
During events Voucher::$redeemer
will be set to the active redeemer (FrittenKeeZ\Vouchers\Models\Redeemer
).
By default vouchers will be marked as redeemed after one use, which is not always the desired outcome.
To allow a voucher to be redeemed multiple times, subscribe to the FrittenKeeZ\Vouchers\Models\Voucher::shouldMarkRedeemed()
event.
Voucher::shouldMarkRedeemed(function (Voucher $voucher) {
// Do some fancy checks here.
return false;
});
To prevent a voucher from being redeemed altogether, subscribe to the FrittenKeeZ\Vouchers\Models\Voucher::redeeming()
event.
Voucher::redeeming(function (Voucher $voucher) {
// Do some fancy checks here.
return false;
});
To prevent a voucher from being redeemed by anyone but the related user.
Voucher::redeeming(function (Voucher $voucher) {
return $voucher->redeemer->redeemer->is($voucher->owner);
});
/* ... */
$voucher = Vouchers::withOwner($user)->create();
Vouchers::redeem($voucher->code, $user);
To perform additional actions after a vouchers has been redeemed, subscribe to the FrittenKeeZ\Vouchers\Models\Voucher::redeemed()
event.
Voucher::redeemed(function (Voucher $voucher) {
// Do some additional stuff here.
});
For convenience we provide some traits for fetching vouchers and redeemers for related entities, fx. users.
FrittenKeeZ\Vouchers\Concerns\HasRedeemers
// Associated redeemers relationship.
HasRedeemers::redeemers(): MorphMany;
// Get all associated redeemers.
$redeemers = $user->redeemers;
FrittenKeeZ\Vouchers\Concerns\HasVouchers
// Owned vouchers relationship.
HasVouchers::vouchers(): MorphMany;
// Get all owned vouchers.
$vouchers = $user->vouchers;
// Associated vouchers relationship.
HasVouchers::associatedVouchers(): MorphToMany;
// Get all associated vouchers.
$vouchers = $user->associatedVouchers;
// Associated voucher entities relationship.
HasVouchers::voucherEntities(): MorphMany;
// Get all associated voucher entities.
$entities = $user->voucherEntities;
You can also create vouchers owned by an entity using these convenience methods.
HasVouchers::createVoucher(Closure|null $callback = null): object;
// Without using callback.
$voucher = $user->createVoucher();
// With using callback.
$voucher = $user->createVoucher(function (FrittenKeeZ\Vouchers\Vouchers $vouchers) {
$vouchers->withPrefix('USR');
});
HasVouchers::createVouchers(int $amount, Closure|null $callback = null): object|array;
// Without using callback.
$vouchers = $user->createVouchers(3);
// With using callback.
$vouchers = $user->createVouchers(3, function (FrittenKeeZ\Vouchers\Vouchers $vouchers) {
$vouchers->withPrefix('USR');
});
Check whether a voucher code is redeemable without throwing any errors.
Vouchers::redeemable(string $code, Closure|null $callback = null): bool;
// Without using callback.
$valid = Vouchers::redeemable('123-456-789');
// With using callback.
$valid = Vouchers::redeemable('123-456-789', function (FrittenKeeZ\Vouchers\Models\Voucher $voucher) {
return $voucher->hasPrefix('foo');
});
Check whether a voucher code exists, optionally also checking a provided list.
Vouchers::exists(string $code, array $codes = []): bool;
$exists = Vouchers::exists('123-456-789', ['987-654-321']);
Additional helpers methods on Voucher model.
// Whether voucher has prefix, optionally specifying a separator different from config.
Voucher::hasPrefix(string $prefix, string|null $separator = null): bool;
// Whether voucher has suffix, optionally specifying a separator different from config.
Voucher::hasSuffix(string $suffix, string|null $separator = null): bool;
// Whether voucher is started.
Voucher::isStarted(): bool;
// Whether voucher is expired.
Voucher::isExpired(): bool;
// Whether voucher is redeemed.
Voucher::isRedeemed(): bool;
// Whether voucher is redeemable.
Voucher::isRedeemable(): bool;
For convenience we also provide Voucher scopes matching the helper methods.
// Scope voucher query to a specific prefix, optionally specifying a separator different from config.
Voucher::withPrefix(string $prefix, string|null $separator = null);
// Scope voucher query to exclude a specific prefix, optionally specifying a separator different from config.
Voucher::withoutPrefix(string $prefix, string|null $separator = null);
// Scope voucher query to a specific suffix, optionally specifying a separator different from config.
Voucher::withSuffix(string $suffix, string|null $separator = null);
// Scope voucher query to exclude a specific suffix, optionally specifying a separator different from config.
Voucher::withoutSuffix(string $suffix, string|null $separator = null);
// Scope voucher query to started vouchers.
Voucher::withStarted();
// Scope voucher query to unstarted vouchers.
Voucher::withoutStarted();
// Scope voucher query to expired vouchers.
Voucher::withExpired();
// Scope voucher query to unexpired vouchers.
Voucher::withoutExpired();
// Scope voucher query to redeemed vouchers.
Voucher::withRedeemed();
// Scope voucher query to unredeemed vouchers.
Voucher::withoutRedeemed();
// Scope voucher query to redeemable vouchers.
Voucher::withRedeemable();
// Scope voucher query to unredeemable vouchers.
Voucher::withoutRedeemable();
// Scope voucher query to have voucher entities, optionally of a specific type (class or alias).
Voucher::withEntities(string|null $type = null);
// Scope voucher query to specific owner type (class or alias).
Voucher::withOwnerType(string $type);
// Scope voucher query to specific owner.
Voucher::withOwner(Illuminate\Database\Eloquent\Model $owner);
// Scope voucher query to no owners.
Voucher::withoutOwner();
Running tests can be done either through composer, or directly calling the PHPUnit binary.
composer test
To run tests with code coverage, please make sure that phpdbg
exists and is executable.
composer test-coverage
open tests/_reports/index.html
The MIT License (MIT). Please see License File for more information.