PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.18.0
GiveWP – Donation Plugin and Fundraising Platform v4.18.0
4.18.0 4.17.0 4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 All 257 releases
give / src / Subscriptions / Endpoints / ListSubscriptions.php

ListSubscriptions.php in GiveWP – Donation Plugin and Fundraising Platform 4.18.0, at src/Subscriptions/Endpoints/ListSubscriptions.php

293 lines 9.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\Subscriptions\Endpoints;
4
5 use Give\Donations\ValueObjects\DonationMetaKeys;
6 use Give\Donors\ValueObjects\DonorMetaKeys;
7 use Give\Framework\Database\DB;
8 use Give\Framework\QueryBuilder\QueryBuilder;
9 use Give\Subscriptions\ListTable\SubscriptionsListTable;
10 use Give\Subscriptions\ValueObjects\SubscriptionMode;
11 use Give\Subscriptions\ValueObjects\SubscriptionStatus;
12 use WP_REST_Request;
13 use WP_REST_Response;
14
15 class ListSubscriptions extends Endpoint
16 {
17 /**
18 * @var string
19 */
20 protected $endpoint = 'admin/subscriptions';
21
22 /**
23 * @var WP_REST_Request
24 */
25 protected $request;
26
27 /**
28 * @var SubscriptionsListTable
29 */
30 protected $listTable;
31
32 /**
33 * @inheritDoc
34 *
35 * @since 4.12.0 Add format parameter to start and end dates, replacing custom validation callback
36 */
37 public function registerRoute()
38 {
39 register_rest_route(
40 'give-api/v2',
41 $this->endpoint,
42 [
43 [
44 'methods' => 'GET',
45 'callback' => [$this, 'handleRequest'],
46 'permission_callback' => [$this, 'permissionsCheck'],
47 ],
48 'args' => [
49 'page' => [
50 'type' => 'integer',
51 'required' => false,
52 'default' => 1,
53 'minimum' => 1
54 ],
55 'perPage' => [
56 'type' => 'integer',
57 'required' => false,
58 'default' => 30,
59 'minimum' => 1
60 ],
61 'donations' => [
62 'type' => 'integer',
63 'required' => false,
64 'default' => 0
65 ],
66 'search' => [
67 'type' => 'string',
68 'required' => false
69 ],
70 'start' => [
71 'type' => 'string',
72 'required' => false,
73 'format' => 'date-time'
74 ],
75 'campaignId' => [
76 'type' => 'integer',
77 'required' => false,
78 'default' => 0
79 ],
80 'end' => [
81 'type' => 'string',
82 'required' => false,
83 'format' => 'date-time'
84 ],
85 'sortColumn' => [
86 'type' => 'string',
87 'required' => false,
88 'sanitize_callback' => 'sanitize_text_field',
89 ],
90 'sortDirection' => [
91 'type' => 'string',
92 'required' => false,
93 'enum' => [
94 'asc',
95 'desc',
96 ],
97 ],
98 'locale' => [
99 'type' => 'string',
100 'required' => false,
101 'default' => get_locale(),
102 ],
103 'testMode' => [
104 'type' => 'boolean',
105 'required' => false,
106 'default' => give_is_test_mode(),
107 ],
108 'return' => [
109 'type' => 'string',
110 'required' => false,
111 'default' => 'columns',
112 'enum' => [
113 'model',
114 'columns',
115 ],
116 ],
117 'status' => [
118 'type' => 'array',
119 'required' => false,
120 'items' => [
121 'type' => 'string',
122 'enum' => array_values(SubscriptionStatus::toArray()),
123 ],
124 'description' => 'Filter subscriptions by status. Accepts comma-separated list of SubscriptionStatus values (e.g., "active,expired,pending"). If not provided, excludes trash subscriptions by default.'
125 ],
126 ],
127 ]
128 );
129 }
130
131 /**
132 * @since 2.24.0
133 *
134 * @param WP_REST_Request $request
135 *
136 * @return WP_REST_Response
137 */
138 public function handleRequest(WP_REST_Request $request): WP_REST_Response
139 {
140 $this->request = $request;
141 $this->listTable = give(SubscriptionsListTable::class);
142
143 $subscriptions = $this->getSubscriptions();
144 $subscriptionsCount = $this->getTotalSubscriptionsCount();
145 $pageCount = (int)ceil($subscriptionsCount / $request->get_param('perPage'));
146
147 if ('model' === $this->request->get_param('return')) {
148 $items = $subscriptions;
149 } else {
150 $this->listTable->items($subscriptions, $this->request->get_param('locale') ?? '');
151 $items = $this->listTable->getItems();
152 }
153
154 return new WP_REST_Response(
155 [
156 'items' => $items,
157 'totalItems' => $subscriptionsCount,
158 'totalPages' => $pageCount,
159 ]
160 );
161 }
162
163 /**
164 * @since 4.12.0 add sort by donor name
165 * @since 2.24.0
166 *
167 * @return array
168 */
169 public function getSubscriptions(): array
170 {
171 $page = $this->request->get_param('page');
172 $perPage = $this->request->get_param('perPage');
173 $sortColumns = $this->listTable->getSortColumnById($this->request->get_param('sortColumn') ?: 'id');
174 $sortDirection = $this->request->get_param('sortDirection') ?: 'desc';
175
176 $query = give()->subscriptions->prepareQuery();
177
178 if ('donorName' === $sortColumns[0]) {
179 $query->attachMeta(
180 'give_donormeta',
181 'customer_id',
182 'donor_id',
183 [DonorMetaKeys::FIRST_NAME, 'firstName'],
184 [DonorMetaKeys::LAST_NAME, 'lastName']
185 );
186 $sortColumns = ['firstName', 'lastName'];
187 }
188
189 $query = $this->getWhereConditions($query);
190
191 foreach ($sortColumns as $sortColumn) {
192 $query->orderBy($sortColumn, $sortDirection);
193 }
194
195 $query->limit($perPage)
196 ->offset(($page - 1) * $perPage);
197
198 $subscriptions = $query->getAll();
199
200 if (!$subscriptions) {
201 return [];
202 }
203
204 return $subscriptions;
205 }
206
207 /**
208 * @since 2.24.0
209 *
210 * @return int
211 */
212 public function getTotalSubscriptionsCount(): int
213 {
214 $query = DB::table('give_subscriptions')->groupBy('payment_mode');
215 $query = $this->getWhereConditions($query);
216
217 return $query->count();
218 }
219
220 /**
221 * @since 4.18.0 Filter payment mode in WHERE instead of HAVING so the database can use indexes
222 * @since 4.12.0 Add "status" where condition
223 * @since 4.11.0 fix search by donor name or email
224 * @since 2.24.0 Replace Query Builder with Subscriptions model
225 * @since 2.21.0
226 *
227 * @param QueryBuilder $query
228 *
229 * @return QueryBuilder
230 */
231 private function getWhereConditions(QueryBuilder $query): QueryBuilder
232 {
233 $search = $this->request->get_param('search');
234 $start = $this->request->get_param('start');
235 $end = $this->request->get_param('end');
236 $campaignId = $this->request->get_param('campaignId');
237 $testMode = $this->request->get_param('testMode');
238 $status = $this->request->get_param('status');
239
240 if (!empty($status)) {
241 $query->whereIn('status', $status);
242 } else {
243 // Default behavior: exclude trashed subscriptions
244 $query->where('status', SubscriptionStatus::TRASHED, '<>');
245 }
246
247 if ($search) {
248 if (ctype_digit($search)) {
249 $query->where('id', $search);
250 } else {
251 $query->whereIn('customer_id', static function (QueryBuilder $builder) use ($search) {
252 $builder
253 ->from('give_donors')
254 ->distinct()
255 ->select('id')
256 ->whereLike('name', $search)
257 ->orWhereLike('email', $search);
258 });
259 }
260 }
261
262 if ($start && $end) {
263 $query->whereBetween('created', $start, $end);
264 } elseif ($start) {
265 $query->where('created', $start, '>=');
266 } elseif ($end) {
267 $query->where('created', $end, '<=');
268 }
269
270 if ($campaignId) {
271 $query
272 ->whereIn('id', static function (QueryBuilder $builder) use ($campaignId) {
273 $builder
274 ->from('give_donationmeta')
275 ->distinct()
276 ->select('meta_value')
277 ->where('meta_key', DonationMetaKeys::SUBSCRIPTION_ID)
278 ->whereIn('donation_id', static function (QueryBuilder $builder) use ($campaignId) {
279 $builder
280 ->from('give_donationmeta')
281 ->select('donation_id')
282 ->where('meta_key', DonationMetaKeys::CAMPAIGN_ID)
283 ->where('meta_value', $campaignId);
284 });
285 });
286 }
287
288 $query->where('payment_mode', $testMode ? SubscriptionMode::TEST : SubscriptionMode::LIVE);
289
290 return $query;
291 }
292 }
293