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 / PackageRepository.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
PackageRepository.php
608 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\Entity\Bookable\Service\Package;
13 use AmeliaBooking\Domain\Factory\Bookable\Service\PackageFactory;
14 use AmeliaBooking\Infrastructure\Connection;
15 use AmeliaBooking\Infrastructure\DB\WPDB\Statement;
16 use AmeliaBooking\Infrastructure\Repository\AbstractRepository;
17 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
18 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\PackagesServicesLocationsTable;
19 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\PackagesServicesProvidersTable;
20 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\PackagesServicesTable;
21 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Bookable\ServicesTable;
22 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Gallery\GalleriesTable;
23 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Location\LocationsTable;
24 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\User\Provider\ProvidersServiceTable;
25 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\User\UsersTable;
26
27 /**
28 * Class PackageRepository
29 *
30 * @package AmeliaBooking\Infrastructure\Repository\Service
31 */
32 class PackageRepository extends AbstractRepository
33 {
34 public const FACTORY = PackageFactory::class;
35
36 /**
37 * @param Connection $connection
38 * @param string $table
39 */
40 public function __construct(
41 Connection $connection,
42 $table
43 ) {
44 parent::__construct($connection, $table);
45 }
46
47 /**
48 * @param Package $entity
49 *
50 * @return int
51 * @throws QueryExecutionException
52 */
53 public function add($entity)
54 {
55 $data = $entity->toArray();
56
57 $params = [
58 ':name' => $data['name'],
59 ':description' => $data['description'],
60 ':color' => $data['color'],
61 ':price' => $data['price'],
62 ':status' => $data['status'],
63 ':pictureFullPath' => $data['pictureFullPath'],
64 ':pictureThumbPath' => $data['pictureThumbPath'],
65 ':position' => $data['position'],
66 ':calculatedPrice' => $data['calculatedPrice'] ? 1 : 0,
67 ':discount' => $data['discount'],
68 ':settings' => $data['settings'],
69 ':endDate' => $data['endDate'],
70 ':durationCount' => $data['durationCount'],
71 ':durationType' => $data['durationType'],
72 ':translations' => $data['translations'],
73 ':deposit' => $data['deposit'],
74 ':depositPayment' => $data['depositPayment'],
75 ':fullPayment' => $data['fullPayment'] ? 1 : 0,
76 ':sharedCapacity' => $data['sharedCapacity'] ? 1 : 0,
77 ':quantity' => $data['quantity'],
78 ':limitPerCustomer' => $data['limitPerCustomer']
79 ];
80
81 try {
82 $statement = $this->connection->prepare(
83 "INSERT INTO
84 {$this->table}
85 (
86 `name`,
87 `description`,
88 `color`,
89 `price`,
90 `status`,
91 `pictureFullPath`,
92 `pictureThumbPath`,
93 `calculatedPrice`,
94 `discount`,
95 `position`,
96 `settings`,
97 `endDate`,
98 `durationCount`,
99 `durationType`,
100 `translations`,
101 `deposit`,
102 `depositPayment`,
103 `fullPayment`,
104 `sharedCapacity`,
105 `quantity`,
106 `limitPerCustomer`
107 ) VALUES (
108 :name,
109 :description,
110 :color,
111 :price,
112 :status,
113 :pictureFullPath,
114 :pictureThumbPath,
115 :calculatedPrice,
116 :discount,
117 :position,
118 :settings,
119 :endDate,
120 :durationCount,
121 :durationType,
122 :translations,
123 :deposit,
124 :depositPayment,
125 :fullPayment,
126 :sharedCapacity,
127 :quantity,
128 :limitPerCustomer
129 )"
130 );
131
132 $statement->execute($params);
133
134 return $this->connection->lastInsertId();
135 } catch (\Exception $e) {
136 throw new QueryExecutionException('Unable to add data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
137 }
138 }
139
140 /**
141 * @param int $packageId
142 * @param Package $entity
143 *
144 * @throws QueryExecutionException
145 */
146 public function update($packageId, $entity)
147 {
148 $data = $entity->toArray();
149
150 $params = [
151 ':name' => $data['name'],
152 ':description' => $data['description'],
153 ':color' => $data['color'],
154 ':price' => $data['price'],
155 ':status' => $data['status'],
156 ':pictureFullPath' => $data['pictureFullPath'],
157 ':pictureThumbPath' => $data['pictureThumbPath'],
158 ':position' => $data['position'],
159 ':calculatedPrice' => $data['calculatedPrice'] ? 1 : 0,
160 ':discount' => $data['discount'],
161 ':settings' => $data['settings'],
162 ':endDate' => $data['endDate'],
163 ':durationCount' => $data['durationCount'],
164 ':durationType' => $data['durationType'],
165 ':translations' => $data['translations'],
166 ':deposit' => $data['deposit'],
167 ':depositPayment' => $data['depositPayment'],
168 ':fullPayment' => $data['fullPayment'] ? 1 : 0,
169 ':sharedCapacity' => $data['sharedCapacity'] ? 1 : 0,
170 ':quantity' => $data['quantity'],
171 ':limitPerCustomer' => $data['limitPerCustomer'],
172 ':id' => $packageId
173 ];
174
175
176 try {
177 $statement = $this->connection->prepare(
178 "UPDATE {$this->table}
179 SET
180 `name` = :name,
181 `description` = :description,
182 `color` = :color,
183 `price` = :price,
184 `status` = :status,
185 `pictureFullPath` = :pictureFullPath,
186 `pictureThumbPath` = :pictureThumbPath,
187 `position` = :position,
188 `calculatedPrice` = :calculatedPrice,
189 `discount` = :discount,
190 `settings` = :settings,
191 `endDate` = :endDate,
192 `durationCount` = :durationCount,
193 `durationType` = :durationType,
194 `translations` = :translations,
195 `deposit` = :deposit,
196 `depositPayment` = :depositPayment,
197 `fullPayment` = :fullPayment,
198 `sharedCapacity` = :sharedCapacity,
199 `quantity` = :quantity,
200 `limitPerCustomer` = :limitPerCustomer
201 WHERE
202 id = :id"
203 );
204
205 $statement->execute($params);
206 } catch (\Exception $e) {
207 throw new QueryExecutionException('Unable to save data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
208 }
209 }
210
211 /**
212 * @param $criteria
213 *
214 * @return Collection
215 * @throws QueryExecutionException
216 * @throws InvalidArgumentException
217 */
218 public function getByCriteria($criteria)
219 {
220 $params = [];
221 $where = [];
222 $order = 'ORDER BY p.name, ps.position ASC, ps.id ASC';
223
224 if (isset($criteria['sort'])) {
225 if ($criteria['sort'] === '') {
226 $order = 'ORDER BY p.position';
227 } else {
228 $sortField = $criteria['sort'];
229 if (strpos($sortField, '-') === 0) {
230 $sortField = substr($sortField, 1);
231 }
232 switch ($sortField) {
233 case 'name':
234 $orderColumn = 'p.name';
235 break;
236 case 'price':
237 $orderColumn = 'p.price';
238 break;
239 case 'id':
240 $orderColumn = 'p.id';
241 break;
242 case 'services':
243 $packageServicesTable = PackagesServicesTable::getTableName();
244 $orderColumn = "(SELECT COUNT(*) FROM {$packageServicesTable} WHERE packageId = p.id)";
245 break;
246 default:
247 $orderColumn = 'p.name';
248 }
249 $orderDirection = $criteria['sort'][0] === '-' ? 'DESC' : 'ASC';
250 $order = "ORDER BY {$orderColumn} {$orderDirection}";
251 }
252 }
253
254 if (!empty($criteria['id'])) {
255 $params[':id'] = $criteria['id'];
256 $where[] = 'p.id = :id';
257 }
258
259 if (!empty($criteria['search'])) {
260 $terms = preg_split('/\s+/', trim($criteria['search']));
261 $termIndex = 0;
262
263 foreach ($terms as $term) {
264 $param = ":search{$termIndex}";
265 $params[$param] = "%{$term}%";
266
267 $where[] = "(
268 p.name LIKE {$param}
269 OR p.id LIKE {$param}
270 )";
271
272 $termIndex++;
273 }
274 }
275
276 if (!empty($criteria['services'])) {
277 $queryServices = [];
278 foreach ((array)$criteria['services'] as $index => $value) {
279 $param = ':service' . $index;
280 $queryServices[] = $param;
281 $params[$param] = $value;
282 }
283 $where[] = 's.id IN (' . implode(', ', $queryServices) . ')';
284 }
285
286 if (!empty($criteria['packages'])) {
287 $queryPackages = [];
288 foreach ((array)$criteria['packages'] as $index => $value) {
289 $param = ':package' . $index;
290 $queryPackages[] = $param;
291 $params[$param] = $value;
292 }
293 $where[] = 'p.id IN (' . implode(', ', $queryPackages) . ')';
294 }
295
296 if (!empty($criteria['status'])) {
297 $params[':status'] = $criteria['status'];
298 $where[] = 's.status = :status';
299 }
300
301 $whereSql = $where ? ' AND ' . implode(' AND ', $where) : '';
302 $servicesTable = ServicesTable::getTableName();
303 $usersTable = UsersTable::getTableName();
304 $locationsTable = LocationsTable::getTableName();
305 $packageServicesTable = PackagesServicesTable::getTableName();
306 $providersToServicesTable = ProvidersServiceTable::getTableName();
307 $packageServicesProvidersTable = PackagesServicesProvidersTable::getTableName();
308 $packageServicesLocationsTable = PackagesServicesLocationsTable::getTableName();
309 $galleriesTable = GalleriesTable::getTableName();
310
311 // Define the common SELECT part of the query
312 $selectSql = "SELECT
313 p.id AS package_id,
314 p.name AS package_name,
315 p.description AS package_description,
316 p.color AS package_color,
317 p.price AS package_price,
318 p.status AS package_status,
319 p.pictureFullPath AS package_picture_full,
320 p.pictureThumbPath AS package_picture_thumb,
321 p.calculatedPrice AS package_calculated_price,
322 p.discount AS package_discount,
323 p.position AS package_position,
324 p.settings AS package_settings,
325 p.endDate AS package_endDate,
326 p.durationCount AS package_durationCount,
327 p.durationType AS package_durationType,
328 p.translations AS package_translations,
329 p.deposit AS package_deposit,
330 p.depositPayment AS package_depositPayment,
331 p.fullPayment AS package_fullPayment,
332 p.sharedCapacity AS package_sharedCapacity,
333 p.quantity AS package_quantity,
334 p.limitPerCustomer AS package_limitPerCustomer,
335 ps.id AS package_service_id,
336 ps.quantity AS package_service_quantity,
337 ps.minimumScheduled AS package_service_minimumScheduled,
338 ps.maximumScheduled AS package_service_maximumScheduled,
339 ps.allowProviderSelection AS package_service_allowProviderSelection,
340 ps.position AS package_service_position,
341 s.id AS service_id,
342 s.price AS service_price,
343 s.minCapacity AS service_minCapacity,
344 s.maxCapacity AS service_maxCapacity,
345 s.name AS service_name,
346 s.description AS service_description,
347 s.status AS service_status,
348 s.categoryId AS service_categoryId,
349 s.duration AS service_duration,
350 s.timeBefore AS service_timeBefore,
351 s.timeAfter AS service_timeAfter,
352 s.pictureFullPath AS service_picture_full,
353 s.pictureThumbPath AS service_picture_thumb,
354 s.translations AS service_translations,
355 s.show AS service_show,
356 s.color AS service_color,
357 l.id AS location_id,
358 l.name AS location_name,
359 l.address AS location_address,
360 l.phone AS location_phone,
361 l.latitude AS location_latitude,
362 l.longitude AS location_longitude,
363 COALESCE(psp.userId, pts.userId) AS provider_id,
364 pu.firstName AS provider_firstName,
365 pu.lastName AS provider_lastName,
366 pu.email AS provider_email,
367 pu.status AS provider_status,
368 pu.translations AS provider_translations,
369 pu.pictureFullPath AS provider_picture_full,
370 pu.pictureThumbPath AS provider_picture_thumb,
371 g.id AS gallery_id,
372 g.pictureFullPath AS gallery_picture_full,
373 g.pictureThumbPath AS gallery_picture_thumb,
374 g.position AS gallery_position";
375
376 $fromSql = "FROM {$this->table} p
377 LEFT JOIN {$packageServicesTable} ps ON ps.packageId = p.id
378 LEFT JOIN {$servicesTable} s ON ps.serviceId = s.id
379 LEFT JOIN {$packageServicesProvidersTable} psp ON psp.packageServiceId = ps.id
380 LEFT JOIN {$providersToServicesTable} pts ON pts.serviceId = ps.serviceId
381 LEFT JOIN {$usersTable} pu ON pu.id = COALESCE(psp.userId, pts.userId)
382 LEFT JOIN {$packageServicesLocationsTable} psl ON psl.packageServiceId = ps.id
383 LEFT JOIN {$locationsTable} l ON l.id = psl.locationId
384 LEFT JOIN {$galleriesTable} g ON g.entityId = p.id AND g.entityType = 'package'";
385
386 try {
387 // PAGINATION: If limit is set, use subquery for package IDs
388 if (!empty($criteria['limit'])) {
389 $itemsPerPage = (int)$criteria['limit'];
390 $page = !empty($criteria['page']) ? (int)$criteria['page'] : 1;
391 $offset = ($page - 1) * $itemsPerPage;
392
393 // Get paginated package IDs
394 $idSql = "SELECT DISTINCT p.id FROM {$this->table} p
395 LEFT JOIN {$packageServicesTable} ps ON ps.packageId = p.id
396 LEFT JOIN {$servicesTable} s ON ps.serviceId = s.id
397 WHERE 1 = 1{$whereSql} {$order}
398 LIMIT {$itemsPerPage} OFFSET {$offset}";
399
400 $idStmt = $this->connection->prepare($idSql);
401 $idStmt->execute($params);
402 $packageIds = $idStmt->fetchAll(Statement::FETCH_COLUMN);
403
404 if (empty($packageIds)) {
405 return call_user_func([static::FACTORY, 'createCollection'], []);
406 }
407
408 // Prepare ID parameters
409 $inParams = [];
410 foreach ($packageIds as $idx => $id) {
411 $inParams[":p_id{$idx}"] = $id;
412 }
413 $inClause = implode(',', array_keys($inParams));
414 $params = array_merge($params, $inParams);
415
416 $whereSqlWithIds = $whereSql ? $whereSql . ' AND ' : ' AND ';
417 $whereSqlWithIds .= "p.id IN ($inClause)";
418
419 $sql = "{$selectSql} {$fromSql} WHERE 1 = 1 {$whereSqlWithIds} {$order}";
420 $statement = $this->connection->prepare($sql);
421 } else {
422 // Standard query without pagination
423 $sql = "{$selectSql} {$fromSql} WHERE 1 = 1 {$whereSql} {$order}";
424 $statement = $this->connection->prepare($sql);
425 }
426
427 $statement->execute($params);
428 $rows = $statement->fetchAll();
429 } catch (\Exception $e) {
430 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
431 }
432
433 return call_user_func([static::FACTORY, 'createCollection'], $rows);
434 }
435
436 /**
437 * @param $id
438 *
439 * @return Collection
440 * @throws QueryExecutionException
441 * @throws InvalidArgumentException
442 */
443 public function getById($id)
444 {
445 $params[':id'] = $id;
446
447 $servicesTable = ServicesTable::getTableName();
448
449 $usersTable = UsersTable::getTableName();
450
451 $locationsTable = LocationsTable::getTableName();
452
453 $packageServicesTable = PackagesServicesTable::getTableName();
454
455 $packageServicesProvidersTable = PackagesServicesProvidersTable::getTableName();
456
457 $packageServicesLocationsTable = PackagesServicesLocationsTable::getTableName();
458
459 $galleriesTable = GalleriesTable::getTableName();
460
461 try {
462 $statement = $this->connection->prepare(
463 "SELECT
464 p.id AS package_id,
465 p.name AS package_name,
466 p.description AS package_description,
467 p.color AS package_color,
468 p.price AS package_price,
469 p.status AS package_status,
470 p.pictureFullPath AS package_picture_full,
471 p.pictureThumbPath AS package_picture_thumb,
472 p.calculatedPrice AS package_calculated_price,
473 p.discount AS package_discount,
474 p.position AS package_position,
475 p.settings AS package_settings,
476 p.endDate AS package_endDate,
477 p.durationCount AS package_durationCount,
478 p.durationType AS package_durationType,
479 p.translations AS package_translations,
480 p.deposit AS package_deposit,
481 p.depositPayment AS package_depositPayment,
482 p.fullPayment AS package_fullPayment,
483 p.sharedCapacity AS package_sharedCapacity,
484 p.quantity AS package_quantity,
485 p.limitPerCustomer AS package_limitPerCustomer,
486
487 ps.id AS package_service_id,
488 ps.quantity AS package_service_quantity,
489 ps.minimumScheduled AS package_service_minimumScheduled,
490 ps.maximumScheduled AS package_service_maximumScheduled,
491 ps.allowProviderSelection AS package_service_allowProviderSelection,
492 ps.position AS package_service_position,
493
494 s.id AS service_id,
495 s.price AS service_price,
496 s.minCapacity AS service_minCapacity,
497 s.maxCapacity AS service_maxCapacity,
498 s.name AS service_name,
499 s.status AS service_status,
500 s.categoryId AS service_categoryId,
501 s.duration AS service_duration,
502 s.timeBefore AS service_timeBefore,
503 s.timeAfter AS service_timeAfter,
504 s.pictureFullPath AS service_picture_full,
505 s.pictureThumbPath AS service_picture_thumb,
506 s.show AS service_show,
507 s.color AS service_color,
508
509 l.id AS location_id,
510 l.name AS location_name,
511 l.address AS location_address,
512 l.phone AS location_phone,
513 l.latitude AS location_latitude,
514 l.longitude AS location_longitude,
515
516 pu.id AS provider_id,
517 pu.firstName AS provider_firstName,
518 pu.lastName AS provider_lastName,
519 pu.email AS provider_email,
520 pu.translations AS provider_translations,
521 pu.stripeConnect AS provider_stripeConnect,
522
523 g.id AS gallery_id,
524 g.pictureFullPath AS gallery_picture_full,
525 g.pictureThumbPath AS gallery_picture_thumb,
526 g.position AS gallery_position
527
528 FROM {$this->table} p
529 LEFT JOIN {$packageServicesTable} ps ON ps.packageId = p.id
530 LEFT JOIN {$servicesTable} s ON ps.serviceId = s.id
531 LEFT JOIN {$packageServicesProvidersTable} psp ON psp.packageServiceId = ps.id
532 LEFT JOIN {$packageServicesLocationsTable} psl ON psl.packageServiceId = ps.id
533 LEFT JOIN {$usersTable} pu ON pu.id = psp.userId
534 LEFT JOIN {$locationsTable} l ON l.id = psl.locationId
535 LEFT JOIN {$galleriesTable} g ON g.entityId = p.id AND g.entityType = 'package'
536 WHERE p.id = :id"
537 );
538
539 $statement->execute($params);
540
541 $rows = $statement->fetchAll();
542 } catch (\Exception $e) {
543 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
544 }
545
546 return call_user_func([static::FACTORY, 'createCollection'], $rows)->getItem($id);
547 }
548
549 public function getCount($criteria)
550 {
551 $params = [];
552 $where = [];
553
554 // Only add filters if criteria is not empty
555 if (!empty($criteria)) {
556 if (!empty($criteria['search'])) {
557 $terms = preg_split('/\s+/', trim($criteria['search']));
558 $termIndex = 0;
559
560 foreach ($terms as $term) {
561 $param = ":search{$termIndex}";
562 $params[$param] = "%{$term}%";
563
564 $where[] = "(
565 p.name LIKE {$param}
566 OR p.id LIKE {$param}
567 )";
568
569 $termIndex++;
570 }
571 }
572
573 if (!empty($criteria['services'])) {
574 $queryServices = [];
575 foreach ((array)$criteria['services'] as $index => $value) {
576 $param = ':service' . $index;
577 $queryServices[] = $param;
578 $params[$param] = $value;
579 }
580 $where[] = "s.id IN (" . implode(', ', $queryServices) . ")";
581 }
582 }
583
584 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
585
586 $packageServicesTable = PackagesServicesTable::getTableName();
587 $servicesTable = ServicesTable::getTableName();
588
589 try {
590 $statement = $this->connection->prepare(
591 "SELECT COUNT(DISTINCT p.id) AS count
592 FROM {$this->table} p
593 LEFT JOIN {$packageServicesTable} ps ON ps.packageId = p.id
594 LEFT JOIN {$servicesTable} s ON ps.serviceId = s.id
595 {$where}"
596 );
597
598 $statement->execute($params);
599
600 $row = $statement->fetch()['count'];
601 } catch (\Exception $e) {
602 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
603 }
604
605 return $row;
606 }
607 }
608