Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
60.00% covered (warning)
60.00%
18 / 30
28.57% covered (danger)
28.57%
2 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
CouponController
60.00% covered (warning)
60.00%
18 / 30
28.57% covered (danger)
28.57%
2 / 7
10.14
0.00% covered (danger)
0.00%
0 / 1
 index
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 create
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 store
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 edit
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 update
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 destroy
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 validated
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Controllers\admin;
4
5use App\Http\Controllers\Controller;
6use App\Models\Coupon;
7use Illuminate\Http\RedirectResponse;
8use Illuminate\Http\Request;
9use Illuminate\Validation\Rule;
10use Illuminate\View\View;
11
12/**
13 * Sales → Coupons. Percent / fixed discount codes with windows, limits and a
14 * minimum order. Gated by the `coupons` feature.
15 */
16class CouponController extends Controller
17{
18    public function index(Request $request): View
19    {
20        $coupons = Coupon::query()
21            ->when($request->filled('search'), fn ($q) => $q->where('code', 'like', '%' . $request->input('search') . '%'))
22            ->latest()
23            ->paginate(20)
24            ->withQueryString();
25
26        return view('admin.coupons.index', compact('coupons'));
27    }
28
29    public function create(): View
30    {
31        return view('admin.coupons.form', ['coupon' => new Coupon(['type' => 'percent', 'is_active' => true])]);
32    }
33
34    public function store(Request $request): RedirectResponse
35    {
36        Coupon::create($this->validated($request));
37
38        return redirect()->route('admin.coupons.index')->with('success', 'Coupon created.');
39    }
40
41    public function edit(Coupon $coupon): View
42    {
43        return view('admin.coupons.form', compact('coupon'));
44    }
45
46    public function update(Request $request, Coupon $coupon): RedirectResponse
47    {
48        $coupon->update($this->validated($request, $coupon));
49
50        return redirect()->route('admin.coupons.index')->with('success', 'Coupon updated.');
51    }
52
53    public function destroy(Coupon $coupon): RedirectResponse
54    {
55        $coupon->delete();
56
57        return redirect()->route('admin.coupons.index')->with('success', 'Coupon deleted.');
58    }
59
60    private function validated(Request $request, ?Coupon $coupon = null): array
61    {
62        $data = $request->validate([
63            'code' => ['required', 'string', 'max:40', Rule::unique('coupons', 'code')->ignore($coupon)],
64            'type' => ['required', Rule::in(['percent', 'fixed'])],
65            'value' => ['required', 'numeric', 'min:0'],
66            'min_order' => ['nullable', 'numeric', 'min:0'],
67            'max_discount' => ['nullable', 'numeric', 'min:0'],
68            'usage_limit' => ['nullable', 'integer', 'min:1'],
69            'per_customer_limit' => ['nullable', 'integer', 'min:1'],
70            'starts_at' => ['nullable', 'date'],
71            'ends_at' => ['nullable', 'date', 'after_or_equal:starts_at'],
72            'description' => ['nullable', 'string', 'max:255'],
73            'is_active' => ['nullable', 'boolean'],
74        ]);
75
76        $data['is_active'] = $request->boolean('is_active');
77        $data['code'] = strtoupper(trim($data['code']));
78
79        return $data;
80    }
81}