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