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 / Bookable / Service / PackageCustomerRepository.php
ameliabooking / src / Infrastructure / Repository / Bookable / Service Last commit date
CategoryRepository.php 3 months ago ExtraRepository.php 3 months ago PackageCustomerRepository.php 1 month ago PackageCustomerServiceRepository.php 2 weeks ago PackageRepository.php 2 months ago PackageServiceLocationRepository.php 3 months ago PackageServiceProviderRepository.php 3 months ago PackageServiceRepository.php 3 months ago ProviderServiceRepository.php 3 months ago ResourceEntitiesRepository.php 3 months ago ResourceRepository.php 3 months ago ServiceRepository.php 2 weeks ago
PackageCustomerRepository.php
643 lines
1 <?php
2
3 namespace AmeliaBooking\Infrastructure\Repository\Bookable\Service;
4
5 use AmeliaBooking\Domain\Collection\Collection;
6 use AmeliaBooking\Domain\Entity\Bookable\Service\Package;
7 use AmeliaBooking\Domain\Entity\Bookable\Service\PackageCustomer;
8 use AmeliaBooking\Domain\Factory\Bookable\Service\PackageCustomerFactory;
9 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
10 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
11 use AmeliaBooking\Infrastructure\Repository\AbstractRepository;
12 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\PackagesCustomersServicesTable;
13 use AmeliaBooking\Infrastructure\Connection;
14 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
15 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\PackagesServicesTable;
16 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\PackagesTable;
17 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\ServicesTable;
18 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\AppointmentsTable;
19 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\CustomerBookingsTable;
20 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Coupon\CouponsTable;
21 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Payment\PaymentsTable;
22 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\User\UsersTable;
23
24 class PackageCustomerRepository extends AbstractRepository
25 {
26 public const FACTORY = PackageCustomerFactory::class;
27
28 /** @var string */
29 protected $packagesCustomersServicesTable;
30
31 /**
32 * @param Connection $connection
33 * @param string $table
34 *
35 * @throws InvalidArgumentException
36 */
37 public function __construct(
38 Connection $connection,
39 $table
40 ) {
41 parent::__construct($connection, $table);
42
43 $this->packagesCustomersServicesTable = PackagesCustomersServicesTable::getTableName();
44 }
45
46 /**
47 * @param PackageCustomer $entity
48 *
49 * @return int
50 * @throws QueryExecutionException
51 */
52 public function add($entity)
53 {
54 $data = $entity->toArray();
55
56 $params = [
57 ':packageId' => $data['packageId'],
58 ':customerId' => $data['customerId'],
59 ':price' => $data['price'],
60 ':tax' => !empty($data['tax']) ? json_encode($data['tax']) : null,
61 ':start' => $data['start'],
62 ':end' => $data['end'],
63 ':purchased' => $data['purchased'],
64 ':bookingsCount' => $data['bookingsCount'],
65 ':couponId' => $data['couponId'],
66 ':token' => $data['token'] ?: null,
67 ];
68
69 try {
70 $statement = $this->connection->prepare(
71 "INSERT INTO {$this->table}
72 (`packageId`, `customerId`, `price`, `tax`, `start`, `end`, `purchased`, `status`, `bookingsCount`, `couponId`, `token`)
73 VALUES
74 (:packageId, :customerId, :price, :tax, :start, :end, :purchased, 'approved', :bookingsCount, :couponId, :token)"
75 );
76
77 $statement->execute($params);
78 } catch (\Exception $e) {
79 throw new QueryExecutionException('Unable to add data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
80 }
81
82 return $this->connection->lastInsertId();
83 }
84
85
86 /**
87 * @param int $id
88 * @param PackageCustomer $entity
89 *
90 * @return boolean
91 * @throws QueryExecutionException
92 */
93 public function update($id, $entity)
94 {
95 $data = $entity->toArray();
96
97 $params = [
98 ':status' => $data['status'],
99 ':end' => $data['end'],
100 ':id' => $id,
101 ];
102
103
104 try {
105 $statement = $this->connection->prepare(
106 "UPDATE {$this->table}
107 SET
108 `status` = :status,
109 `end` = :end
110 WHERE
111 id = :id"
112 );
113
114 $statement->execute($params);
115
116 return true;
117 } catch (\Exception $e) {
118 throw new QueryExecutionException('Unable to save data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
119 }
120 }
121
122 /**
123 * Returns token for given id
124 *
125 * @param $id
126 *
127 * @return array
128 * @throws QueryExecutionException
129 */
130 public function getToken($id)
131 {
132 try {
133 $statement = $this->connection->prepare(
134 "SELECT pc.token
135 FROM {$this->table} pc
136 WHERE pc.id = :id"
137 );
138
139 $statement->execute([':id' => $id]);
140
141 $row = $statement->fetch();
142 } catch (\Exception $e) {
143 throw new QueryExecutionException('Unable to return package customer from' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
144 }
145
146 return $row;
147 }
148
149 /**
150 * @param Package $package
151 * @param int $customerId
152 * @param array $limitPerCustomer
153 * @param boolean $packageSpecific
154 * @return int
155 * @throws QueryExecutionException
156 */
157 public function getUserPackageCount($package, $customerId, $limitPerCustomer, $packageSpecific)
158 {
159 $params = [
160 ':customerId' => $customerId
161 ];
162
163 $startDate = DateTimeService::getNowDateTimeObject()->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i');
164
165 $intervalString = "interval " . $limitPerCustomer['period'] . " " . $limitPerCustomer['timeFrame'];
166
167 $where = "(STR_TO_DATE('" . $startDate . "', '%Y-%m-%d %H:%i:%s') BETWEEN " .
168 "(pc.purchased - " . $intervalString . " + interval 1 second) AND " .
169 "(pc.purchased + " . $intervalString . " - interval 1 second))"; //+ interval 2 day
170
171 if ($packageSpecific) {
172 $where .= " AND pc.packageId = :packageId";
173 $params[':packageId'] = $package->getId()->getValue();
174 }
175
176 try {
177 $statement = $this->connection->prepare(
178 "SELECT COUNT(DISTINCT pc.id) AS count
179 FROM {$this->table} pc
180 WHERE pc.customerId = :customerId AND {$where} AND pc.status = 'approved'
181 "
182 );
183
184 $statement->execute($params);
185
186 $rows = $statement->fetch()['count'];
187 } catch (\Exception $e) {
188 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
189 }
190
191 return $rows;
192 }
193
194 /**
195 * @param array $criteria
196 *
197 * @return array
198 * @throws QueryExecutionException
199 * @throws InvalidArgumentException
200 */
201 public function getFilteredIds($criteria = [], $itemsPerPage = null)
202 {
203 $bookingsTable = CustomerBookingsTable::getTableName();
204 $appointmentsTable = AppointmentsTable::getTableName();
205 $usersTable = UsersTable::getTableName();
206 $packagesTable = PackagesTable::getTableName();
207
208 $params = [];
209 $where = [];
210 $joins = '';
211 $having = '';
212
213 if (!empty($criteria['dates'])) {
214 $where[] = "(pc.purchased BETWEEN :purchasedFrom AND :purchasedTo)";
215
216 $params[':purchasedFrom'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][0]);
217
218 $params[':purchasedTo'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][1]);
219 }
220
221 if (!empty($criteria['packages'])) {
222 $queryServices = [];
223
224 foreach ($criteria['packages'] as $index => $value) {
225 $param = ':package' . $index;
226
227 $queryServices[] = $param;
228
229 $params[$param] = $value;
230 }
231
232 $where[] = 'pc.packageId IN (' . implode(', ', $queryServices) . ')';
233 }
234
235
236 if (!empty($criteria['customers'])) {
237 $queryCustomers = [];
238
239 foreach ($criteria['customers'] as $index => $value) {
240 $param = ':customer' . $index;
241
242 $queryCustomers[] = $param;
243
244 $params[$param] = $value;
245 }
246
247 $where[] = 'pc.customerId IN (' . implode(', ', $queryCustomers) . ')';
248 }
249
250
251 if (!empty($criteria['search'])) {
252 $terms = preg_split('/\s+/', trim($criteria['search']));
253 $termIndex = 0;
254
255 foreach ($terms as $term) {
256 $param = ":search{$termIndex}";
257 $params[$param] = "%{$term}%";
258
259 $where[] = "(
260 p.name LIKE {$param}
261 OR u.firstName LIKE {$param}
262 OR u.lastName LIKE {$param}
263 OR pc.id LIKE {$param}
264 )";
265
266 $termIndex++;
267 }
268
269 $joins .= "
270 INNER JOIN {$usersTable} u ON u.id = pc.customerId
271 INNER JOIN {$packagesTable} p ON p.id = pc.packageId
272 ";
273 }
274
275
276 if (!empty($criteria['status'])) {
277 $whereOr = [];
278 foreach ($criteria['status'] as $status) {
279 switch ($status) {
280 case 'expired':
281 $whereOr[] = "(pc.end IS NOT NULL AND pc.end < NOW())";
282 break;
283 case 'approved':
284 case 'active':
285 $whereOr[] = "((pc.end > NOW() OR pc.end IS NULL) AND pc.status = 'approved')";
286 break;
287 case 'canceled':
288 $whereOr[] = "(pc.status = 'canceled')";
289 break;
290 default:
291 break;
292 }
293 }
294 $where[] = '(' . implode(' OR ', $whereOr) . ')';
295 }
296
297 if (!empty($criteria['providers']) || !empty($criteria['services']) || !empty($criteria['availability']) || !empty($criteria['locations'])) {
298 $whereProviders = '';
299 if (!empty($criteria['providers'])) {
300 $queryProviders = [];
301
302 foreach ($criteria['providers'] as $index => $value) {
303 $param = ':provider' . $index;
304
305 $queryProviders[] = $param;
306
307 $params[$param] = $value;
308 }
309
310 $whereProviders = 'a.providerId IN (' . implode(', ', $queryProviders) . ')';
311 }
312
313 $whereServices = '';
314 $queryServices = [];
315 if (!empty($criteria['services'])) {
316 foreach ($criteria['services'] as $index => $value) {
317 $param = ':service' . $index;
318
319 $queryServices[] = $param;
320
321 $params[$param] = $value;
322 }
323
324 $whereServices = 'a.serviceId IN (' . implode(', ', $queryServices) . ')';
325 }
326
327 $whereLocations = '';
328 if (!empty($criteria['locations'])) {
329 $queryLocations = [];
330
331 foreach ($criteria['locations'] as $index => $value) {
332 $param = ':location' . $index;
333
334 $queryLocations[] = $param;
335
336 $params[$param] = $value;
337 }
338
339 $whereLocations = 'a.locationId IN (' . implode(', ', $queryLocations) . ')';
340 }
341
342 if (!empty($criteria['availability']) && count($criteria['availability']) === 1) {
343 if ($criteria['availability'][0] === 'full') {
344 $having = "HAVING COUNT(a.id)>0 AND
345 (
346 COUNT(a.id) = (
347 SELECT SUM(pcs2.bookingsCount) FROM {$this->packagesCustomersServicesTable} pcs2 WHERE pcs2.packageCustomerId = pc.id
348 )
349 OR COUNT(a.id) = pc.bookingsCount
350 )";
351 } elseif ($criteria['availability'][0] === 'available') {
352 $having = "HAVING
353 (pc.bookingsCount = 0 AND COUNT(a.id) < (
354 SELECT SUM(pcs2.bookingsCount) FROM {$this->packagesCustomersServicesTable} pcs2 WHERE pcs2.packageCustomerId=pc.id
355 )
356 )
357 OR (pc.bookingsCount > 0 AND COUNT(a.id) < pc.bookingsCount)";
358 }
359
360 if (!empty($whereServices)) {
361 $where[] = "EXISTS (
362 SELECT a2.id
363 FROM {$appointmentsTable} a2
364 INNER JOIN {$bookingsTable} cb2 ON a2.id = cb2.appointmentId
365 INNER join {$this->packagesCustomersServicesTable} pcs2 on pcs2.id = cb2.packageCustomerServiceId and pcs2.packageCustomerId = pc.id
366
367 WHERE a2.serviceId IN (" . implode(', ', $queryServices) . "))";
368 }
369 if (!empty($whereProviders)) {
370 $where[] = "EXISTS (
371 SELECT a2.id
372 FROM {$appointmentsTable} a2
373 INNER JOIN {$bookingsTable} cb2 ON a2.id = cb2.appointmentId
374 INNER join {$this->packagesCustomersServicesTable} pcs2 on pcs2.id = cb2.packageCustomerServiceId and pcs2.packageCustomerId = pc.id
375
376 WHERE a2.providerId IN (" . implode(', ', $queryProviders) . "))";
377 }
378 if (!empty($whereLocations)) {
379 $where[] = "EXISTS (
380 SELECT a2.id
381 FROM {$appointmentsTable} a2
382 INNER JOIN {$bookingsTable} cb2 ON a2.id = cb2.appointmentId
383 INNER join {$this->packagesCustomersServicesTable} pcs2 on pcs2.id = cb2.packageCustomerServiceId and pcs2.packageCustomerId = pc.id
384
385 WHERE a2.locationId IN (" . implode(', ', $queryLocations) . "))";
386 }
387 } else {
388 if (!empty($whereServices)) {
389 $where[] = $whereServices;
390 }
391 if (!empty($whereProviders)) {
392 $where[] = $whereProviders;
393 }
394 if (!empty($whereLocations)) {
395 $where[] = $whereLocations;
396 }
397 }
398
399 $joins .= "
400 INNER JOIN {$this->packagesCustomersServicesTable} pcs ON pc.id = pcs.packageCustomerId
401 LEFT JOIN {$bookingsTable} cb ON pcs.id = cb.packageCustomerServiceId
402 LEFT JOIN {$appointmentsTable} a ON a.id = cb.appointmentId
403 ";
404 }
405
406
407 if (isset($criteria['couponId'])) {
408 $where[] = 'pc.couponId = :couponId';
409 $params[':couponId'] = (int)$criteria['couponId'];
410 }
411
412 $limit = $this->getLimit(
413 !empty($criteria['page']) ? (int)$criteria['page'] : 0,
414 !empty($itemsPerPage) ? (int)$itemsPerPage : 0
415 );
416
417 $orderBy = 'ORDER BY pc.purchased';
418 if (!empty($criteria['sort'])) {
419 $column = $criteria['sort'][0] === '-' ? substr($criteria['sort'], 1) : $criteria['sort'];
420 $orderColumn = '';
421 if ($column === 'customer') {
422 $joins .= "
423 INNER JOIN {$usersTable} cu ON cu.id = pc.customerId
424 ";
425 $orderColumn = 'CONCAT(cu.firstName, \' \', cu.lastName)';
426 } elseif ($column === 'date') {
427 $orderColumn = 'pc.purchased';
428 } elseif ($column === 'id') {
429 $orderColumn = 'pc.id';
430 } elseif ($column === 'package') {
431 $joins .= "
432 INNER JOIN {$packagesTable} pa ON pa.id = pc.packageId
433 ";
434 $orderColumn = 'pa.name';
435 }
436 $orderDir = $orderColumn ? ($criteria['sort'][0] === '-' ? 'DESC' : 'ASC') : '';
437 $orderBy = $orderColumn ? "ORDER BY {$orderColumn} {$orderDir}" : 'ORDER BY pc.purchased';
438 }
439
440 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
441
442 try {
443 $statement = $this->connection->prepare(
444 "SELECT
445 pc.id AS id,
446 pc.bookingsCount AS bookingsCount
447 FROM {$this->table} pc
448 {$joins}
449 {$where}
450 GROUP BY pc.id
451 {$having}
452 {$orderBy}
453 {$limit}"
454 );
455
456 $statement->execute($params);
457
458 $rows = $statement->fetchAll();
459 } catch (\Exception $e) {
460 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
461 }
462
463 return array_column($rows, 'id');
464 }
465
466 /**
467 * @param array $ids
468 * @param array $options
469 * @param string $sort
470 *
471 * @return Collection
472 * @throws InvalidArgumentException
473 * @throws QueryExecutionException
474 */
475 public function getFiltered($ids, $options = [], $sort = null)
476 {
477 $bookingsTable = CustomerBookingsTable::getTableName();
478
479 $appointmentsTable = AppointmentsTable::getTableName();
480
481 $usersTable = UsersTable::getTableName();
482 $packagesTable = PackagesTable::getTableName();
483 $packageServicesTable = PackagesServicesTable::getTableName();
484 $servicesTable = ServicesTable::getTableName();
485 $paymentsTable = PaymentsTable::getTableName();
486 $couponsTable = CouponsTable::getTableName();
487
488 $params = [];
489
490 $where = [];
491
492 $fields = "";
493
494 $joins = "
495 INNER JOIN {$this->packagesCustomersServicesTable} pcs ON pc.id = pcs.packageCustomerId
496 LEFT JOIN {$bookingsTable} cb ON pcs.id = cb.packageCustomerServiceId
497 LEFT JOIN {$appointmentsTable} a ON a.id = cb.appointmentId
498 LEFT JOIN {$usersTable} cu ON cu.id = pc.customerId
499 LEFT JOIN {$packagesTable} pa ON pa.id = pc.packageId
500 LEFT JOIN {$paymentsTable} p ON p.packageCustomerId = pc.id
501 LEFT JOIN {$couponsTable} c ON c.id = pc.couponId
502 ";
503
504 if (!empty($options['fetchPackageServices'])) {
505 $joins .= "
506 LEFT JOIN {$packageServicesTable} pas ON pas.packageId = pa.id
507 LEFT JOIN {$servicesTable} s ON s.id = pas.serviceId
508 ";
509
510 $fields .= "
511 pas.id AS package_service_id,
512 pas.serviceId AS package_service_serviceId,
513
514 s.id AS service_id,
515 s.name AS service_name,
516 ";
517 }
518
519 if (!empty($options['fetchAppointmentProviders'])) {
520 $joins .= "
521 LEFT JOIN {$usersTable} pu ON pu.id = a.providerId
522 ";
523
524 $fields .= "
525 pu.id AS provider_id,
526 pu.firstName AS provider_firstName,
527 pu.lastName AS provider_lastName,
528 pu.email AS provider_email,
529 pu.badgeId AS provider_badgeId,
530 pu.pictureFullPath AS provider_pictureFullPath,
531 pu.pictureThumbPath AS provider_pictureThumbPath,
532 ";
533 }
534
535 if (!empty($ids)) {
536 $queryIds = [];
537
538 foreach ($ids as $index => $value) {
539 $param = ':id' . $index;
540
541 $queryIds[] = $param;
542
543 $params[$param] = $value;
544 }
545
546 $where[] = 'pc.id IN (' . implode(', ', $queryIds) . ')';
547 }
548
549 $orderBy = 'ORDER BY pc.purchased';
550 if ($sort) {
551 $column = $sort[0] === '-' ? substr($sort, 1) : $sort;
552 $orderColumn = '';
553 if ($column === 'customer') {
554 $orderColumn = 'CONCAT(cu.firstName, \' \', cu.lastName)';
555 } elseif ($column === 'date') {
556 $orderColumn = 'pc.purchased';
557 } elseif ($column === 'id') {
558 $orderColumn = 'pc.id';
559 } elseif ($column === 'package') {
560 $orderColumn = 'pa.name';
561 }
562 $orderDir = $orderColumn ? ($sort[0] === '-' ? 'DESC' : 'ASC') : '';
563 $orderBy = $orderColumn ? "ORDER BY {$orderColumn} {$orderDir}" : 'ORDER BY pc.purchased';
564 }
565
566 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
567
568 $fields .= "
569 pc.id AS package_customer_id,
570 pc.packageId AS package_customer_packageId,
571 pc.purchased AS package_customer_purchased,
572 pc.end AS package_customer_end,
573 pc.status AS package_customer_status,
574 pc.customerId AS package_customer_customerId,
575 pc.bookingsCount AS package_customer_bookingsCount,
576 pc.price AS package_customer_price,
577 pc.tax AS package_customer_tax,
578 pc.couponId AS package_customer_couponId,
579 pc.token AS package_customer_token,
580
581 pcs.id AS package_customer_service_id,
582 pcs.bookingsCount AS package_customer_service_bookingsCount,
583
584 cb.id AS booking_id,
585
586 a.id AS appointment_id,
587 a.providerId AS appointment_providerId,
588 a.serviceId AS appointment_serviceId,
589 a.notifyParticipants AS appointment_notifyParticipants,
590 a.bookingStart AS appointment_bookingStart,
591 a.bookingEnd AS appointment_bookingEnd,
592 a.status AS appointment_status,
593
594 cu.id AS customer_id,
595 cu.firstname AS customer_firstName,
596 cu.lastname AS customer_lastName,
597 cu.email AS customer_email,
598 cu.note AS customer_note,
599
600 pa.id AS package_id,
601 pa.name AS package_name,
602 pa.pictureThumbPath AS package_pictureThumbPath,
603 pa.pictureFullPath AS package_pictureFullPath,
604 pa.color AS package_color,
605 pa.calculatedPrice AS package_calculatedPrice,
606 pa.discount AS package_discount,
607
608 p.id AS payment_id,
609 p.status AS payment_status,
610 p.amount AS payment_amount,
611 p.dateTime AS payment_dateTime,
612 p.gateway AS payment_gateway,
613 p.wcOrderId AS payment_wcOrderId,
614 p.wcOrderItemId AS payment_wcOrderItemId,
615 p.created AS payment_created,
616
617 c.id AS coupon_id,
618 c.discount AS coupon_discount,
619 c.deduction AS coupon_deduction,
620 c.status AS coupon_status
621 ";
622
623 try {
624 $statement = $this->connection->prepare(
625 "SELECT {$fields}
626 FROM {$this->table} pc
627 {$joins}
628 {$where}
629 {$orderBy}
630 "
631 );
632
633 $statement->execute($params);
634
635 $rows = $statement->fetchAll();
636 } catch (\Exception $e) {
637 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
638 }
639
640 return call_user_func([static::FACTORY, 'createCollection'], $rows);
641 }
642 }
643