PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.1.3
Booking for Appointments and Events Calendar – Amelia v2.1.3
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 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 / ServiceRepository.php
ameliabooking / src / Infrastructure / Repository / Bookable / Service Last commit date
CategoryRepository.php 5 months ago ExtraRepository.php 5 months ago PackageCustomerRepository.php 5 months ago PackageCustomerServiceRepository.php 5 months ago PackageRepository.php 5 months ago PackageServiceLocationRepository.php 5 months ago PackageServiceProviderRepository.php 5 months ago PackageServiceRepository.php 5 months ago ProviderServiceRepository.php 5 months ago ResourceEntitiesRepository.php 5 months ago ResourceRepository.php 5 months ago ServiceRepository.php 5 months ago
ServiceRepository.php
1068 lines
1 <?php
2
3 /**
4 * @copyright © Melograno Ventures. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace AmeliaBooking\Infrastructure\Repository\Bookable\Service;
9
10 use AmeliaBooking\Domain\Collection\Collection;
11 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
12 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
13 use AmeliaBooking\Infrastructure\Connection;
14 use AmeliaBooking\Domain\Entity\Bookable\Service\Service;
15 use AmeliaBooking\Domain\Factory\Bookable\Service\ServiceFactory;
16 use AmeliaBooking\Infrastructure\Licence;
17 use AmeliaBooking\Infrastructure\Repository\AbstractRepository;
18 use AmeliaBooking\Domain\Repository\Bookable\Service\ServiceRepositoryInterface;
19 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
20 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\AppointmentsTable;
21
22 /**
23 * Class ServiceRepository
24 *
25 * @package AmeliaBooking\Infrastructure\Repository\Service
26 */
27 class ServiceRepository extends AbstractRepository implements ServiceRepositoryInterface
28 {
29 public const FACTORY = ServiceFactory::class;
30
31 /** @var string */
32 protected $providerServicesTable;
33
34 /** @var string */
35 protected $extrasTable;
36
37 /** @var string */
38 protected $serviceViewsTable;
39
40 /** @var string */
41 protected $galleriesTable;
42
43 /**
44 * @param Connection $connection
45 * @param string $table
46 * @param string $providerServicesTable
47 * @param string $extrasTable
48 * @param string $serviceViewsTable
49 * @param string $galleriesTable
50 */
51 public function __construct(
52 Connection $connection,
53 $table,
54 $providerServicesTable,
55 $extrasTable,
56 $serviceViewsTable,
57 $galleriesTable
58 ) {
59 parent::__construct($connection, $table);
60 $this->providerServicesTable = $providerServicesTable;
61 $this->extrasTable = $extrasTable;
62 $this->serviceViewsTable = $serviceViewsTable;
63 $this->galleriesTable = $galleriesTable;
64 }
65
66 /**
67 * @return Collection
68 * @throws QueryExecutionException
69 */
70 public function getAllArrayIndexedById($ids = [])
71 {
72 $where = '';
73 $params = [];
74 if (!empty($ids)) {
75 $query = [];
76
77 foreach ((array)$ids as $index => $value) {
78 $param = ':id' . $index;
79
80 $query[] = $param;
81
82 $params[$param] = $value;
83 }
84
85 $where = 'WHERE s.id IN (' . implode(', ', $query) . ')';
86 }
87
88 try {
89 $statement = $this->connection->prepare("SELECT
90 s.id AS service_id,
91 s.name AS service_name,
92 s.description AS service_description,
93 s.color AS service_color,
94 s.price AS service_price,
95 s.customPricing AS service_customPricing,
96 s.limitPerCustomer AS service_limitPerCustomer,
97 s.status AS service_status,
98 s.categoryId AS service_categoryId,
99 s.maxCapacity AS service_maxCapacity,
100 s.maxExtraPeople AS service_maxExtraPeople,
101 s.minCapacity AS service_minCapacity,
102 s.duration AS service_duration,
103 s.timeBefore AS service_timeBefore,
104 s.timeAfter AS service_timeAfter,
105 s.bringingAnyone as service_bringingAnyone,
106 s.pictureFullPath AS service_picture_full,
107 s.pictureThumbPath AS service_picture_thumb,
108 s.position AS service_position,
109 s.show AS service_show,
110 s.aggregatedPrice AS service_aggregatedPrice,
111 s.settings AS service_settings,
112 s.recurringCycle AS service_recurringCycle,
113 s.recurringSub AS service_recurringSub,
114 s.recurringPayment AS service_recurringPayment,
115 s.translations AS service_translations,
116 s.deposit AS service_deposit,
117 s.depositPayment AS service_depositPayment,
118 s.depositPerPerson AS service_depositPerPerson,
119 s.fullPayment AS service_fullPayment,
120 s.mandatoryExtra AS service_mandatoryExtra,
121 s.minSelectedExtras AS service_minSelectedExtras,
122
123 e.id AS extra_id,
124 e.name AS extra_name,
125 e.price AS extra_price,
126 e.maxQuantity AS extra_maxQuantity,
127 e.duration AS extra_duration,
128 e.position AS extra_position,
129 e.aggregatedPrice AS extra_aggregatedPrice,
130 e.description AS extra_description,
131 e.translations AS extra_translations,
132
133 g.id AS gallery_id,
134 g.pictureFullPath AS gallery_picture_full,
135 g.pictureThumbPath AS gallery_picture_thumb,
136 g.position AS gallery_position
137 FROM {$this->table} s
138 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
139 LEFT JOIN {$this->galleriesTable} g ON g.entityId = s.id AND g.entityType = 'service'
140 {$where}
141 ORDER BY s.position, s.name ASC, e.position ASC, g.position ASC");
142
143 $statement->execute($params);
144
145 $rows = $statement->fetchAll();
146 } catch (\Exception $e) {
147 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
148 }
149
150 /** @var Collection $services */
151 $services = call_user_func([static::FACTORY, 'createCollection'], $rows);
152
153 /** @var Service $service */
154 foreach ($services->getItems() as $service) {
155 if ($service->getSettings() && json_decode($service->getSettings()->getValue(), true) === null) {
156 $service->setSettings(null);
157 }
158 }
159
160 return $services;
161 }
162
163 /**
164 * @param Service $entity
165 *
166 * @return int
167 * @throws QueryExecutionException
168 */
169 public function add($entity)
170 {
171 $data = $entity->toArray();
172
173 $params = [
174 ':name' => $data['name'],
175 ':description' => $data['description'],
176 ':color' => $data['color'],
177 ':price' => $data['price'],
178 ':status' => $data['status'],
179 ':categoryId' => $data['categoryId'],
180 ':minCapacity' => $data['minCapacity'],
181 ':maxCapacity' => $data['maxCapacity'],
182 ':maxExtraPeople' => $data['maxExtraPeople'],
183 ':duration' => $data['duration'],
184 ':bringingAnyone' => $data['bringingAnyone'] ? 1 : 0,
185 ':aggregatedPrice' => $data['aggregatedPrice'] ? 1 : 0,
186 ':pictureFullPath' => $data['pictureFullPath'],
187 ':pictureThumbPath' => $data['pictureThumbPath'],
188 ':position' => $data['position'],
189 ':mandatoryExtra' => $data['mandatoryExtra'] ? 1 : 0,
190 ':minSelectedExtras' => $data['minSelectedExtras'],
191 ];
192
193 $additionalData = Licence\DataModifier::getServiceRepositoryData($data);
194
195 $params = array_merge($params, $additionalData['values']);
196
197 try {
198 $statement = $this->connection->prepare(
199 "INSERT INTO
200 {$this->table}
201 (
202 {$additionalData['columns']}
203 `name`,
204 `description`,
205 `color`,
206 `price`,
207 `status`,
208 `categoryId`,
209 `minCapacity`,
210 `maxCapacity`,
211 `maxExtraPeople`,
212 `duration`,
213 `bringingAnyone`,
214 `aggregatedPrice`,
215 `pictureFullPath`,
216 `pictureThumbPath`,
217 `position`,
218 `mandatoryExtra`,
219 `minSelectedExtras`
220 ) VALUES (
221 {$additionalData['placeholders']}
222 :name,
223 :description,
224 :color,
225 :price,
226 :status,
227 :categoryId,
228 :minCapacity,
229 :maxCapacity,
230 :maxExtraPeople,
231 :duration,
232 :bringingAnyone,
233 :aggregatedPrice,
234 :pictureFullPath,
235 :pictureThumbPath,
236 :position,
237 :mandatoryExtra,
238 :minSelectedExtras
239 )"
240 );
241
242 $statement->execute($params);
243
244 return $this->connection->lastInsertId();
245 } catch (\Exception $e) {
246 throw new QueryExecutionException('Unable to add data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
247 }
248 }
249
250 /**
251 * @param int $id
252 * @param Service $entity
253 *
254 * @return mixed
255 * @throws QueryExecutionException
256 */
257 public function update($id, $entity)
258 {
259 $data = $entity->toArray();
260
261 $params = [
262 ':name' => $data['name'],
263 ':description' => $data['description'],
264 ':color' => $data['color'],
265 ':price' => $data['price'],
266 ':status' => $data['status'],
267 ':categoryId' => $data['categoryId'],
268 ':maxExtraPeople' => $data['maxExtraPeople'],
269 ':duration' => $data['duration'],
270 ':bringingAnyone' => $data['bringingAnyone'] ? 1 : 0,
271 ':aggregatedPrice' => $data['aggregatedPrice'] ? 1 : 0,
272 ':pictureFullPath' => $data['pictureFullPath'],
273 ':pictureThumbPath' => $data['pictureThumbPath'],
274 ':position' => $data['position'],
275 ':mandatoryExtra' => $data['mandatoryExtra'] ? 1 : 0,
276 ':minSelectedExtras' => $data['minSelectedExtras'],
277 ':id' => $id
278 ];
279
280 $additionalData = Licence\DataModifier::getServiceRepositoryData($data);
281
282 $params = array_merge($params, $additionalData['values']);
283
284 try {
285 $statement = $this->connection->prepare(
286 "UPDATE {$this->table}
287 SET
288 {$additionalData['columnsPlaceholders']}
289 `name` = :name,
290 `description` = :description,
291 `color` = :color,
292 `price` = :price,
293 `status` = :status,
294 `categoryId` = :categoryId,
295 `maxExtraPeople` = :maxExtraPeople,
296 `duration` = :duration,
297 `bringingAnyone` = :bringingAnyone,
298 `aggregatedPrice` = :aggregatedPrice,
299 `pictureFullPath` = :pictureFullPath,
300 `pictureThumbPath` = :pictureThumbPath,
301 `position` = :position,
302 `mandatoryExtra` = :mandatoryExtra,
303 `minSelectedExtras` = :minSelectedExtras
304 WHERE
305 id = :id"
306 );
307
308 $statement->execute($params);
309
310 return true;
311 } catch (\Exception $e) {
312 throw new QueryExecutionException('Unable to save data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
313 }
314 }
315
316 /**
317 * @param array $criteria
318 * @param int $itemsPerPage
319 *
320 * @return Collection
321 * @throws QueryExecutionException
322 * @throws InvalidArgumentException
323 */
324 public function getFiltered($criteria, $itemsPerPage = null)
325 {
326 $params = [];
327
328 $where = [];
329
330 $orderColumn = 's.position, s.id';
331
332 $orderDirection = 'ASC';
333
334 if (!empty($criteria['sort'])) {
335 switch ($criteria['sort']) {
336 case ('nameAsc'):
337 $orderColumn = 's.name';
338 $orderDirection = 'ASC';
339 break;
340
341 case ('nameDesc'):
342 $orderColumn = 's.name';
343 $orderDirection = 'DESC';
344 break;
345
346 case ('priceAsc'):
347 $orderColumn = 's.price';
348 $orderDirection = 'ASC';
349 break;
350
351 case ('priceDesc'):
352 $orderColumn = 's.price';
353 $orderDirection = 'DESC';
354 break;
355
356 case ('durationAsc'):
357 $orderColumn = 's.duration';
358 $orderDirection = 'ASC';
359 break;
360
361 case ('durationDesc'):
362 $orderColumn = 's.duration';
363 $orderDirection = 'DESC';
364 break;
365
366 case ('idAsc'):
367 $orderColumn = 's.id';
368 $orderDirection = 'ASC';
369 break;
370
371 case ('idDesc'):
372 $orderColumn = 's.id';
373 $orderDirection = 'DESC';
374 break;
375
376 case ('custom'):
377 $orderColumn = 's.position, s.id';
378 $orderDirection = 'ASC';
379 break;
380 }
381 }
382
383 if (!empty($criteria['search'])) {
384 $terms = preg_split('/\s+/', trim($criteria['search']));
385 $termIndex = 0;
386
387 foreach ($terms as $term) {
388 $param = ":search{$termIndex}";
389 $params[$param] = "%{$term}%";
390
391 $where[] = "(
392 s.name LIKE {$param}
393 OR s.description LIKE {$param}
394 OR s.id LIKE {$param}
395 )";
396
397 $termIndex++;
398 }
399 }
400
401 if (!empty($criteria['categoryId'])) {
402 $params[':categoryId'] = $criteria['categoryId'];
403
404 $where[] = 's.categoryId = :categoryId';
405 }
406
407 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
408
409 $order = "ORDER BY {$orderColumn} {$orderDirection}";
410
411 $limit = $this->getLimit(
412 !empty($criteria['page']) ? (int)$criteria['page'] : 0,
413 (int)$itemsPerPage
414 );
415
416 try {
417 $statement = $this->connection->prepare(
418 "SELECT s.*
419 FROM {$this->table} s
420 {$where}
421 {$order}
422 {$limit}"
423 );
424
425 $statement->execute($params);
426
427 $rows = $statement->fetchAll();
428 } catch (\Exception $e) {
429 throw new QueryExecutionException('Unable to find by ids in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
430 }
431
432 $items = new Collection();
433
434 foreach ($rows as $row) {
435 $items->addItem(call_user_func([static::FACTORY, 'create'], $row), $row['id']);
436 }
437
438 return $items;
439 }
440
441 /**
442 * @param array $criteria
443 *
444 * @return mixed
445 * @throws QueryExecutionException
446 */
447 public function getCount($criteria)
448 {
449 $params = [];
450
451 $where = [];
452
453 if (!empty($criteria['search'])) {
454 $terms = preg_split('/\s+/', trim($criteria['search']));
455 $termIndex = 0;
456
457 foreach ($terms as $term) {
458 $param = ":search{$termIndex}";
459 $params[$param] = "%{$term}%";
460
461 $where[] = "(
462 s.name LIKE {$param}
463 OR s.description LIKE {$param}
464 OR s.id LIKE {$param}
465 )";
466
467 $termIndex++;
468 }
469 }
470
471 if (!empty($criteria['categoryId'])) {
472 $params[':categoryId'] = $criteria['categoryId'];
473
474 $where[] = 's.categoryId = :categoryId';
475 }
476
477 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
478
479 try {
480 $statement = $this->connection->prepare(
481 "SELECT COUNT(*) as count
482 FROM {$this->table} s
483 {$where}
484 ORDER BY s.position, s.id"
485 );
486
487 $statement->execute($params);
488
489 $row = $statement->fetch()['count'];
490 } catch (\Exception $e) {
491 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
492 }
493
494 return $row;
495 }
496
497 /**
498 * @param int $serviceId
499 * @param int $userId
500 *
501 * @return Collection
502 * @throws QueryExecutionException
503 */
504 public function getProviderServicesWithExtras($serviceId, $userId)
505 {
506 try {
507 $statement = $this->connection->prepare(
508 "SELECT
509 s.id AS service_id,
510 s.name AS service_name,
511 s.description AS service_description,
512 s.color AS service_color,
513 ps.price AS service_price,
514 s.status AS service_status,
515 s.categoryId AS service_categoryId,
516 ps.minCapacity AS service_minCapacity,
517 ps.maxCapacity AS service_maxCapacity,
518 ps.customPricing AS service_customPricing,
519 s.limitPerCustomer AS service_limitPerCustomer,
520 s.duration AS service_duration,
521 s.timeBefore AS service_timeBefore,
522 s.timeAfter AS service_timeAfter,
523 s.bringingAnyone as service_bringingAnyone,
524 s.pictureFullPath AS service_picture_full,
525 s.pictureThumbPath AS service_picture_thumb,
526 s.aggregatedPrice AS service_aggregatedPrice,
527 s.settings AS service_settings,
528 s.recurringPayment AS service_recurringPayment,
529 s.translations AS service_translations,
530 s.show AS service_show,
531 s.deposit AS service_deposit,
532 s.depositPayment AS service_depositPayment,
533 s.depositPerPerson AS service_depositPerPerson,
534 s.fullPayment AS service_fullPayment,
535
536 e.id AS extra_id,
537 e.name AS extra_name,
538 e.price AS extra_price,
539 e.maxQuantity AS extra_maxQuantity,
540 e.duration AS extra_duration,
541 e.aggregatedPrice AS extra_aggregatedPrice,
542 e.position AS extra_position,
543 e.translations AS extra_translations
544 FROM {$this->table} s
545 INNER JOIN {$this->providerServicesTable} ps ON s.id = ps.serviceId
546 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
547 WHERE ps.userId = :userId AND ps.serviceId = :serviceId"
548 );
549
550 $statement->bindParam(':userId', $userId);
551 $statement->bindParam(':serviceId', $serviceId);
552
553 $statement->execute();
554
555 $rows = $statement->fetchAll();
556 } catch (\Exception $e) {
557 throw new QueryExecutionException('Unable to find by ids in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
558 }
559
560 return call_user_func([static::FACTORY, 'createCollection'], $rows);
561 }
562
563 /**
564 * @param $criteria
565 *
566 * @return Collection
567 * @throws QueryExecutionException
568 */
569 public function getByCriteria($criteria)
570 {
571 $params = [];
572 $where = [];
573
574 $order = 'ORDER BY s.name ASC';
575 if (isset($criteria['sort'])) {
576 if ($criteria['sort'] === '') {
577 $order = 'ORDER BY s.position';
578 } else {
579 $orderColumn = strpos($criteria['sort'], 'name') !== false ? 's.name' : 's.price';
580 $orderDirection = $criteria['sort'][0] === '-' ? 'DESC' : 'ASC';
581 $order = "ORDER BY {$orderColumn} {$orderDirection}";
582 }
583 }
584
585 if (!empty($criteria['search'])) {
586 $terms = preg_split('/\s+/', trim($criteria['search']));
587 $termIndex = 0;
588
589 foreach ($terms as $term) {
590 $param = ":search{$termIndex}";
591 $params[$param] = "%{$term}%";
592
593 $where[] = "(
594 s.name LIKE {$param}
595 )";
596
597 $termIndex++;
598 }
599 }
600
601 if (!empty($criteria['services'])) {
602 $queryServices = [];
603
604 foreach ((array)$criteria['services'] as $index => $value) {
605 $param = ':service' . $index;
606 $queryServices[] = $param;
607 $params[$param] = $value;
608 }
609
610 $where[] = 's.id IN (' . implode(', ', $queryServices) . ')';
611 }
612
613 if (!empty($criteria['categories'])) {
614 $queryCategories = [];
615
616 foreach ((array)$criteria['categories'] as $index => $value) {
617 $param = ':category' . $index;
618 $queryCategories[] = $param;
619 $params[$param] = $value;
620 }
621
622 $where[] = 's.categoryId IN (' . implode(', ', $queryCategories) . ')';
623 }
624
625 if (!empty($criteria['providers'])) {
626 $queryProviders = [];
627
628 foreach ((array)$criteria['providers'] as $index => $value) {
629 $param = ':provider' . $index;
630 $queryProviders[] = $param;
631 $params[$param] = $value;
632 }
633
634 $where[] = 'ps.userId IN (' . implode(', ', $queryProviders) . ')';
635 }
636
637 if (!empty($criteria['status'])) {
638 $params[':status'] = $criteria['status'];
639
640 $where[] = 's.status = :status';
641 }
642
643 $where = $where ? ' AND ' . implode(' AND ', $where) : '';
644
645 try {
646 $statement = $this->connection->prepare(
647 "SELECT
648 s.id AS service_id,
649 s.name AS service_name,
650 s.description AS service_description,
651 s.color AS service_color,
652 s.price AS service_price,
653 s.status AS service_status,
654 s.categoryId AS service_categoryId,
655 s.maxCapacity AS service_maxCapacity,
656 s.maxExtraPeople AS service_maxExtraPeople,
657 s.minCapacity AS service_minCapacity,
658 s.duration AS service_duration,
659 s.timeBefore AS service_timeBefore,
660 s.timeAfter AS service_timeAfter,
661 s.bringingAnyone AS service_bringingAnyone,
662 s.pictureFullPath AS service_picture_full,
663 s.pictureThumbPath AS service_picture_thumb,
664 s.show AS service_show,
665 s.position AS service_position,
666 s.aggregatedPrice AS service_aggregatedPrice,
667 s.settings AS service_settings,
668 s.translations AS service_translations,
669 s.recurringCycle AS service_recurringCycle,
670 s.recurringSub AS service_recurringSub,
671 s.recurringPayment AS service_recurringPayment,
672 s.deposit AS service_deposit,
673 s.depositPayment AS service_depositPayment,
674 s.depositPerPerson AS service_depositPerPerson,
675 s.fullPayment AS service_fullPayment,
676 s.mandatoryExtra AS service_mandatoryExtra,
677 s.minSelectedExtras AS service_minSelectedExtras,
678 s.customPricing AS service_customPricing,
679 s.limitPerCustomer AS service_limitPerCustomer,
680
681 e.id AS extra_id,
682 e.name AS extra_name,
683 e.price AS extra_price,
684 e.maxQuantity AS extra_maxQuantity,
685 e.duration AS extra_duration,
686 e.position AS extra_position,
687 e.aggregatedPrice AS extra_aggregatedPrice,
688 e.description AS extra_description,
689 e.translations AS extra_translations,
690
691 g.id AS gallery_id,
692 g.pictureFullPath AS gallery_picture_full,
693 g.pictureThumbPath AS gallery_picture_thumb,
694 g.position AS gallery_position
695
696 FROM {$this->table} s
697 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
698 LEFT JOIN {$this->providerServicesTable} ps ON ps.serviceId = s.id
699 LEFT JOIN {$this->galleriesTable} g ON g.entityId = s.id AND g.entityType = 'service'
700 WHERE 1 = 1 $where
701 $order"
702 );
703
704 $statement->execute($params);
705
706 $rows = $statement->fetchAll();
707 } catch (\Exception $e) {
708 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
709 }
710
711 return call_user_func([static::FACTORY, 'createCollection'], $rows);
712 }
713
714 /**
715 * @param int $serviceId
716 *
717 * @return Service
718 * @throws QueryExecutionException
719 */
720 public function getByIdWithExtras($serviceId)
721 {
722 try {
723 $statement = $this->connection->prepare(
724 "SELECT
725 s.id AS service_id,
726 s.name AS service_name,
727 s.description AS service_description,
728 s.color AS service_color,
729 s.price AS service_price,
730 s.customPricing AS service_customPricing,
731 s.limitPerCustomer AS service_limitPerCustomer,
732 s.status AS service_status,
733 s.categoryId AS service_categoryId,
734 s.maxCapacity AS service_maxCapacity,
735 s.maxExtraPeople AS service_maxExtraPeople,
736 s.minCapacity AS service_minCapacity,
737 s.duration AS service_duration,
738 s.timeBefore AS service_timeBefore,
739 s.timeAfter AS service_timeAfter,
740 s.bringingAnyone AS service_bringingAnyone,
741 s.priority AS service_priority,
742 s.pictureFullPath AS service_picture_full,
743 s.pictureThumbPath AS service_picture_thumb,
744 s.aggregatedPrice AS service_aggregatedPrice,
745 s.settings AS service_settings,
746 s.translations AS service_translations,
747 s.deposit AS service_deposit,
748 s.depositPayment AS service_depositPayment,
749 s.depositPerPerson AS service_depositPerPerson,
750 s.fullPayment AS servie_fullPayment,
751
752 e.id AS extra_id,
753 e.name AS extra_name,
754 e.description AS extra_description,
755 e.price AS extra_price,
756 e.maxQuantity AS extra_maxQuantity,
757 e.duration AS extra_duration,
758 e.aggregatedPrice AS extra_aggregatedPrice,
759 e.position AS extra_position,
760 e.translations AS extra_translations
761
762 FROM {$this->table} s
763 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
764 WHERE s.id = :serviceId
765 ORDER BY s.id, e.id"
766 );
767
768 $statement->bindParam(':serviceId', $serviceId);
769
770 $statement->execute();
771
772 $rows = $statement->fetchAll();
773 } catch (\Exception $e) {
774 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
775 }
776
777 return call_user_func([static::FACTORY, 'createCollection'], $rows)->getItem($serviceId);
778 }
779
780 /**
781 * @param array $criteria
782 *
783 * @return Collection
784 * @throws QueryExecutionException
785 */
786 public function getWithExtras($criteria)
787 {
788 $order = '';
789
790 $where = [];
791
792 if (isset($criteria['sort'])) {
793 if ($criteria['sort'] === '') {
794 $order = 'ORDER BY s.position';
795 } else {
796 $orderColumn = strpos($criteria['sort'], 'name') !== false ? 's.name' : 's.price';
797
798 $orderDirection = $criteria['sort'][0] === '-' ? 'DESC' : 'ASC';
799
800 $order = "ORDER BY {$orderColumn} {$orderDirection}";
801 }
802 }
803
804 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
805
806 try {
807 $statement = $this->connection->query(
808 "SELECT
809 s.id AS service_id,
810 s.name AS service_name,
811 s.description AS service_description,
812 s.color AS service_color,
813 s.price AS service_price,
814 s.customPricing AS service_customPricing,
815 s.status AS service_status,
816 s.categoryId AS service_categoryId,
817 s.maxCapacity AS service_maxCapacity,
818 s.maxExtraPeople AS service_maxExtraPeople,
819 s.minCapacity AS service_minCapacity,
820 s.duration AS service_duration,
821 s.timeBefore AS service_timeBefore,
822 s.timeAfter AS service_timeAfter,
823 s.bringingAnyone as service_bringingAnyone,
824 s.pictureFullPath AS service_picture_full,
825 s.pictureThumbPath AS service_picture_thumb,
826 s.position AS service_position,
827 s.show AS service_show,
828 s.aggregatedPrice AS service_aggregatedPrice,
829 s.settings AS service_settings,
830 s.recurringCycle AS service_recurringCycle,
831 s.recurringSub AS service_recurringSub,
832 s.recurringPayment AS service_recurringPayment,
833 s.translations AS service_translations,
834 s.deposit AS service_deposit,
835 s.depositPayment AS service_depositPayment,
836 s.depositPerPerson AS service_depositPerPerson,
837 s.fullPayment AS service_fullPayment,
838 s.mandatoryExtra AS service_mandatoryExtra,
839 s.minSelectedExtras AS service_minSelectedExtras,
840
841 e.id AS extra_id,
842 e.name AS extra_name,
843 e.price AS extra_price,
844 e.maxQuantity AS extra_maxQuantity,
845 e.duration AS extra_duration,
846 e.position AS extra_position,
847 e.aggregatedPrice AS extra_aggregatedPrice,
848 e.description AS extra_description,
849 e.translations AS extra_translations
850 FROM {$this->table} s
851 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
852 {$where}
853 {$order}"
854 );
855
856 $rows = $statement->fetchAll();
857 } catch (\Exception $e) {
858 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
859 }
860
861 /** @var Collection $services */
862 $services = call_user_func([static::FACTORY, 'createCollection'], $rows);
863
864 /** @var Service $service */
865 foreach ($services->getItems() as $service) {
866 if ($service->getSettings() && json_decode($service->getSettings()->getValue(), true) === null) {
867 $service->setSettings(null);
868 }
869 }
870
871 return $services;
872 }
873
874 /**
875 * Return an array of services with the number of appointments for the given date period.
876 * Keys of the array are Services IDs.
877 *
878 * @param $criteria
879 *
880 * @return array
881 * @throws QueryExecutionException
882 * @throws \AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException
883 */
884 public function getAllNumberOfAppointments($criteria)
885 {
886 $appointmentTable = AppointmentsTable::getTableName();
887
888 $params = [];
889 $where = [];
890
891 if ($criteria['dates']) {
892 $where[] = "(a.bookingStart BETWEEN :bookingFrom AND :bookingTo)";
893 $params[':bookingFrom'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][0]);
894 $params[':bookingTo'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][1]);
895 }
896
897 if (isset($criteria['status'])) {
898 $where[] = 's.status = :status';
899 $params[':status'] = $criteria['status'];
900 }
901
902 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
903
904 try {
905 $statement = $this->connection->prepare(
906 "SELECT
907 s.id,
908 s.name,
909 COUNT(a.providerId) AS appointments
910 FROM {$this->table} s
911 INNER JOIN {$appointmentTable} a ON s.id = a.serviceId
912 $where
913 GROUP BY serviceId"
914 );
915
916 $statement->execute($params);
917
918 $rows = $statement->fetchAll();
919 } catch (\Exception $e) {
920 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
921 }
922
923 $result = [];
924
925 foreach ($rows as $row) {
926 $result[$row['id']] = $row;
927 }
928
929 return $result;
930 }
931
932 /**
933 * Return an array of services with the number of views for the given date period.
934 * Keys of the array are Services IDs.
935 *
936 * @param $criteria
937 *
938 * @return array
939 * @throws QueryExecutionException
940 */
941 public function getAllNumberOfViews($criteria)
942 {
943 $params = [];
944
945 $where = [];
946
947 if ($criteria['dates']) {
948 $where[] = "(sv.date BETWEEN :bookingFrom AND :bookingTo)";
949
950 $params[':bookingFrom'] = explode(' ', $criteria['dates'][0])[0];
951
952 $params[':bookingTo'] = explode(' ', $criteria['dates'][1])[0];
953 }
954
955 if (isset($criteria['status'])) {
956 $where[] = 's.status = :status';
957
958 $params[':status'] = $criteria['status'];
959 }
960
961 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
962
963 try {
964 $statement = $this->connection->prepare(
965 "SELECT
966 s.id,
967 s.name,
968 SUM(sv.views) AS views
969 FROM {$this->table} s
970 INNER JOIN {$this->serviceViewsTable} sv ON sv.serviceId = s.id
971 $where
972 GROUP BY s.id"
973 );
974
975 $statement->execute($params);
976
977 $rows = $statement->fetchAll();
978 } catch (\Exception $e) {
979 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
980 }
981
982 $result = [];
983
984 foreach ($rows as $row) {
985 $result[$row['id']] = $row;
986 }
987
988 return $result;
989 }
990
991 /**
992 * @param $serviceId
993 *
994 * @return bool
995 * @throws QueryExecutionException
996 */
997 public function addViewStats($serviceId)
998 {
999 $date = DateTimeService::getNowDate();
1000
1001 $params = [
1002 ':serviceId' => $serviceId,
1003 ':date' => $date,
1004 ':views' => 1
1005 ];
1006
1007 try {
1008 // Check if there is already data for this provider for this date
1009 $statement = $this->connection->prepare(
1010 "SELECT COUNT(*) AS count
1011 FROM {$this->serviceViewsTable} AS pv
1012 WHERE pv.serviceId = :serviceId
1013 AND pv.date = :date"
1014 );
1015
1016 $statement->bindParam(':serviceId', $serviceId);
1017 $statement->bindParam(':date', $date);
1018 $statement->execute();
1019 $count = $statement->fetch()['count'];
1020
1021 if (!$count) {
1022 $statement = $this->connection->prepare(
1023 "INSERT INTO {$this->serviceViewsTable}
1024 (`serviceId`, `date`, `views`)
1025 VALUES
1026 (:serviceId, :date, :views)"
1027 );
1028 } else {
1029 $statement = $this->connection->prepare(
1030 "UPDATE {$this->serviceViewsTable} pv SET pv.views = pv.views + :views
1031 WHERE pv.serviceId = :serviceId
1032 AND pv.date = :date"
1033 );
1034 }
1035
1036 $statement->execute($params);
1037 } catch (\Exception $e) {
1038 throw new QueryExecutionException('Unable to add data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
1039 }
1040
1041 return true;
1042 }
1043
1044 /**
1045 * @param int $serviceId
1046 *
1047 * @return mixed
1048 * @throws QueryExecutionException
1049 */
1050 public function deleteViewStats($serviceId)
1051 {
1052 $params = [
1053 ':serviceId' => $serviceId,
1054 ];
1055
1056 try {
1057 $statement = $this->connection->prepare(
1058 "DELETE FROM {$this->serviceViewsTable} WHERE serviceId = :serviceId"
1059 );
1060
1061 $statement->execute($params);
1062 return true;
1063 } catch (\Exception $e) {
1064 throw new QueryExecutionException('Unable to delete data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
1065 }
1066 }
1067 }
1068