PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
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 trunk 1.2.0 All 47 releases
fluent-cart / app / Services / Filter / CouponFilter.php

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

203 lines 7.0 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 * @return array<string, array<string, mixed>>
102 */
103 protected static function sortableColumns(): array
104 {
105 return [
106 'id' => ['label' => __('ID', 'fluent-cart'), 'column' => 'id'],
107 'title' => ['label' => __('Title', 'fluent-cart'), 'column' => 'title'],
108 'code' => ['label' => __('Code', 'fluent-cart'), 'column' => 'code'],
109 'amount' => ['label' => __('Amount', 'fluent-cart'), 'column' => 'amount'],
110 'stackable' => ['label' => __('Stackable', 'fluent-cart'), 'column' => 'stackable'],
111 'status' => ['label' => __('Status', 'fluent-cart'), 'column' => 'status'],
112 // The table has always offered this as "Expiry Date"; the column
113 // behind it is end_date.
114 'expiry_date' => ['label' => __('Expiry Date', 'fluent-cart'), 'column' => 'end_date'],
115 ];
116 }
117
118 /**
119 * No screen key: CouponTable.js sends no `with` at all. AllCoupons.vue and
120 * CouponsTableMobile.vue render the real `use_count` column off the coupons
121 * table, so no admin screen needs a relation here.
122 *
123 * PUBLIC key — `appliedCouponsCount` was an accepted request value before
124 * this allow-list existed, so it stays one. "No consumer in our repos" is
125 * not evidence of no consumer, and a name that used to answer and now
126 * silently does not is the regression this tier exists to prevent.
127 *
128 * No permission bar: what comes back is a NUMBER, not the orders. A caller
129 * learns how many times a coupon has been redeemed, which the coupon row's
130 * own `use_count` column already tells them on the same response.
131 *
132 * `appliedCoupons` itself stays denied on its own merit: the ROWS say which
133 * orders used which coupon, and that is order data behind `orders/view`.
134 *
135 * @return array<string, callable>
136 */
137 protected function allowedWiths(): array
138 {
139 return [
140 'appliedCouponsCount' => [$this, 'publicAppliedCouponsCount'],
141 ];
142 }
143
144 /**
145 * `with[]=appliedCouponsCount` — a public entry point. A count is not a
146 * special case: it is a callback that calls withCount(), which surfaces as
147 * the `applied_coupons_count` attribute.
148 *
149 * @param \FluentCart\Framework\Database\Orm\Builder $query
150 * @return \FluentCart\Framework\Database\Orm\Builder
151 */
152 protected function publicAppliedCouponsCount($query)
153 {
154 return $query->withCount('appliedCoupons');
155 }
156
157 /**
158 * Sent by Orders/Coupon.vue when suggesting coupons to apply to an order.
159 *
160 * @return array<string, callable>
161 */
162 protected function allowedScopes(): array
163 {
164 return [
165 'active' => [$this, 'activeScope'],
166 ];
167 }
168
169 /**
170 * Declared `($query)` on purpose: the array form of a `scopes` entry would
171 * otherwise hand this callback raw client arguments, and Coupon's
172 * `scopeActive()` takes none.
173 *
174 * @param \FluentCart\Framework\Database\Orm\Builder $query
175 * @return \FluentCart\Framework\Database\Orm\Builder
176 */
177 protected function activeScope($query)
178 {
179 return $query->scopes(['active']);
180 }
181
182
183 public function applyActiveViewFilter(?string $activeView = null): void
184 {
185 $activeView = $activeView ?? $this->activeView;
186 $tabsMap = $this->tabsMap();
187
188 if ($activeView === 'expired') {
189 $this->query->where(function ($query) {
190 $query->where('end_date', '<', DateTime::gmtNow())
191 ->where('end_date', '!=', '0000-00-00 00:00:00')
192 ->whereNotNull('end_date');
193 })
194 ->orWhere('status', '!=', 'active');
195 return;
196 }
197
198 $this->query->when($activeView, function ($query, $activeView) use ($tabsMap) {
199 $query->where($tabsMap[$activeView], $activeView);
200 });
201 }
202 }
203