frittenkeez/laravel-vouchers

Laravel 9+ 优惠券系统

0.5.0 2024-06-13 15:35 UTC

This package is auto-updated.

Last update: 2024-09-13 16:09:18 UTC


README

Packagist Downloads License GitHub Workflow Status

目录

安装

使用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)。有关更多信息,请参阅许可证文件