@@ -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
106148Besides defaults specified in ` config/vouchers.php ` , one can override options when generating codes or creating vouchers.
107149Following 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.
116158Vouchers::withoutPrefix();
117159// Override code suffix.
118- Vouchers::withSuffix(string $suffix);
160+ Vouchers::withSuffix(string|null $suffix);
119161// Disable code suffix.
120162Vouchers::withoutSuffix();
121163// Override prefix and suffix separator.
122- Vouchers::withSeparator(string $separator);
164+ Vouchers::withSeparator(string|null $separator);
123165// Disable prefix and suffix separator.
124166Vouchers::withoutSeparator();
125167```
126168Following 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.
131173Vouchers::withStartTime(DateTime|null $timestamp);
132174// Set voucher start time using interval.
@@ -150,7 +192,7 @@ Vouchers::withEntities(Illuminate\Database\Eloquent\Model[] $entities);
150192Vouchers::withEntities(Illuminate\Support\Collection<Illuminate \Database\Eloquent\Model > $entities);
151193Vouchers::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```
155197All 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();
189231Vouchers::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
193235Voucher::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
199261For 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.
215277HasVouchers::associatedVouchers(): MorphToMany;
216278// Get all associated vouchers.
217279$vouchers = $user->associatedVouchers;
@@ -223,7 +285,7 @@ $entities = $user->voucherEntities;
223285```
224286You 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
246308Check 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+ ```
257330Check whether a voucher code exists, optionally also checking a provided list.
258331``` php
259332Vouchers::exists(string $code, array $codes = []): bool;
@@ -274,6 +347,8 @@ Voucher::isExpired(): bool;
274347Voucher::isRedeemed(): bool;
275348// Whether voucher is redeemable.
276349Voucher::isRedeemable(): bool;
350+ // Whether voucher is unredeemable.
351+ Voucher::isUnredeemable(): bool;
277352```
278353
279354### Scopes
@@ -297,12 +372,16 @@ Voucher::withExpired();
297372Voucher::withoutExpired();
298373// Scope voucher query to redeemed vouchers.
299374Voucher::withRedeemed();
300- // Scope voucher query to unredeemed vouchers.
375+ // Scope voucher query to without redeemed vouchers.
301376Voucher::withoutRedeemed();
302377// Scope voucher query to redeemable vouchers.
303378Voucher::withRedeemable();
304- // Scope voucher query to unredeemable vouchers.
379+ // Scope voucher query to without redeemable vouchers.
305380Voucher::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).
307386Voucher::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
319398composer 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
0 commit comments