PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.1
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
fluent-cart / app / Services / Filter / CouponFilter.php

CouponFilter.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.1, at app/Services/Filter/CouponFilter.php

185 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Filter;
4
5 use FluentCart\App\Models\Coupon;
6 use FluentCart\App\Services\DateTime\DateTime;
7 use FluentCart\Framework\Database\Orm\Builder;
8 use FluentCart\Framework\Support\Str;
9
10 class CouponFilter extends BaseFilter
11 {
12
13 public function buildQuery(): Builder
14 {
15 $query = parent::buildQuery();
16
17 // The listing must show how many times each coupon was actually
18 // applied — the stored use_count column can lag (it is decremented on
19 // cancel and starts at 0 for imported rows). Same derived aggregate
20 // CouponResource::get() already exposes.
21 $query->withCount([
22 'appliedCoupons as total_items' => function ($query) {
23 $query->selectRaw('count(*)');
24 }
25 ]);
26
27 return $query;
28 }
29
30 public function applySimpleFilter(?string $search = null): void
31 {
32
33 $this->query->when($search ?? $this->search, function ($query, $search) {
34 return $query
35 ->where(function ($query) use ($search) {
36 if (Str::of($search)->contains('%')) {
37 $query
38 ->where('type', 'percentage')
39 ->search([
40 'amount' => [
41 'column' => 'amount',
42 'operator' => 'like_all',
43 'value' => Str::of($search)->remove('%')->toString()
44 ],
45 ]);
46 } else {
47
48 $searchArray = [
49 'title' => [
50 'column' => 'title',
51 'operator' => 'like_all',
52 'value' => $search
53 ],
54 'code' => [
55 'column' => 'code',
56 'operator' => 'or_like_all',
57 'value' => $search
58 ],
59 'id' => [
60 'column' => 'id',
61 'operator' => 'or_like_all',
62 'value' => $search
63 ]
64 ];
65 if (is_numeric($search)) {
66 $searchArray['amount'] = [
67 'column' => 'amount',
68 'operator' => 'or_where',
69 'value' => $search * 100
70 ];
71 }
72
73
74 $query->search($searchArray);
75 }
76 });
77 });
78 }
79
80
81 public function tabsMap(): array
82 {
83 return [
84 'active' => 'status',
85 //'disabled' => 'status',
86 'expired' => 'status',
87 ];
88 }
89
90 public function getModel(): string
91 {
92 return Coupon::class;
93 }
94
95 public static function getFilterName(): string
96 {
97 return 'coupons';
98 }
99
100 /**
101 * No screen key: CouponTable.js sends no `with` at all. AllCoupons.vue and
102 * CouponsTableMobile.vue render the real `use_count` column off the coupons
103 * table, so no admin screen needs a relation here.
104 *
105 * PUBLIC key — `appliedCouponsCount` was an accepted request value before
106 * this allow-list existed, so it stays one. "No consumer in our repos" is
107 * not evidence of no consumer, and a name that used to answer and now
108 * silently does not is the regression this tier exists to prevent.
109 *
110 * No permission bar: what comes back is a NUMBER, not the orders. A caller
111 * learns how many times a coupon has been redeemed, which the coupon row's
112 * own `use_count` column already tells them on the same response.
113 *
114 * `appliedCoupons` itself stays denied on its own merit: the ROWS say which
115 * orders used which coupon, and that is order data behind `orders/view`.
116 *
117 * @return array<string, callable>
118 */
119 protected function allowedWiths(): array
120 {
121 return [
122 'appliedCouponsCount' => [$this, 'publicAppliedCouponsCount'],
123 ];
124 }
125
126 /**
127 * `with[]=appliedCouponsCount` — a public entry point. A count is not a
128 * special case: it is a callback that calls withCount(), which surfaces as
129 * the `applied_coupons_count` attribute.
130 *
131 * @param \FluentCart\Framework\Database\Orm\Builder $query
132 * @return \FluentCart\Framework\Database\Orm\Builder
133 */
134 protected function publicAppliedCouponsCount($query)
135 {
136 return $query->withCount('appliedCoupons');
137 }
138
139 /**
140 * Sent by Orders/Coupon.vue when suggesting coupons to apply to an order.
141 *
142 * @return array<string, callable>
143 */
144 protected function allowedScopes(): array
145 {
146 return [
147 'active' => [$this, 'activeScope'],
148 ];
149 }
150
151 /**
152 * Declared `($query)` on purpose: the array form of a `scopes` entry would
153 * otherwise hand this callback raw client arguments, and Coupon's
154 * `scopeActive()` takes none.
155 *
156 * @param \FluentCart\Framework\Database\Orm\Builder $query
157 * @return \FluentCart\Framework\Database\Orm\Builder
158 */
159 protected function activeScope($query)
160 {
161 return $query->scopes(['active']);
162 }
163
164
165 public function applyActiveViewFilter(?string $activeView = null): void
166 {
167 $activeView = $activeView ?? $this->activeView;
168 $tabsMap = $this->tabsMap();
169
170 if ($activeView === 'expired') {
171 $this->query->where(function ($query) {
172 $query->where('end_date', '<', DateTime::gmtNow())
173 ->where('end_date', '!=', '0000-00-00 00:00:00')
174 ->whereNotNull('end_date');
175 })
176 ->orWhere('status', '!=', 'active');
177 return;
178 }
179
180 $this->query->when($activeView, function ($query, $activeView) use ($tabsMap) {
181 $query->where($tabsMap[$activeView], $activeView);
182 });
183 }
184 }
185