PluginProbe
Booking for Appointments and Events Calendar – Amelia / 2.4.2
Booking for Appointments and Events Calendar – Amelia v2.4.2
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 All 59 releases
ameliabooking / src / Application / Commands / Booking / Event / GetEventsCommandHandler.php
GetEventsCommandHandler.php
429 lines 17.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AmeliaBooking\Application\Commands\Booking\Event;
4
5 use AmeliaBooking\Application\Commands\CommandHandler;
6 use AmeliaBooking\Application\Commands\CommandResult;
7 use AmeliaBooking\Application\Common\Exceptions\AccessDeniedException;
8 use AmeliaBooking\Application\Services\Booking\EventApplicationService;
9 use AmeliaBooking\Application\Services\User\ProviderApplicationService;
10 use AmeliaBooking\Application\Services\User\UserApplicationService;
11 use AmeliaBooking\Domain\Collection\Collection;
12 use AmeliaBooking\Domain\Common\Exceptions\AuthorizationException;
13 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
14 use AmeliaBooking\Domain\Entity\Booking\Appointment\CustomerBooking;
15 use AmeliaBooking\Domain\Entity\Booking\Event\Event;
16 use AmeliaBooking\Domain\Entity\Booking\Event\EventPeriod;
17 use AmeliaBooking\Domain\Entity\Entities;
18 use AmeliaBooking\Domain\Entity\User\AbstractUser;
19 use AmeliaBooking\Domain\Factory\Booking\Event\EventPeriodFactory;
20 use AmeliaBooking\Domain\Services\Settings\SettingsService;
21 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
22 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\CustomerBookingRepository;
23 use AmeliaBooking\Infrastructure\Repository\Booking\Event\EventRepository;
24 use DateTimeZone;
25 use Exception;
26
27 /**
28 * Class GetEventsCommandHandler
29 *
30 * @package AmeliaBooking\Application\Commands\Booking\Event
31 */
32 class GetEventsCommandHandler extends CommandHandler
33 {
34 /**
35 * @param GetEventsCommand $command
36 *
37 * @return CommandResult
38 *
39 * @throws AccessDeniedException
40 * @throws InvalidArgumentException
41 * @throws QueryExecutionException
42 * @throws Exception
43 */
44 public function handle(GetEventsCommand $command)
45 {
46 $result = new CommandResult();
47
48 /** @var SettingsService $settingsDS */
49 $settingsDS = $this->container->get('domain.settings.service');
50 /** @var EventRepository $eventRepository */
51 $eventRepository = $this->container->get('domain.booking.event.repository');
52 /** @var UserApplicationService $userAS */
53 $userAS = $this->container->get('application.user.service');
54 /** @var EventApplicationService $eventAS */
55 $eventAS = $this->container->get('application.booking.event.service');
56 /** @var ProviderApplicationService $providerAS */
57 $providerAS = $this->container->get('application.user.provider.service');
58
59
60 $params = $command->getField('params');
61
62 /** @var AbstractUser|null $user */
63 $user = null;
64
65 // Cabinet requests (source=cabinet-provider/customer) use page for *pagination*,
66 // not to signal a frontend booking-form context. Without this guard, sending
67 // page=1 from a provider JWT makes $isFrontEnd=true, which skips the auth block,
68 // clears provider scoping, and adds show=1 — exposing all published events.
69 $isCabinetPage = $command->getPage() === 'cabinet';
70
71 $isFrontEnd = isset($params['page']) && empty($params['group']) && !$isCabinetPage;
72
73 $fetchBookings = !$isFrontEnd && (
74 !isset($params['bookings']) || filter_var($params['bookings'], FILTER_VALIDATE_BOOLEAN)
75 );
76
77 $isCalendarPage = $isFrontEnd && (int)$params['page'] === 0;
78
79 if (!$isFrontEnd) {
80 try {
81 $user = $command->getUserApplicationService()->authorization(
82 $isCabinetPage ? $command->getToken() : null,
83 $command->getCabinetType()
84 );
85 } catch (AuthorizationException $e) {
86 $result->setResult(CommandResult::RESULT_ERROR);
87 $result->setData(
88 [
89 'reauthorize' => true
90 ]
91 );
92
93 return $result;
94 }
95
96 if ($userAS->isAmeliaUser($user) && $userAS->isCustomer($user)) {
97 $params['customerId'] = $user->getId()->getValue();
98 }
99
100 if ($user && $user->getType() === AbstractUser::USER_ROLE_PROVIDER) {
101 $params['providers'] = [$user->getId()->getValue()];
102 }
103 }
104
105 if ($isFrontEnd && !empty($params['providers'])) {
106 $params['providers'] = array_values($params['providers']);
107 }
108
109 if (isset($params['dates'][0])) {
110 $params['dates'][0] ? $params['dates'][0] .= ' 00:00:00' : null;
111 }
112
113 if (isset($params['dates'][1])) {
114 $params['dates'][1] ? $params['dates'][1] .= ' 23:59:59' : null;
115 }
116
117 if ($isFrontEnd) {
118 $params['show'] = 1;
119
120 if (!empty($params['tag'])) {
121 $params['tag'] = str_replace('___', ' ', $params['tag']);
122 }
123 }
124
125 if (isset($params['range']) && $isFrontEnd) {
126 if ($params['range'] === 'future') {
127 $startDate = date('Y-m-d 00:00:00');
128
129 // Date filter is selected on the frontend,
130 // so if the provided date is in the past,
131 // we can return empty result right away
132 if (!empty($params['dates'][0]) && $params['dates'][0] < $startDate) {
133 $result->setResult(CommandResult::RESULT_SUCCESS);
134 $result->setData(
135 [
136 Entities::EVENTS => [],
137 'count' => 0,
138 'countTotal' => 0,
139 'customersNoShowCount' => []
140 ]
141 );
142 return $result;
143 }
144
145 // Date filter selected and date is in future or present.
146 // Intentionally discard any user-provided end date because a
147 // "future" widget has no upper bound.
148 if (!empty($params['dates'][0])) {
149 $params['dates'][1] = null;
150 }
151
152 // Date filter not selected
153 if (empty($params['dates'][0])) {
154 $params['dates'][0] = $startDate;
155 $params['dates'][1] = null;
156 }
157 }
158
159 if ($params['range'] === 'past') {
160 $endDate = date('Y-m-d 23:59:59', strtotime('-1 day'));
161
162 // Date filter is selected on the frontend,
163 // so if the provided date is in the future,
164 // we can return empty result right away
165 if (!empty($params['dates'][0]) && $params['dates'][0] > $endDate) {
166 $result->setResult(CommandResult::RESULT_SUCCESS);
167 $result->setData(
168 [
169 Entities::EVENTS => [],
170 'count' => 0,
171 'countTotal' => 0,
172 'customersNoShowCount' => []
173 ]
174 );
175 return $result;
176 }
177
178 // Date filter selected and date is in past
179 if (!empty($params['dates'][0])) {
180 $params['dates'][1] = $endDate;
181 }
182
183 // Date filter not selected
184 if (empty($params['dates'][0])) {
185 $params['dates'][0] = null;
186 $params['dates'][1] = $endDate;
187 }
188 }
189
190 if (is_array($params['range'])) {
191 $startDate = !empty($params['range'][0]) ? $params['range'][0] . ' 00:00:00' : null;
192 $endDate = !empty($params['range'][1]) ? $params['range'][1] . ' 23:59:59' : null;
193
194 // Date filter is selected on the frontend,
195 // so if the provided date is out of range,
196 // we can return empty result right away
197 if (!empty($params['dates'][0]) && ($params['dates'][0] < $startDate || $params['dates'][0] > $endDate)) {
198 $result->setResult(CommandResult::RESULT_SUCCESS);
199 $result->setData(
200 [
201 Entities::EVENTS => [],
202 'count' => 0,
203 'countTotal' => 0,
204 'customersNoShowCount' => []
205 ]
206 );
207
208 return $result;
209 }
210
211 // Date filter selected and date is in range
212 if (!empty($params['dates'][0])) {
213 $params['dates'][1] = $endDate;
214 }
215
216 // Date filter not selected
217 if (empty($params['dates'][0])) {
218 $params['dates'][0] = $startDate;
219 $params['dates'][1] = $endDate;
220 }
221 }
222 }
223
224 $criteria = [
225 'fetchEventsPeriods' => true,
226 'fetchEventsTickets' => true,
227 'fetchEventsTags' => $isFrontEnd,
228 'fetchEventsProviders' => true,
229 'fetchEventsOrganizer' => true,
230 'fetchEventsImages' => true,
231 'fetchBookings' => $fetchBookings,
232 'fetchBookingsTickets' => $fetchBookings,
233 'fetchBookingsCoupons' => $fetchBookings && $isCabinetPage,
234 'fetchBookingsPayments' => $fetchBookings && $isCabinetPage,
235 'fetchBookingsUsers' => $fetchBookings && $isCabinetPage,
236 'fetchOccupancy' => !$fetchBookings,
237 ];
238
239 /** @var Collection $events */
240 $events = $eventAS->getEventsByCriteria(
241 $params,
242 $criteria,
243 !empty($params['limit'])
244 ? $params['limit']
245 : ($isFrontEnd ? $settingsDS->getSetting('general', 'itemsPerPage') : 10)
246 );
247
248 $selectedEventIds = [];
249
250 if (!empty($params['idPopup']) && !$events->keyExists($params['idPopup'])) {
251 $selectedEventIds = [$params['idPopup']];
252 } elseif (!empty($params['ids'])) {
253 $missingIds = array_values(array_diff(array_map('intval', $params['ids']), $events->keys()));
254 if (!empty($missingIds)) {
255 $selectedEventIds = $missingIds;
256 }
257 }
258
259 /** @var Collection $requestedEvents */
260 $requestedEvents = !empty($selectedEventIds) ? $eventAS->getEventsByCriteria(
261 ['id' => $selectedEventIds, 'search' => !empty($params['search']) ? $params['search'] : null],
262 $criteria,
263 0
264 ) : new Collection();
265
266 $existingSeriesIds = [];
267 /** @var Event $existingEvent */
268 foreach ($events->getItems() as $existingEvent) {
269 $seriesId = $existingEvent->getParentId()
270 ? $existingEvent->getParentId()->getValue()
271 : $existingEvent->getId()->getValue();
272 $existingSeriesIds[$seriesId] = $existingEvent->getId()->getValue();
273 }
274
275 $replacedEventIds = [];
276
277 foreach ($requestedEvents->getItems() as $event) {
278 if ($events->keyExists($event->getId()->getValue())) {
279 continue;
280 }
281
282 $requestedSeriesId = $event->getParentId()
283 ? $event->getParentId()->getValue()
284 : $event->getId()->getValue();
285 if (!empty($existingSeriesIds[$requestedSeriesId]) && !empty($params['skipRecurring'])) {
286 $replacedEventIds[$event->getId()->getValue()] = $existingSeriesIds[$requestedSeriesId];
287 continue;
288 }
289
290 $events->prependItem($event, $event->getId()->getValue(), true);
291 }
292
293 $eventsArray = [];
294
295 $customersNoShowCountIds = [];
296
297 $noShowTagEnabled = $settingsDS->isFeatureEnabled('noShowTag');
298
299 /** @var Event $event */
300 foreach ($events->getItems() as $event) {
301 // this would affect paging on frontend, should be done in the database?
302 if ($isFrontEnd && !$event->getShow()->getValue()) {
303 continue;
304 }
305
306 if (
307 ($isFrontEnd && $settingsDS->getSetting('general', 'showClientTimeZone')) ||
308 $isCabinetPage || ($user && $user->getType() === AbstractUser::USER_ROLE_PROVIDER)
309 ) {
310 $timeZone = !empty($params['timeZone'])
311 ? $params['timeZone']
312 : ($user && $user->getType() === Entities::PROVIDER ? $providerAS->getTimeZone($user) : 'UTC');
313
314 /** @var EventPeriod $period */
315 foreach ($event->getPeriods()->getItems() as $period) {
316 $period->getPeriodStart()->getValue()->setTimezone(new DateTimeZone($timeZone));
317 $period->getPeriodEnd()->getValue()->setTimezone(new DateTimeZone($timeZone));
318 }
319 }
320
321 $eventsInfo = $eventAS->getEventInfo($event, $isFrontEnd);
322
323 if ($isFrontEnd) {
324 $event->setBookings(new Collection());
325
326 /** @var EventPeriod $eventPeriod */
327 foreach ($event->getPeriods()->getItems() as $key => $eventPeriod) {
328 /** @var EventPeriod $newEventPeriod **/
329 $newEventPeriod = EventPeriodFactory::create(
330 array_merge(
331 $eventPeriod->toArray(),
332 ['zoomMeeting' => null]
333 )
334 );
335
336 $event->getPeriods()->placeItem($newEventPeriod, $key, true);
337 }
338 }
339
340 $ameliaUserId = $userAS->isAmeliaUser($user) && $user->getId() ? $user->getId()->getValue() : null;
341
342 // Delete other bookings if user is customer
343 if ($userAS->isCustomer($user)) {
344 /** @var CustomerBooking $booking */
345 foreach ($event->getBookings()->getItems() as $bookingKey => $booking) {
346 if ($booking->getCustomerId()->getValue() !== $ameliaUserId) {
347 $event->getBookings()->deleteItem($bookingKey);
348 }
349 }
350 }
351
352 if (!$isFrontEnd && $userAS->isCustomer($user) && $event->getBookings()->length() === 0) {
353 continue;
354 }
355
356 /** @var CustomerBooking $booking */
357 foreach ($event->getBookings()->getItems() as $booking) {
358 if ($noShowTagEnabled) {
359 $customersNoShowCountIds[] = $booking->getCustomerId()->getValue();
360 }
361 }
362
363 $eventArray = $event->toArray();
364
365 $eventArray['staff'] = array_map(
366 function ($provider) use ($providerAS) {
367 return [
368 'id' => $provider['id'],
369 'firstName' => $provider['firstName'],
370 'lastName' => $provider['lastName'],
371 'picture' => $provider['pictureThumbPath'],
372 'badge' => $providerAS->getBadge($provider['badgeId'])
373 ];
374 },
375 $eventArray['providers']
376 );
377
378 // TODO - Redesign: Check if providers can be removed
379 // unset($eventArray['providers']);
380
381 if (!empty($eventArray['organizerId'])) {
382 $eventArray['organizer'] = [
383 'id' => $eventArray['organizerId'],
384 'firstName' => !empty($eventArray['organizer']) ? $eventArray['organizer']['firstName'] : '',
385 'lastName' => !empty($eventArray['organizer']) ? $eventArray['organizer']['lastName'] : '',
386 'picture' => !empty($eventArray['organizer']) ? $eventArray['organizer']['pictureThumbPath'] : '',
387 'badge' => !empty($eventArray['organizer']) ? $providerAS->getBadge($eventArray['organizer']['badgeId']) : null
388 ];
389 }
390
391 usort(
392 $eventArray['gallery'],
393 function ($picture1, $picture2) {
394 return $picture1['position'] <=> $picture2['position'];
395 }
396 );
397
398 $eventsArray[] = array_merge($eventArray, $eventsInfo);
399 }
400
401 $customersNoShowCount = [];
402
403 if ($noShowTagEnabled && $customersNoShowCountIds) {
404 /** @var CustomerBookingRepository $bookingRepository */
405 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
406
407 $customersNoShowCount = $bookingRepository->countByNoShowStatus($customersNoShowCountIds);
408 }
409
410 $eventsArray = apply_filters('amelia_get_events_filter', $eventsArray);
411
412 do_action('amelia_get_events', $eventsArray);
413
414 $result->setResult(CommandResult::RESULT_SUCCESS);
415 $result->setMessage('Successfully retrieved events');
416 $result->setData(
417 [
418 Entities::EVENTS => $eventsArray,
419 'count' => !$isCalendarPage && empty($params['skipCount']) ? (int)sizeof($eventRepository->getFilteredIds($params, 0)) : null,
420 'countTotal' => !$isCalendarPage && empty($params['skipCount']) ? (int)sizeof($eventRepository->getFilteredIds([], 0)) : null,
421 'customersNoShowCount' => $customersNoShowCount ? array_values($customersNoShowCount) : [],
422 'replacedEventIds' => !empty($replacedEventIds) ? $replacedEventIds : null,
423 ]
424 );
425
426 return $result;
427 }
428 }
429