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 / ServiceRepository.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
ServiceRepository.php
1099 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 $providersJoin = $this->getProvidersJoin($criteria, $params, $where);
408
409 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
410
411 $order = "ORDER BY {$orderColumn} {$orderDirection}";
412
413 $limit = $this->getLimit(
414 !empty($criteria['page']) ? (int)$criteria['page'] : 0,
415 (int)$itemsPerPage
416 );
417
418 try {
419 $statement = $this->connection->prepare(
420 "SELECT DISTINCT s.*
421 FROM {$this->table} s
422 {$providersJoin}
423 {$where}
424 {$order}
425 {$limit}"
426 );
427
428 $statement->execute($params);
429
430 $rows = $statement->fetchAll();
431 } catch (\Exception $e) {
432 throw new QueryExecutionException('Unable to find by ids in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
433 }
434
435 $items = new Collection();
436
437 foreach ($rows as $row) {
438 $items->addItem(call_user_func([static::FACTORY, 'create'], $row), $row['id']);
439 }
440
441 return $items;
442 }
443
444 /**
445 * @param array $criteria
446 *
447 * @return mixed
448 * @throws QueryExecutionException
449 */
450 public function getCount($criteria)
451 {
452 $params = [];
453
454 $where = [];
455
456 if (!empty($criteria['search'])) {
457 $terms = preg_split('/\s+/', trim($criteria['search']));
458 $termIndex = 0;
459
460 foreach ($terms as $term) {
461 $param = ":search{$termIndex}";
462 $params[$param] = "%{$term}%";
463
464 $where[] = "(
465 s.name LIKE {$param}
466 OR s.description LIKE {$param}
467 OR s.id LIKE {$param}
468 )";
469
470 $termIndex++;
471 }
472 }
473
474 if (!empty($criteria['categoryId'])) {
475 $params[':categoryId'] = $criteria['categoryId'];
476
477 $where[] = 's.categoryId = :categoryId';
478 }
479
480 $providersJoin = $this->getProvidersJoin($criteria, $params, $where);
481
482 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
483
484 try {
485 $statement = $this->connection->prepare(
486 "SELECT COUNT(DISTINCT s.id) as count
487 FROM {$this->table} s
488 {$providersJoin}
489 {$where}"
490 );
491
492 $statement->execute($params);
493
494 $row = $statement->fetch()['count'];
495 } catch (\Exception $e) {
496 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
497 }
498
499 return $row;
500 }
501
502 /**
503 * @param int $serviceId
504 * @param int $userId
505 *
506 * @return Collection
507 * @throws QueryExecutionException
508 */
509 public function getProviderServicesWithExtras($serviceId, $userId)
510 {
511 try {
512 $statement = $this->connection->prepare(
513 "SELECT
514 s.id AS service_id,
515 s.name AS service_name,
516 s.description AS service_description,
517 s.color AS service_color,
518 ps.price AS service_price,
519 s.status AS service_status,
520 s.categoryId AS service_categoryId,
521 ps.minCapacity AS service_minCapacity,
522 ps.maxCapacity AS service_maxCapacity,
523 ps.customPricing AS service_customPricing,
524 s.limitPerCustomer AS service_limitPerCustomer,
525 s.duration AS service_duration,
526 s.timeBefore AS service_timeBefore,
527 s.timeAfter AS service_timeAfter,
528 s.bringingAnyone as service_bringingAnyone,
529 s.pictureFullPath AS service_picture_full,
530 s.pictureThumbPath AS service_picture_thumb,
531 s.aggregatedPrice AS service_aggregatedPrice,
532 s.settings AS service_settings,
533 s.recurringPayment AS service_recurringPayment,
534 s.translations AS service_translations,
535 s.show AS service_show,
536 s.deposit AS service_deposit,
537 s.depositPayment AS service_depositPayment,
538 s.depositPerPerson AS service_depositPerPerson,
539 s.fullPayment AS service_fullPayment,
540
541 e.id AS extra_id,
542 e.name AS extra_name,
543 e.price AS extra_price,
544 e.maxQuantity AS extra_maxQuantity,
545 e.duration AS extra_duration,
546 e.aggregatedPrice AS extra_aggregatedPrice,
547 e.position AS extra_position,
548 e.translations AS extra_translations
549 FROM {$this->table} s
550 INNER JOIN {$this->providerServicesTable} ps ON s.id = ps.serviceId
551 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
552 WHERE ps.userId = :userId AND ps.serviceId = :serviceId"
553 );
554
555 $statement->bindParam(':userId', $userId);
556 $statement->bindParam(':serviceId', $serviceId);
557
558 $statement->execute();
559
560 $rows = $statement->fetchAll();
561 } catch (\Exception $e) {
562 throw new QueryExecutionException('Unable to find by ids in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
563 }
564
565 return call_user_func([static::FACTORY, 'createCollection'], $rows);
566 }
567
568 /**
569 * @param $criteria
570 *
571 * @return Collection
572 * @throws QueryExecutionException
573 */
574 public function getByCriteria($criteria)
575 {
576 $params = [];
577 $where = [];
578
579 $order = 'ORDER BY s.name ASC';
580 if (isset($criteria['sort'])) {
581 if ($criteria['sort'] === '') {
582 $order = 'ORDER BY s.position';
583 } else {
584 $orderColumn = strpos($criteria['sort'], 'name') !== false ? 's.name' : 's.price';
585 $orderDirection = $criteria['sort'][0] === '-' ? 'DESC' : 'ASC';
586 $order = "ORDER BY {$orderColumn} {$orderDirection}";
587 }
588 }
589
590 if (!empty($criteria['search'])) {
591 $terms = preg_split('/\s+/', trim($criteria['search']));
592 $termIndex = 0;
593
594 foreach ($terms as $term) {
595 $param = ":search{$termIndex}";
596 $params[$param] = "%{$term}%";
597
598 $where[] = "(
599 s.name LIKE {$param}
600 )";
601
602 $termIndex++;
603 }
604 }
605
606 if (!empty($criteria['services'])) {
607 $queryServices = [];
608
609 foreach ((array)$criteria['services'] as $index => $value) {
610 $param = ':service' . $index;
611 $queryServices[] = $param;
612 $params[$param] = $value;
613 }
614
615 $where[] = 's.id IN (' . implode(', ', $queryServices) . ')';
616 }
617
618 if (!empty($criteria['categories'])) {
619 $queryCategories = [];
620
621 foreach ((array)$criteria['categories'] as $index => $value) {
622 $param = ':category' . $index;
623 $queryCategories[] = $param;
624 $params[$param] = $value;
625 }
626
627 $where[] = 's.categoryId IN (' . implode(', ', $queryCategories) . ')';
628 }
629
630 if (!empty($criteria['providers'])) {
631 $queryProviders = [];
632
633 foreach ((array)$criteria['providers'] as $index => $value) {
634 $param = ':provider' . $index;
635 $queryProviders[] = $param;
636 $params[$param] = $value;
637 }
638
639 $where[] = 'ps.userId IN (' . implode(', ', $queryProviders) . ')';
640 }
641
642 if (!empty($criteria['status'])) {
643 $params[':status'] = $criteria['status'];
644
645 $where[] = 's.status = :status';
646 }
647
648 $where = $where ? ' AND ' . implode(' AND ', $where) : '';
649
650 try {
651 $statement = $this->connection->prepare(
652 "SELECT
653 s.id AS service_id,
654 s.name AS service_name,
655 s.description AS service_description,
656 s.color AS service_color,
657 s.price AS service_price,
658 s.status AS service_status,
659 s.categoryId AS service_categoryId,
660 s.maxCapacity AS service_maxCapacity,
661 s.maxExtraPeople AS service_maxExtraPeople,
662 s.minCapacity AS service_minCapacity,
663 s.duration AS service_duration,
664 s.timeBefore AS service_timeBefore,
665 s.timeAfter AS service_timeAfter,
666 s.bringingAnyone AS service_bringingAnyone,
667 s.pictureFullPath AS service_picture_full,
668 s.pictureThumbPath AS service_picture_thumb,
669 s.show AS service_show,
670 s.position AS service_position,
671 s.aggregatedPrice AS service_aggregatedPrice,
672 s.settings AS service_settings,
673 s.translations AS service_translations,
674 s.recurringCycle AS service_recurringCycle,
675 s.recurringSub AS service_recurringSub,
676 s.recurringPayment AS service_recurringPayment,
677 s.deposit AS service_deposit,
678 s.depositPayment AS service_depositPayment,
679 s.depositPerPerson AS service_depositPerPerson,
680 s.fullPayment AS service_fullPayment,
681 s.mandatoryExtra AS service_mandatoryExtra,
682 s.minSelectedExtras AS service_minSelectedExtras,
683 s.customPricing AS service_customPricing,
684 s.limitPerCustomer AS service_limitPerCustomer,
685
686 e.id AS extra_id,
687 e.name AS extra_name,
688 e.price AS extra_price,
689 e.maxQuantity AS extra_maxQuantity,
690 e.duration AS extra_duration,
691 e.position AS extra_position,
692 e.aggregatedPrice AS extra_aggregatedPrice,
693 e.description AS extra_description,
694 e.translations AS extra_translations,
695
696 g.id AS gallery_id,
697 g.pictureFullPath AS gallery_picture_full,
698 g.pictureThumbPath AS gallery_picture_thumb,
699 g.position AS gallery_position
700
701 FROM {$this->table} s
702 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
703 LEFT JOIN {$this->providerServicesTable} ps ON ps.serviceId = s.id
704 LEFT JOIN {$this->galleriesTable} g ON g.entityId = s.id AND g.entityType = 'service'
705 WHERE 1 = 1 $where
706 $order"
707 );
708
709 $statement->execute($params);
710
711 $rows = $statement->fetchAll();
712 } catch (\Exception $e) {
713 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
714 }
715
716 return call_user_func([static::FACTORY, 'createCollection'], $rows);
717 }
718
719 /**
720 * @param int $serviceId
721 *
722 * @return Service
723 * @throws QueryExecutionException
724 */
725 public function getByIdWithExtras($serviceId)
726 {
727 try {
728 $statement = $this->connection->prepare(
729 "SELECT
730 s.id AS service_id,
731 s.name AS service_name,
732 s.description AS service_description,
733 s.color AS service_color,
734 s.price AS service_price,
735 s.customPricing AS service_customPricing,
736 s.limitPerCustomer AS service_limitPerCustomer,
737 s.status AS service_status,
738 s.categoryId AS service_categoryId,
739 s.maxCapacity AS service_maxCapacity,
740 s.maxExtraPeople AS service_maxExtraPeople,
741 s.minCapacity AS service_minCapacity,
742 s.duration AS service_duration,
743 s.timeBefore AS service_timeBefore,
744 s.timeAfter AS service_timeAfter,
745 s.bringingAnyone AS service_bringingAnyone,
746 s.priority AS service_priority,
747 s.pictureFullPath AS service_picture_full,
748 s.pictureThumbPath AS service_picture_thumb,
749 s.aggregatedPrice AS service_aggregatedPrice,
750 s.settings AS service_settings,
751 s.translations AS service_translations,
752 s.deposit AS service_deposit,
753 s.depositPayment AS service_depositPayment,
754 s.depositPerPerson AS service_depositPerPerson,
755 s.fullPayment AS servie_fullPayment,
756
757 e.id AS extra_id,
758 e.name AS extra_name,
759 e.description AS extra_description,
760 e.price AS extra_price,
761 e.maxQuantity AS extra_maxQuantity,
762 e.duration AS extra_duration,
763 e.aggregatedPrice AS extra_aggregatedPrice,
764 e.position AS extra_position,
765 e.translations AS extra_translations
766
767 FROM {$this->table} s
768 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
769 WHERE s.id = :serviceId
770 ORDER BY s.id, e.id"
771 );
772
773 $statement->bindParam(':serviceId', $serviceId);
774
775 $statement->execute();
776
777 $rows = $statement->fetchAll();
778 } catch (\Exception $e) {
779 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
780 }
781
782 return call_user_func([static::FACTORY, 'createCollection'], $rows)->getItem($serviceId);
783 }
784
785 /**
786 * @param array $criteria
787 *
788 * @return Collection
789 * @throws QueryExecutionException
790 */
791 public function getWithExtras($criteria)
792 {
793 $order = '';
794
795 $where = [];
796
797 if (isset($criteria['sort'])) {
798 if ($criteria['sort'] === '') {
799 $order = 'ORDER BY s.position';
800 } else {
801 $orderColumn = strpos($criteria['sort'], 'name') !== false ? 's.name' : 's.price';
802
803 $orderDirection = $criteria['sort'][0] === '-' ? 'DESC' : 'ASC';
804
805 $order = "ORDER BY {$orderColumn} {$orderDirection}";
806 }
807 }
808
809 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
810
811 try {
812 $statement = $this->connection->query(
813 "SELECT
814 s.id AS service_id,
815 s.name AS service_name,
816 s.description AS service_description,
817 s.color AS service_color,
818 s.price AS service_price,
819 s.customPricing AS service_customPricing,
820 s.status AS service_status,
821 s.categoryId AS service_categoryId,
822 s.maxCapacity AS service_maxCapacity,
823 s.maxExtraPeople AS service_maxExtraPeople,
824 s.minCapacity AS service_minCapacity,
825 s.duration AS service_duration,
826 s.timeBefore AS service_timeBefore,
827 s.timeAfter AS service_timeAfter,
828 s.bringingAnyone as service_bringingAnyone,
829 s.pictureFullPath AS service_picture_full,
830 s.pictureThumbPath AS service_picture_thumb,
831 s.position AS service_position,
832 s.show AS service_show,
833 s.aggregatedPrice AS service_aggregatedPrice,
834 s.settings AS service_settings,
835 s.recurringCycle AS service_recurringCycle,
836 s.recurringSub AS service_recurringSub,
837 s.recurringPayment AS service_recurringPayment,
838 s.translations AS service_translations,
839 s.deposit AS service_deposit,
840 s.depositPayment AS service_depositPayment,
841 s.depositPerPerson AS service_depositPerPerson,
842 s.fullPayment AS service_fullPayment,
843 s.mandatoryExtra AS service_mandatoryExtra,
844 s.minSelectedExtras AS service_minSelectedExtras,
845
846 e.id AS extra_id,
847 e.name AS extra_name,
848 e.price AS extra_price,
849 e.maxQuantity AS extra_maxQuantity,
850 e.duration AS extra_duration,
851 e.position AS extra_position,
852 e.aggregatedPrice AS extra_aggregatedPrice,
853 e.description AS extra_description,
854 e.translations AS extra_translations
855 FROM {$this->table} s
856 LEFT JOIN {$this->extrasTable} e ON e.serviceId = s.id
857 {$where}
858 {$order}"
859 );
860
861 $rows = $statement->fetchAll();
862 } catch (\Exception $e) {
863 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
864 }
865
866 /** @var Collection $services */
867 $services = call_user_func([static::FACTORY, 'createCollection'], $rows);
868
869 /** @var Service $service */
870 foreach ($services->getItems() as $service) {
871 if ($service->getSettings() && json_decode($service->getSettings()->getValue(), true) === null) {
872 $service->setSettings(null);
873 }
874 }
875
876 return $services;
877 }
878
879 /**
880 * Return an array of services with the number of appointments for the given date period.
881 * Keys of the array are Services IDs.
882 *
883 * @param $criteria
884 *
885 * @return array
886 * @throws QueryExecutionException
887 * @throws \AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException
888 */
889 public function getAllNumberOfAppointments($criteria)
890 {
891 $appointmentTable = AppointmentsTable::getTableName();
892
893 $params = [];
894 $where = [];
895
896 if ($criteria['dates']) {
897 $where[] = "(a.bookingStart BETWEEN :bookingFrom AND :bookingTo)";
898 $params[':bookingFrom'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][0]);
899 $params[':bookingTo'] = DateTimeService::getCustomDateTimeInUtc($criteria['dates'][1]);
900 }
901
902 if (isset($criteria['status'])) {
903 $where[] = 's.status = :status';
904 $params[':status'] = $criteria['status'];
905 }
906
907 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
908
909 try {
910 $statement = $this->connection->prepare(
911 "SELECT
912 s.id,
913 s.name,
914 COUNT(a.providerId) AS appointments
915 FROM {$this->table} s
916 INNER JOIN {$appointmentTable} a ON s.id = a.serviceId
917 $where
918 GROUP BY serviceId"
919 );
920
921 $statement->execute($params);
922
923 $rows = $statement->fetchAll();
924 } catch (\Exception $e) {
925 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
926 }
927
928 $result = [];
929
930 foreach ($rows as $row) {
931 $result[$row['id']] = $row;
932 }
933
934 return $result;
935 }
936
937 /**
938 * Return an array of services with the number of views for the given date period.
939 * Keys of the array are Services IDs.
940 *
941 * @param $criteria
942 *
943 * @return array
944 * @throws QueryExecutionException
945 */
946 public function getAllNumberOfViews($criteria)
947 {
948 $params = [];
949
950 $where = [];
951
952 if ($criteria['dates']) {
953 $where[] = "(sv.date BETWEEN :bookingFrom AND :bookingTo)";
954
955 $params[':bookingFrom'] = explode(' ', $criteria['dates'][0])[0];
956
957 $params[':bookingTo'] = explode(' ', $criteria['dates'][1])[0];
958 }
959
960 if (isset($criteria['status'])) {
961 $where[] = 's.status = :status';
962
963 $params[':status'] = $criteria['status'];
964 }
965
966 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
967
968 try {
969 $statement = $this->connection->prepare(
970 "SELECT
971 s.id,
972 s.name,
973 SUM(sv.views) AS views
974 FROM {$this->table} s
975 INNER JOIN {$this->serviceViewsTable} sv ON sv.serviceId = s.id
976 $where
977 GROUP BY s.id"
978 );
979
980 $statement->execute($params);
981
982 $rows = $statement->fetchAll();
983 } catch (\Exception $e) {
984 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
985 }
986
987 $result = [];
988
989 foreach ($rows as $row) {
990 $result[$row['id']] = $row;
991 }
992
993 return $result;
994 }
995
996 /**
997 * @param $serviceId
998 *
999 * @return bool
1000 * @throws QueryExecutionException
1001 */
1002 public function addViewStats($serviceId)
1003 {
1004 $date = DateTimeService::getNowDate();
1005
1006 $params = [
1007 ':serviceId' => $serviceId,
1008 ':date' => $date,
1009 ':views' => 1
1010 ];
1011
1012 try {
1013 // Check if there is already data for this provider for this date
1014 $statement = $this->connection->prepare(
1015 "SELECT COUNT(*) AS count
1016 FROM {$this->serviceViewsTable} AS pv
1017 WHERE pv.serviceId = :serviceId
1018 AND pv.date = :date"
1019 );
1020
1021 $statement->bindParam(':serviceId', $serviceId);
1022 $statement->bindParam(':date', $date);
1023 $statement->execute();
1024 $count = $statement->fetch()['count'];
1025
1026 if (!$count) {
1027 $statement = $this->connection->prepare(
1028 "INSERT INTO {$this->serviceViewsTable}
1029 (`serviceId`, `date`, `views`)
1030 VALUES
1031 (:serviceId, :date, :views)"
1032 );
1033 } else {
1034 $statement = $this->connection->prepare(
1035 "UPDATE {$this->serviceViewsTable} pv SET pv.views = pv.views + :views
1036 WHERE pv.serviceId = :serviceId
1037 AND pv.date = :date"
1038 );
1039 }
1040
1041 $statement->execute($params);
1042 } catch (\Exception $e) {
1043 throw new QueryExecutionException('Unable to add data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
1044 }
1045
1046 return true;
1047 }
1048
1049 /**
1050 * @param int $serviceId
1051 *
1052 * @return mixed
1053 * @throws QueryExecutionException
1054 */
1055 public function deleteViewStats($serviceId)
1056 {
1057 $params = [
1058 ':serviceId' => $serviceId,
1059 ];
1060
1061 try {
1062 $statement = $this->connection->prepare(
1063 "DELETE FROM {$this->serviceViewsTable} WHERE serviceId = :serviceId"
1064 );
1065
1066 $statement->execute($params);
1067 return true;
1068 } catch (\Exception $e) {
1069 throw new QueryExecutionException('Unable to delete data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
1070 }
1071 }
1072
1073 /**
1074 * @param array $criteria
1075 * @param array $params
1076 * @param array $where
1077 *
1078 * @return string
1079 */
1080 private function getProvidersJoin($criteria, &$params, &$where)
1081 {
1082 if (empty($criteria['providers'])) {
1083 return '';
1084 }
1085
1086 $queryProviders = [];
1087
1088 foreach ((array)$criteria['providers'] as $index => $value) {
1089 $param = ':provider' . $index;
1090 $queryProviders[] = $param;
1091 $params[$param] = $value;
1092 }
1093
1094 $where[] = 'ps.userId IN (' . implode(', ', $queryProviders) . ')';
1095
1096 return "INNER JOIN {$this->providerServicesTable} ps ON ps.serviceId = s.id";
1097 }
1098 }
1099