Skip to content

Commit c5b9488

Browse files
committed
Updated readme and voucher events
1 parent b5feb06 commit c5b9488

File tree

3 files changed

+111
-23
lines changed

3 files changed

+111
-23
lines changed

README.md

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,31 +102,73 @@ try {
102102
}
103103
```
104104

105+
### Unredeem Vouchers
106+
Unredeeming voucher can be done by either providing a related redeemer entity, or by using a redeemer query filter to let the package find the redeemer for you.
107+
```php
108+
Vouchers::unredeem(string $code, Illuminate\Database\Eloquent\Model|null $entity = null, Closure(Illuminate\Database\Eloquent\Builder)|null $callback = null): bool;
109+
110+
try {
111+
$success = Vouchers::unredeem('123-456-789', $user);
112+
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherNotFoundException $e) {
113+
// Voucher was not found with the provided code.
114+
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherRedeemerNotFoundException $e) {
115+
// Voucher redeemer was not found.
116+
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherUnstartedException $e) {
117+
// Voucher is not yet started.
118+
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherExpiredException $e) {
119+
// Voucher is expired.
120+
}
121+
```
122+
Or if you don't care about the specific exceptions:
123+
```php
124+
try {
125+
$success = Vouchers::unredeem('123-456-789', $user);
126+
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherException $e) {
127+
// Voucher was not possible to unredeem.
128+
}
129+
```
130+
Without specifying the redeemer entity, which will use the first redeemer found:
131+
```php
132+
try {
133+
$success = Vouchers::unredeem('123-456-789');
134+
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherException $e) {
135+
// Voucher was not possible to unredeem.
136+
}
137+
```
138+
With specifying a redeemer query filter:
139+
```php
140+
try {
141+
$success = Vouchers::unredeem(code: '123-456-789', callback: fn (Illuminate\Database\Eloquent\Builder $query) => $query->where('metadata->foo', 'bar));
142+
} catch (FrittenKeeZ\Vouchers\Exceptions\VoucherException $e) {
143+
// Voucher was not possible to unredeem.
144+
}
145+
```
146+
105147
### Options
106148
Besides defaults specified in `config/vouchers.php`, one can override options when generating codes or creating vouchers.
107149
Following methods apply to `Vouchers::generate()`, `Vouchers::batch()` and `Vouchers::create()` calls.
108150
```php
109151
// Override characters list.
110-
Vouchers::withCharacters(string $characters);
152+
Vouchers::withCharacters(string|null $characters);
111153
// Override code mask.
112-
Vouchers::withMask(string $mask);
154+
Vouchers::withMask(string|null $mask);
113155
// Override code prefix.
114-
Vouchers::withPrefix(string $prefix);
156+
Vouchers::withPrefix(string|null $prefix);
115157
// Disable code prefix.
116158
Vouchers::withoutPrefix();
117159
// Override code suffix.
118-
Vouchers::withSuffix(string $suffix);
160+
Vouchers::withSuffix(string|null $suffix);
119161
// Disable code suffix.
120162
Vouchers::withoutSuffix();
121163
// Override prefix and suffix separator.
122-
Vouchers::withSeparator(string $separator);
164+
Vouchers::withSeparator(string|null $separator);
123165
// Disable prefix and suffix separator.
124166
Vouchers::withoutSeparator();
125167
```
126168
Following methods only apply to `Vouchers::create()` call.
127169
```php
128170
// Add metadata to voucher.
129-
Vouchers::withMetadata(array $metadata);
171+
Vouchers::withMetadata(array|null $metadata);
130172
// Set voucher start time.
131173
Vouchers::withStartTime(DateTime|null $timestamp);
132174
// Set voucher start time using interval.
@@ -150,7 +192,7 @@ Vouchers::withEntities(Illuminate\Database\Eloquent\Model[] $entities);
150192
Vouchers::withEntities(Illuminate\Support\Collection<Illuminate\Database\Eloquent\Model> $entities);
151193
Vouchers::withEntities(Generator<Illuminate\Database\Eloquent\Model> $entities);
152194
// Set owning entity for voucher.
153-
Vouchers::withOwner(Illuminate\Database\Eloquent\Model $owner);
195+
Vouchers::withOwner(Illuminate\Database\Eloquent\Model|null $owner);
154196
```
155197
All calls are chainable and dynamic options will be reset when calling `Vouchers::create()` or `Vouchers::reset()`.
156198
```php
@@ -188,12 +230,32 @@ Voucher::redeeming(function (Voucher $voucher) {
188230
$voucher = Vouchers::withOwner($user)->create();
189231
Vouchers::redeem($voucher->code, $user);
190232
```
191-
To perform additional actions after a vouchers has been redeemed, subscribe to the `FrittenKeeZ\Vouchers\Models\Voucher::redeemed()` event.
233+
To perform additional actions after a voucher has been redeemed, subscribe to the `FrittenKeeZ\Vouchers\Models\Voucher::redeemed()` event.
192234
```php
193235
Voucher::redeemed(function (Voucher $voucher) {
194236
// Do some additional stuff here.
195237
});
196238
```
239+
To prevent a voucher to from being marked as unredeemed after first unredeeming, subscribe to the `FrittenKeeZ\Vouchers\Models\Voucher::shouldMarkUnredeemed()` event. Note that a voucher will still be marked as unredeemed if there are no more redeemers left.
240+
```php
241+
Voucher::shouldMarkUnredeemed(function (Voucher $voucher) {
242+
// Do some fancy checks here.
243+
return false;
244+
});
245+
```
246+
To prevent a voucher from being unredeemed altogether, subscribe to the `FrittenKeeZ\Vouchers\Models\Voucher::unredeeming()` event.
247+
```php
248+
Voucher::unredeeming(function (Voucher $voucher) {
249+
// Do some fancy checks here.
250+
return false;
251+
});
252+
```
253+
To perform additional actions after a voucher has been unredeemed, subscribe to the `FrittenKeeZ\Vouchers\Models\Voucher::unredeemed()` event.
254+
```php
255+
Voucher::unredeemed(function (Voucher $voucher) {
256+
// Do some additional stuff here.
257+
});
258+
```
197259

198260
### Traits
199261
For convenience we provide some traits for fetching vouchers and redeemers for related entities, fx. users.
@@ -211,7 +273,7 @@ HasVouchers::vouchers(): MorphMany;
211273
// Get all owned vouchers.
212274
$vouchers = $user->vouchers;
213275

214-
// Associated vouchers relationship.
276+
// Associated vouchers through VoucherEntity relationship.
215277
HasVouchers::associatedVouchers(): MorphToMany;
216278
// Get all associated vouchers.
217279
$vouchers = $user->associatedVouchers;
@@ -223,7 +285,7 @@ $entities = $user->voucherEntities;
223285
```
224286
You can also create vouchers owned by an entity using these convenience methods.
225287
```php
226-
HasVouchers::createVoucher(Closure|null $callback = null): object;
288+
HasVouchers::createVoucher(Closure(FrittenKeeZ\Vouchers\Vouchers)|null $callback = null): object;
227289

228290
// Without using callback.
229291
$voucher = $user->createVoucher();
@@ -232,7 +294,7 @@ $voucher = $user->createVoucher(function (FrittenKeeZ\Vouchers\Vouchers $voucher
232294
$vouchers->withPrefix('USR');
233295
});
234296

235-
HasVouchers::createVouchers(int $amount, Closure|null $callback = null): object|array;
297+
HasVouchers::createVouchers(int $amount, Closure(FrittenKeeZ\Vouchers\Vouchers)|null $callback = null): object|array;
236298

237299
// Without using callback.
238300
$vouchers = $user->createVouchers(3);
@@ -245,7 +307,7 @@ $vouchers = $user->createVouchers(3, function (FrittenKeeZ\Vouchers\Vouchers $vo
245307
### Helpers
246308
Check whether a voucher code is redeemable without throwing any errors.
247309
```php
248-
Vouchers::redeemable(string $code, Closure|null $callback = null): bool;
310+
Vouchers::redeemable(string $code, Closure(FrittenKeeZ\Vouchers\Models\Voucher)|null $callback = null): bool;
249311

250312
// Without using callback.
251313
$valid = Vouchers::redeemable('123-456-789');
@@ -254,6 +316,17 @@ $valid = Vouchers::redeemable('123-456-789', function (FrittenKeeZ\Vouchers\Mode
254316
return $voucher->hasPrefix('foo');
255317
});
256318
```
319+
Check whether a voucher code is unredeemable without throwing any errors.
320+
```php
321+
Vouchers::unredeemable(string $code, Closure(FrittenKeeZ\Vouchers\Models\Voucher)|null $callback = null): bool;
322+
323+
// Without using callback.
324+
$valid = Vouchers::unredeemable('123-456-789');
325+
// With using callback.
326+
$valid = Vouchers::unredeemable('123-456-789', function (FrittenKeeZ\Vouchers\Models\Voucher $voucher) {
327+
return $voucher->hasPrefix('foo');
328+
});
329+
```
257330
Check whether a voucher code exists, optionally also checking a provided list.
258331
```php
259332
Vouchers::exists(string $code, array $codes = []): bool;
@@ -274,6 +347,8 @@ Voucher::isExpired(): bool;
274347
Voucher::isRedeemed(): bool;
275348
// Whether voucher is redeemable.
276349
Voucher::isRedeemable(): bool;
350+
// Whether voucher is unredeemable.
351+
Voucher::isUnredeemable(): bool;
277352
```
278353

279354
### Scopes
@@ -297,12 +372,16 @@ Voucher::withExpired();
297372
Voucher::withoutExpired();
298373
// Scope voucher query to redeemed vouchers.
299374
Voucher::withRedeemed();
300-
// Scope voucher query to unredeemed vouchers.
375+
// Scope voucher query to without redeemed vouchers.
301376
Voucher::withoutRedeemed();
302377
// Scope voucher query to redeemable vouchers.
303378
Voucher::withRedeemable();
304-
// Scope voucher query to unredeemable vouchers.
379+
// Scope voucher query to without redeemable vouchers.
305380
Voucher::withoutRedeemable();
381+
// Scope voucher query to unredeemable vouchers.
382+
Voucher::withUnredeemable();
383+
// Scope voucher query to without unredeemable vouchers.
384+
Voucher::withoutUnredeemable();
306385
// Scope voucher query to have voucher entities, optionally of a specific type (class or alias).
307386
Voucher::withEntities(string|null $type = null);
308387
// Scope voucher query to specific owner type (class or alias).
@@ -314,9 +393,15 @@ Voucher::withoutOwner();
314393
```
315394

316395
## Testing
317-
Running tests can be done either through composer, or directly calling the PHPUnit binary.
396+
Running tests can be done either through composer, or directly calling the Pest binary.
318397
```bash
319398
composer test
399+
./vendor/bin/pest --parallel
400+
```
401+
It is also possible to run the tests with coverage report.
402+
```bash
403+
composer test-coverage
404+
./vendor/bin/pest --parallel --coverage
320405
```
321406

322407
## License

src/Models/Voucher.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public function unredeem(Redeemer $redeemer): bool
206206

207207
// Reset redeemed timestamp unless specified otherwise.
208208
// This will mark the voucher as unredeemed.
209-
if ($this->fireModelEvent('shouldMarkUnredeemed') !== false) {
209+
if ($this->fireModelEvent('shouldMarkUnredeemed') !== false || !$this->redeemers()->exists()) {
210210
$this->redeemed_at = null;
211211
}
212212

tests/Feature/Voucher/UnredeemingTest.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,24 @@
7676
test('should mark unredeemed event', function () {
7777
// Don't mark voucher as unredeemed for multiple uses.
7878
Voucher::shouldMarkUnredeemed(function (Voucher $voucher) {
79-
expect($voucher->isRedeemed())->toBeFalse();
79+
expect($voucher->isRedeemed())->toBeTrue();
8080

8181
return false;
8282
});
8383

84-
$voucher = Voucher::factory()->create();
85-
$redeemer = Redeemer::factory()->for(User::factory(), 'redeemer')->for($voucher)->create();
86-
Redeemer::factory()->for(User::factory(), 'redeemer')->for($voucher)->create();
84+
$voucher = Voucher::factory()->redeemed()->create();
85+
$primary = Redeemer::factory()->for(User::factory(), 'redeemer')->for($voucher)->create();
86+
$secondary = Redeemer::factory()->for(User::factory(), 'redeemer')->for($voucher)->create();
8787

88-
expect($voucher->isRedeemed())->toBeFalse();
88+
expect($voucher->isRedeemed())->toBeTrue();
8989
expect($voucher->redeemers)->not->toBeEmpty();
9090
expect($voucher->isUnredeemable())->toBeTrue();
91-
expect($voucher->unredeem($redeemer))->toBeTrue();
92-
expect($voucher->isRedeemed())->toBeFalse();
91+
expect($voucher->unredeem($primary))->toBeTrue();
92+
expect($voucher->isRedeemed())->toBeTrue();
9393
expect($voucher->redeemers)->not->toBeEmpty();
94+
expect($voucher->unredeem($secondary))->toBeTrue();
95+
expect($voucher->isRedeemed())->toBeFalse();
96+
expect($voucher->redeemers)->toBeEmpty();
9497
});
9598

9699
/**

0 commit comments

Comments
 (0)