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 / PackageRepository.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
PackageRepository.php
610 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.phone AS provider_phone,
521 pu.countryPhoneIso AS provider_countryPhoneIso,
522 pu.translations AS provider_translations,
523 pu.stripeConnect AS provider_stripeConnect,
524
525 g.id AS gallery_id,
526 g.pictureFullPath AS gallery_picture_full,
527 g.pictureThumbPath AS gallery_picture_thumb,
528 g.position AS gallery_position
529
530 FROM {$this->table} p
531 LEFT JOIN {$packageServicesTable} ps ON ps.packageId = p.id
532 LEFT JOIN {$servicesTable} s ON ps.serviceId = s.id
533 LEFT JOIN {$packageServicesProvidersTable} psp ON psp.packageServiceId = ps.id
534 LEFT JOIN {$packageServicesLocationsTable} psl ON psl.packageServiceId = ps.id
535 LEFT JOIN {$usersTable} pu ON pu.id = psp.userId
536 LEFT JOIN {$locationsTable} l ON l.id = psl.locationId
537 LEFT JOIN {$galleriesTable} g ON g.entityId = p.id AND g.entityType = 'package'
538 WHERE p.id = :id"
539 );
540
541 $statement->execute($params);
542
543 $rows = $statement->fetchAll();
544 } catch (\Exception $e) {
545 throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
546 }
547
548 return call_user_func([static::FACTORY, 'createCollection'], $rows)->getItem($id);
549 }
550
551 public function getCount($criteria)
552 {
553 $params = [];
554 $where = [];
555
556 // Only add filters if criteria is not empty
557 if (!empty($criteria)) {
558 if (!empty($criteria['search'])) {
559 $terms = preg_split('/\s+/', trim($criteria['search']));
560 $termIndex = 0;
561
562 foreach ($terms as $term) {
563 $param = ":search{$termIndex}";
564 $params[$param] = "%{$term}%";
565
566 $where[] = "(
567 p.name LIKE {$param}
568 OR p.id LIKE {$param}
569 )";
570
571 $termIndex++;
572 }
573 }
574
575 if (!empty($criteria['services'])) {
576 $queryServices = [];
577 foreach ((array)$criteria['services'] as $index => $value) {
578 $param = ':service' . $index;
579 $queryServices[] = $param;
580 $params[$param] = $value;
581 }
582 $where[] = "s.id IN (" . implode(', ', $queryServices) . ")";
583 }
584 }
585
586 $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';
587
588 $packageServicesTable = PackagesServicesTable::getTableName();
589 $servicesTable = ServicesTable::getTableName();
590
591 try {
592 $statement = $this->connection->prepare(
593 "SELECT COUNT(DISTINCT p.id) AS count
594 FROM {$this->table} p
595 LEFT JOIN {$packageServicesTable} ps ON ps.packageId = p.id
596 LEFT JOIN {$servicesTable} s ON ps.serviceId = s.id
597 {$where}"
598 );
599
600 $statement->execute($params);
601
602 $row = $statement->fetch()['count'];
603 } catch (\Exception $e) {
604 throw new QueryExecutionException('Unable to get data from ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
605 }
606
607 return $row;
608 }
609 }
610