frittenkeez / laravel-vouchers
Laravel 9+ 优惠券系统
Requires
- php: ^8.1
- illuminate/config: ^9.0|^10.0|^11.0
- illuminate/console: ^9.0|^10.0|^11.0
- illuminate/database: ^9.0|^10.0|^11.0
- illuminate/support: ^9.0|^10.0|^11.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.4
- illuminate/support: ^9.45|^10.0|^11.0
- laravel/pint: ^1.16
- nesbot/carbon: ^2.63
- orchestra/testbench: ^7.0|^8.0|^9.0
- phpunit/phpunit: ^9.6|^10.5
README
目录
安装
使用Composer安装此包
composer require frittenkeez/laravel-vouchers
升级
请阅读升级指南。
变更日志
请阅读发布说明。
配置
使用Artisan命令发布配置
php artisan vendor:publish --tag=config --provider="FrittenKeeZ\Vouchers\VouchersServiceProvider"
使用Artisan命令发布迁移
php artisan vendor:publish --tag=migrations --provider="FrittenKeeZ\Vouchers\VouchersServiceProvider"
别忘了运行迁移
php artisan migrate
通过 config/vouchers.php
修改基本配置 - 应有良好的文档,因此此处不需要描述所有选项。
使用
此包附带一个易于使用的门面 Vouchers
,全限定名称为 FrittenKeeZ\Vouchers\Facades\Vouchers
。
生成代码
生成代码时无需检查是否存在;如果未指定,则使用配置中的默认值。
Vouchers::generate(string|null $mask = null, string|null $characters = null): string; $code = Vouchers::generate('***-***-***', '1234567890');
也可以批量生成代码;这些代码将与现有代码进行核对。
Vouchers::batch(int amount): array; $codes = Vouchers::batch(10);
创建优惠券
生成一个或多个优惠券同样简单。
Vouchers::create(int $amount = 1): object|array; $voucher = Vouchers::create(); $vouchers = Vouchers::create(10);
兑换优惠券
兑换优惠券需要提供一个兑换实体。
还可以提供兑换实体的额外元数据。
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. }
选项
除了在 config/vouchers.php
中指定的默认值外,在生成代码或创建优惠券时还可以覆盖选项。
以下方法适用于 Vouchers::generate()
、Vouchers::batch()
和 Vouchers::create()
调用。
// 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();
以下方法仅适用于 Vouchers::create()
调用。
// 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);
所有调用都是可链式调用的,并且当调用 Vouchers::create()
或 Vouchers::reset()
时,动态选项将重置。
$voucher = Vouchers::withMask('***-***-***') ->withMetadata(['foo' => 'bar']) ->withExpireDateIn(CarbonInterval::create('P30D')) ->create(); $voucher = Vouchers::withOwner($user)->withPrefix('USR');
事件
在事件期间,Voucher::$redeemer
将设置为活动兑换者(FrittenKeeZ\Vouchers\Models\Redeemer
)。
默认情况下,优惠券在首次使用后将被标记为兑换,这并不总是期望的结果。
要允许优惠券被多次兑换,请订阅 FrittenKeeZ\Vouchers\Models\Voucher::shouldMarkRedeemed()
事件。
Voucher::shouldMarkRedeemed(function (Voucher $voucher) { // Do some fancy checks here. return false; });
要防止优惠券被兑换,请订阅 FrittenKeeZ\Vouchers\Models\Voucher::redeeming()
事件。
Voucher::redeeming(function (Voucher $voucher) { // Do some fancy checks here. return false; });
要防止优惠券被除相关用户之外的人兑换。
Voucher::redeeming(function (Voucher $voucher) { return $voucher->redeemer->redeemer->is($voucher->owner); }); /* ... */ $voucher = Vouchers::withOwner($user)->create(); Vouchers::redeem($voucher->code, $user);
要在优惠券兑换后执行额外操作,请订阅 FrittenKeeZ\Vouchers\Models\Voucher::redeemed()
事件。
Voucher::redeemed(function (Voucher $voucher) { // Do some additional stuff here. });
特性
为了方便,我们提供了一些特性,用于获取相关实体(如用户)的优惠券和兑换者。
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;
您还可以使用这些便利方法创建实体拥有的优惠券。
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'); });
助手函数
检查优惠券代码是否可兑换,而不抛出任何错误。
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'); });
检查优惠券代码是否存在,可选地还可以检查提供的列表。
Vouchers::exists(string $code, array $codes = []): bool; $exists = Vouchers::exists('123-456-789', ['987-654-321']);
在Voucher模型上提供额外的助手函数方法。
// 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;
作用域
为了方便,我们还提供了与助手函数匹配的Voucher作用域。
// 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();
测试
可以通过composer或直接调用PHPUnit二进制文件运行测试。
composer test
许可
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。