PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / trunk
Booking for Appointments and Events Calendar – Amelia vtrunk
2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Infrastructure / Repository / User / CustomerRepository.php
ameliabooking / src / Infrastructure / Repository / User Last commit date
CustomerRepository.php 2 weeks ago ProviderRepository.php 4 weeks ago UserRepository.php 2 months ago WPUserRepository.php 3 months ago
CustomerRepository.php
420 lines
1 <?php
2
3 namespace AmeliaBooking\Infrastructure\Repository\User;
4
5 use AmeliaBooking\Domain\Collection\Collection;
6 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
7 use AmeliaBooking\Domain\Entity\User\AbstractUser;
8 use AmeliaBooking\Domain\Repository\User\CustomerRepositoryInterface;
9 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
10 use AmeliaBooking\Domain\ValueObjects\String\BookingStatus;
11 use AmeliaBooking\Domain\ValueObjects\String\Status;
12 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
13 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\PackagesCustomersTable;
14 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\AppointmentsTable;
15 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\CustomerBookingsTable;
16 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\CustomerBookingsToEventsPeriodsTable;
17 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\EventsPeriodsTable;
18 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\User\WPUsersTable;
19
20 /**
21 * Class UserRepository
22 *
23 * @package AmeliaBooking\Infrastructure\Repository
24 */
25 class CustomerRepository extends UserRepository implements CustomerRepositoryInterface
26 {
27 /**
28 * @param $criteria
29 * @param int $itemsPerPage
30 *
31 * @return array
32 * @throws QueryExecutionException
33 * @throws \Exception
34 */
35 public function getFiltered($criteria, $itemsPerPage = null)
36 {
37 try {
38 $wpUserTable = WPUsersTable::getTableName();
39 $bookingsTable = CustomerBookingsTable::getTableName();
40 $appointmentsTable = AppointmentsTable::getTableName();
41 $eventsPeriodsTable = EventsPeriodsTable::getTableName();
42 $bookingsEventsPeriodsTable = CustomerBookingsToEventsPeriodsTable::getTableName();
43 $packagesCustomersTable = PackagesCustomersTable::getTableName();
44
45 $params = [
46 ':type_customer' => AbstractUser::USER_ROLE_CUSTOMER,
47 ':type_admin' => AbstractUser::USER_ROLE_ADMIN,
48 ];
49
50 $joinWithBookings = empty($criteria['ignoredBookings']);
51
52 $where = [
53 'u.type IN (:type_customer, :type_admin)',
54 ];
55
56 $order = '';
57 if (!empty($criteria['sort'])) {
58 $column = $criteria['sort'][0] === '-' ? substr($criteria['sort'], 1) : $criteria['sort'];
59
60 $orderColumns = [
61 'customer' => 'CONCAT(u.firstName, \' \', u.lastName)',
62 'total-bookings' => 'totalBookings',
63 ];
64 $orderColumn = $orderColumns[$column] ?? 'lastBooking';
65
66 $orderDirection = $criteria['sort'][0] === '-' ? 'DESC' : 'ASC';
67 $order = "ORDER BY {$orderColumn} {$orderDirection}";
68
69 $joinWithBookings = $column !== 'customer' || $joinWithBookings;
70 }
71
72 if (!empty($criteria['search'])) {
73 $terms = preg_split('/\s+/', trim($criteria['search']));
74 $termIndex = 0;
75
76 foreach ($terms as $term) {
77 $param = ":search{$termIndex}";
78 $params[$param] = "%{$term}%";
79
80 $where[] = "(
81 u.firstName LIKE {$param}
82 OR u.lastName LIKE {$param}
83 OR u.email LIKE {$param}
84 OR u.phone LIKE {$param}
85 OR u.note LIKE {$param}
86 OR wpu.display_name LIKE {$param}
87 OR u.id LIKE {$param}
88 )";
89
90 $termIndex++;
91 }
92 }
93
94 if (!empty($criteria['customers'])) {
95 $customersCriteria = [];
96
97 foreach ((array)$criteria['customers'] as $key => $customerId) {
98 $params[":customerId$key"] = $customerId;
99 $customersCriteria[] = ":customerId$key";
100 }
101
102 $where[] = 'u.id IN (' . implode(', ', $customersCriteria) . ')';
103 }
104
105 $statsFields = '
106 NULL as lastBooking,
107 NULL as lastAppointment,
108 NULL as lastEvent,
109 0 as totalBookings,
110 0 as countPendingAppointments,
111 0 as countAppointmentBookings,
112 0 as countEventBookings,
113 ';
114
115 $statsJoins = '';
116
117 $having = '';
118
119 if ($joinWithBookings) {
120 $params[':bookingPendingStatus'] = BookingStatus::PENDING;
121
122 $statsFields = "
123 COALESCE(GREATEST(MAX(app.bookingStart), MAX(ep.periodStart)),
124 MAX(app.bookingStart), MAX(ep.periodStart), MAX(pc.purchased)) as lastBooking,
125 MAX(app.bookingStart) as lastAppointment,
126 MAX(ep.periodStart) as lastEvent,
127 MAX(pc.purchased) as lastPackage,
128 COUNT(DISTINCT cb.id) as totalBookings,
129 SUM(case when cb.status = :bookingPendingStatus then 1 else 0 end) as countPendingAppointments,
130 COUNT(DISTINCT CASE WHEN cb.appointmentId IS NOT NULL THEN cb.id ELSE NULL END) as countAppointmentBookings,
131 COUNT(DISTINCT CASE WHEN cb.appointmentId IS NULL THEN cb.id ELSE NULL END) as countEventBookings,
132 COUNT(pc.customerId) as countPackagePurchases,
133 ";
134
135 $statsJoins = "
136 LEFT JOIN {$bookingsTable} cb ON u.id = cb.customerId
137 LEFT JOIN {$appointmentsTable} app ON app.id = cb.appointmentId
138 LEFT JOIN {$bookingsEventsPeriodsTable} bep ON bep.customerBookingId = cb.id
139 LEFT JOIN {$eventsPeriodsTable} ep ON ep.id = bep.eventPeriodId
140 LEFT JOIN {$packagesCustomersTable} pc ON pc.customerId = u.id
141 ";
142
143 if (!empty($criteria['noShow'])) {
144 $having = "HAVING (";
145 foreach ($criteria['noShow'] as $index => $noShowId) {
146 $param = ':noShow' . $index;
147 $params[$param] = $noShowId;
148 $having .= ($index === 0 ? "" : " OR ") . "(COUNT(DISTINCT CASE WHEN cb.status = 'no-show' THEN cb.id ELSE NULL END) " .
149 ($noShowId === "3" ? '>=' : '=') . " " . $param . ")";
150 }
151 $having .= ")";
152 }
153 }
154
155 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
156
157 $limit = $this->getLimit(
158 !empty($criteria['page']) ? (int)$criteria['page'] : 0,
159 (int)$itemsPerPage
160 );
161
162 $statement = $this->connection->prepare(
163 "SELECT
164 u.id as id,
165 u.status as status,
166 u.firstName as firstName,
167 u.lastName as lastName,
168 u.email as email,
169 u.phone as phone,
170 u.countryPhoneIso AS countryPhoneIso,
171 u.gender as gender,
172 u.externalId as externalId,
173 u.translations as translations,
174 IF(u.birthday IS NOT NULL, u.birthday , '') as birthday,
175 u.note as note,
176 u.customFields as customFields,
177 {$statsFields}
178 IF(wpu.display_name IS NOT NULL, wpu.display_name , '') as wpName
179 FROM {$this->table} as u
180 LEFT JOIN {$wpUserTable} wpu ON u.externalId = wpu.id
181 {$statsJoins}
182 {$where}
183 GROUP BY u.id
184 {$having}
185 {$order}
186 {$limit}"
187 );
188
189 $statement->execute($params);
190
191 $rows = $statement->fetchAll();
192 } catch (\Exception $e) {
193 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
194 }
195
196 $items = [];
197 foreach ($rows as $row) {
198 $row['id'] = (int)$row['id'];
199 $row['externalId'] = $row['externalId'] === null ? $row['externalId'] : (int)$row['externalId'];
200 $row['lastBooking'] = !empty($row['lastBooking']) ? DateTimeService::getCustomDateTimeFromUtc($row['lastBooking']) : $row['lastBooking'];
201 $row['lastAppointment'] = !empty($row['lastAppointment']) ?
202 DateTimeService::getCustomDateTimeFromUtc($row['lastAppointment']) :
203 $row['lastAppointment'];
204 $row['lastEvent'] = !empty($row['lastEvent']) ? DateTimeService::getCustomDateTimeFromUtc($row['lastEvent']) : $row['lastEvent'];
205 $row['lastPackage'] = !empty($row['lastPackage']) ? DateTimeService::getCustomDateTimeFromUtc($row['lastPackage']) : null;
206
207 $row['totalBookings'] = (int)$row['totalBookings'];
208 $row['totalAppointments'] = (int)$row['countAppointmentBookings'];
209 $row['totalEvents'] = (int)$row['countEventBookings'];
210 $row['totalPackages'] = !empty($row['countPackagePurchases']) ? (int)$row['countPackagePurchases'] : 0;
211
212 // Fix for customFields being encoded multiple times
213 if ($row['customFields'] && !is_array(json_decode($row['customFields'], true))) {
214 $row['customFields'] = null;
215 }
216
217 $items[$row['id']] = $row;
218 }
219
220 return $items;
221 }
222
223 /**
224 * @param $criteria
225 *
226 * @return mixed
227 * @throws QueryExecutionException
228 */
229 public function getCount($criteria)
230 {
231 $wpUserTable = WPUsersTable::getTableName();
232
233 $params = [
234 ':type_customer' => AbstractUser::USER_ROLE_CUSTOMER,
235 ':type_admin' => AbstractUser::USER_ROLE_ADMIN,
236 ];
237
238 $where = [
239 'u.type IN (:type_customer, :type_admin)',
240 ];
241
242 if (!empty($criteria['search'])) {
243 $terms = preg_split('/\s+/', trim($criteria['search']));
244 $termIndex = 0;
245
246 foreach ($terms as $term) {
247 $param = ":search{$termIndex}";
248 $params[$param] = "%{$term}%";
249
250 $where[] = "(
251 u.firstName LIKE {$param}
252 OR u.lastName LIKE {$param}
253 OR u.email LIKE {$param}
254 OR u.phone LIKE {$param}
255 OR u.note LIKE {$param}
256 OR wpu.display_name LIKE {$param}
257 OR u.id LIKE {$param}
258 )";
259
260 $termIndex++;
261 }
262 }
263
264 if (!empty($criteria['customers'])) {
265 $customersCriteria = [];
266
267 foreach ((array)$criteria['customers'] as $key => $customerId) {
268 $params[":customerId$key"] = $customerId;
269 $customersCriteria[] = ":customerId$key";
270 }
271
272 $where[] = 'u.id IN (' . implode(', ', $customersCriteria) . ')';
273 }
274
275 if (!empty($criteria['noShow'])) {
276 $bookingsTable = CustomerBookingsTable::getTableName();
277
278 $noShowWhere = "exists (SELECT COUNT(*) as c FROM {$bookingsTable} cb WHERE cb.status='no-show' AND cb.customerId=u.id HAVING ";
279
280 foreach ($criteria['noShow'] as $index => $noShowId) {
281 $param = ':noShow' . $index;
282 $params[$param] = $noShowId;
283 $noShowWhere .= ($index === 0 ? "" : " OR ") . "c " . ($noShowId === "3" ? '>=' : '=') . $param;
284 }
285 $noShowWhere .= ")";
286
287 $where[] = $noShowWhere;
288 }
289
290 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
291
292 try {
293 $statement = $this->connection->prepare(
294 "SELECT COUNT(*) as count
295 FROM {$this->table} as u
296 LEFT JOIN {$wpUserTable} wpu ON u.externalId = wpu.id
297 $where
298 "
299 );
300
301 $statement->execute($params);
302
303 $rows = $statement->fetch()['count'];
304 } catch (\Exception $e) {
305 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
306 }
307
308 return $rows;
309 }
310
311 /**
312 * @param string $phone
313 *
314 * @return array
315 * @throws QueryExecutionException
316 * @throws \Exception
317 */
318 public function getByPhoneNumber($phone)
319 {
320 try {
321 $params[':phone'] = '+' . $phone;
322
323 $statement = $this->connection->prepare(
324 "SELECT
325 u.id as id,
326 u.status as status,
327 u.firstName as firstName,
328 u.lastName as lastName,
329 u.email as email,
330 u.phone as phone,
331 u.countryPhoneIso AS countryPhoneIso,
332 u.gender as gender,
333 u.externalId as externalId,
334 IF(u.birthday IS NOT NULL, u.birthday , '') as birthday,
335 u.note as note
336 FROM {$this->table} as u
337 WHERE u.type = 'customer' AND phone = :phone"
338 );
339
340 $statement->execute($params);
341
342 $rows = $statement->fetchAll();
343 } catch (\Exception $e) {
344 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
345 }
346
347 return $rows;
348 }
349
350 /**
351 * @param array $criteria
352 *
353 * @return Collection
354 * @throws QueryExecutionException
355 * @throws InvalidArgumentException
356 * @throws InvalidArgumentException
357 */
358 public function getByCriteria($criteria = [])
359 {
360 $params = [];
361
362 $where = [];
363
364 $fields = '
365 u.id AS id,
366 u.type AS type,
367 u.firstName AS firstName,
368 u.lastName AS lastName,
369 u.email AS email,
370 u.note AS note,
371 u.phone AS phone,
372 u.countryPhoneIso AS countryPhoneIso,
373 u.gender AS gender,
374 u.birthday AS birthday,
375 u.status AS status
376 ';
377
378 if (!empty($criteria['ids'])) {
379 $queryIds = [];
380
381 foreach ($criteria['ids'] as $index => $value) {
382 $param = ':id' . $index;
383
384 $queryIds[] = $param;
385
386 $params[$param] = $value;
387 }
388
389 $where[] = 'u.id IN (' . implode(', ', $queryIds) . ')';
390 }
391
392 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
393
394 try {
395 $statement = $this->connection->prepare(
396 "SELECT
397 {$fields}
398 FROM {$this->table} u
399 {$where}"
400 );
401
402 $statement->execute($params);
403
404 $rows = $statement->fetchAll();
405 } catch (\Exception $e) {
406 throw new QueryExecutionException('Unable to find event by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
407 }
408
409 $items = new Collection();
410
411 foreach ($rows as $row) {
412 $row['type'] = 'customer';
413
414 $items->addItem(call_user_func([static::FACTORY, 'create'], $row), $row['id']);
415 }
416
417 return $items;
418 }
419 }
420