php 49 lines · 7 steps

How a custom coupon rule validates in Laravel

A Laravel validation rule checks a coupon code against existence, status, expiry, limits, and per-user reuse.

Explained by highlit
1<?php
2 
3namespace App\Rules;
4 
5use App\Models\Coupon;
6use Closure;
7use Illuminate\Contracts\Validation\ValidationRule;
8 
9class Redeemable implements ValidationRule
10{
11 public function __construct(protected ?int $userId = null)
12 {
13 }
14 
15 public function validate(string $attribute, mixed $value, Closure $fail): void
16 {
17 $coupon = Coupon::whereRaw('LOWER(code) = ?', [strtolower($value)])->first();
18 
19 if (! $coupon) {
20 $fail('The :attribute is not a valid coupon.')->translate();
21 
22 return;
23 }
24 
25 if (! $coupon->is_active) {
26 $fail('This coupon is no longer active.');
27 
28 return;
29 }
30 
31 if ($coupon->expires_at !== null && $coupon->expires_at->isPast()) {
32 $fail('This coupon expired on :date.')->translate([
33 'date' => $coupon->expires_at->toFormattedDateString(),
34 ]);
35 
36 return;
37 }
38 
39 if ($coupon->max_redemptions !== null && $coupon->redemptions_count >= $coupon->max_redemptions) {
40 $fail('This coupon has reached its redemption limit.');
41 
42 return;
43 }
44 
45 if ($this->userId !== null && $coupon->redemptions()->where('user_id', $this->userId)->exists()) {
46 $fail('You have already redeemed this coupon.');
47 }
48 }
49}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Custom rule objects let you encapsulate multi-step business logic behind Laravel's validation pipeline.
  2. 2Sequential guard clauses that each fail-and-return keep complex validation readable and short-circuited.
  3. 3Constructor parameters let a rule carry context, like the current user, into its checks.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How a custom coupon rule validates in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code