RegistrationService::checkConfirmRegistration()   C
last analyzed

Complexity

Conditions 13
Paths 64

Size

Total Lines 51
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 35
nc 64
nop 2
dl 0
loc 51
rs 6.6166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Extension "sf_event_mgt" for TYPO3 CMS.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace DERHANSEN\SfEventMgt\Service;
13
14
use DateTime;
15
use DERHANSEN\SfEventMgt\Domain\Model\Event;
16
use DERHANSEN\SfEventMgt\Domain\Model\FrontendUser;
17
use DERHANSEN\SfEventMgt\Domain\Model\Registration;
18
use DERHANSEN\SfEventMgt\Domain\Repository\FrontendUserRepository;
19
use DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository;
20
use DERHANSEN\SfEventMgt\Event\AfterRegistrationMovedFromWaitlist;
0 ignored issues
show
Bug introduced by
The type DERHANSEN\SfEventMgt\Eve...rationMovedFromWaitlist was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use DERHANSEN\SfEventMgt\Event\ModifyCheckRegistrationSuccessEvent;
22
use DERHANSEN\SfEventMgt\Event\ModifyRegistrationPriceEvent;
23
use DERHANSEN\SfEventMgt\Payment\AbstractPayment;
24
use DERHANSEN\SfEventMgt\Security\HashScope;
25
use DERHANSEN\SfEventMgt\Utility\MessageType;
26
use DERHANSEN\SfEventMgt\Utility\RegistrationResult;
27
use Psr\EventDispatcher\EventDispatcherInterface;
28
use Psr\Http\Message\ServerRequestInterface;
29
use TYPO3\CMS\Core\Context\Context;
30
use TYPO3\CMS\Core\Crypto\HashService;
31
use TYPO3\CMS\Core\Database\Connection;
32
use TYPO3\CMS\Core\Database\ConnectionPool;
33
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
34
use TYPO3\CMS\Core\Http\PropagateResponseException;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Extbase\Mvc\RequestInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Extbase\Reflection\ObjectAccess was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
38
use TYPO3\CMS\Frontend\Controller\ErrorController;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Frontend\Controller\ErrorController was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
39
40
class RegistrationService
41
{
42
    public function __construct(
43
        protected readonly Context $context,
44
        protected readonly EventDispatcherInterface $eventDispatcher,
45
        protected readonly RegistrationRepository $registrationRepository,
46
        protected readonly FrontendUserRepository $frontendUserRepository,
47
        protected readonly HashService $hashService,
48
        protected readonly PaymentService $paymentService,
49
        protected readonly NotificationService $notificationService,
50
    ) {
51
    }
52
53
    /**
54
     * Duplicates the given registration (all public accessible properties) the
55
     * amount of times configured in amountOfRegistrations
56
     */
57
    public function createDependingRegistrations(Registration $registration): void
58
    {
59
        $registrations = $registration->getAmountOfRegistrations();
60
        for ($i = 1; $i <= $registrations - 1; $i++) {
61
            $newReg = GeneralUtility::makeInstance(Registration::class);
62
            $properties = ObjectAccess::getGettableProperties($registration);
63
            foreach ($properties as $propertyName => $propertyValue) {
64
                ObjectAccess::setProperty($newReg, $propertyName, $propertyValue);
65
            }
66
67
            /** @var Registration $newReg */
68
            $newReg->setMainRegistration($registration);
69
            $newReg->setAmountOfRegistrations(1);
70
            $newReg->setIgnoreNotifications(true);
71
            $this->registrationRepository->add($newReg);
72
        }
73
    }
74
75
    /**
76
     * Confirms all depending registrations based on the given main registration
77
     */
78
    public function confirmDependingRegistrations(Registration $registration): void
79
    {
80
        $registrations = $this->registrationRepository->findBy(['mainRegistration' => $registration]);
81
        foreach ($registrations as $foundRegistration) {
82
            /** @var Registration $foundRegistration */
83
            $foundRegistration->setConfirmed(true);
84
            $this->registrationRepository->update($foundRegistration);
85
        }
86
    }
87
88
    /**
89
     * Checks if the registration can be confirmed and returns an array of variables
90
     */
91
    public function checkConfirmRegistration(int $regUid, string $hmac): array
92
    {
93
        /* @var $registration Registration */
94
        $registration = null;
95
        $failed = false;
96
        $messageKey = 'event.message.confirmation_successful';
97
        $titleKey = 'confirmRegistration.title.successful';
98
99
        $isValidHmac = $this->hashService->validateHmac('reg-' . $regUid, HashScope::RegistrationUid->value, $hmac);
100
        if (!$isValidHmac) {
101
            $failed = true;
102
            $messageKey = 'event.message.confirmation_failed_wrong_hmac';
103
            $titleKey = 'confirmRegistration.title.failed';
104
        } else {
105
            $registration = $this->registrationRepository->findByUid($regUid);
106
        }
107
108
        if (!$failed && is_null($registration)) {
109
            $failed = true;
110
            $messageKey = 'event.message.confirmation_failed_registration_not_found';
111
            $titleKey = 'confirmRegistration.title.failed';
112
        }
113
114
        if (!$failed && !$registration->getEvent()) {
115
            $failed = true;
116
            $messageKey = 'event.message.confirmation_failed_registration_event_not_found';
117
            $titleKey = 'confirmRegistration.title.failed';
118
        }
119
120
        if (!$failed && !$registration->getConfirmed() && $registration->getConfirmationUntil() < new DateTime()) {
121
            $failed = true;
122
            $messageKey = 'event.message.confirmation_failed_confirmation_until_expired';
123
            $titleKey = 'confirmRegistration.title.failed';
124
        }
125
126
        if (!$failed && $registration->getConfirmed() === true) {
127
            $failed = true;
128
            $messageKey = 'event.message.confirmation_failed_already_confirmed';
129
            $titleKey = 'confirmRegistration.title.failed';
130
        }
131
132
        if (!$failed && $registration->getWaitlist()) {
133
            $messageKey = 'event.message.confirmation_waitlist_successful';
134
            $titleKey = 'confirmRegistrationWaitlist.title.successful';
135
        }
136
137
        return [
138
            $failed,
139
            $registration,
140
            $messageKey,
141
            $titleKey,
142
        ];
143
    }
144
145
    /**
146
     * Cancels all depending registrations based on the given main registration
147
     */
148
    public function cancelDependingRegistrations(Registration $registration): void
149
    {
150
        $registrations = $this->registrationRepository->findBy(['mainRegistration' => $registration]);
151
        foreach ($registrations as $foundRegistration) {
152
            $this->registrationRepository->remove($foundRegistration);
153
        }
154
    }
155
156
    /**
157
     * Checks if the registration can be cancelled and returns an array of variables
158
     */
159
    public function checkCancelRegistration(int $regUid, string $hmac): array
160
    {
161
        /* @var $registration Registration|null */
162
        $registration = null;
163
        $failed = false;
164
        $messageKey = 'event.message.cancel_successful';
165
        $titleKey = 'cancelRegistration.title.successful';
166
167
        $isValidHmac = $this->hashService->validateHmac('reg-' . $regUid, HashScope::RegistrationUid->value, $hmac);
168
        if (!$isValidHmac) {
169
            $failed = true;
170
            $messageKey = 'event.message.cancel_failed_wrong_hmac';
171
            $titleKey = 'cancelRegistration.title.failed';
172
        } else {
173
            $registration = $this->registrationRepository->findByUid($regUid);
174
        }
175
176
        if (!$failed && is_null($registration)) {
177
            $failed = true;
178
            $messageKey = 'event.message.cancel_failed_registration_not_found_or_cancelled';
179
            $titleKey = 'cancelRegistration.title.failed';
180
        }
181
182
        if (!$failed && !$registration->getEvent()) {
183
            $failed = true;
184
            $messageKey = 'event.message.cancel_failed_event_not_found';
185
            $titleKey = 'cancelRegistration.title.failed';
186
        }
187
188
        if (!$failed && $registration->getEvent()->getEnableCancel() === false) {
189
            $failed = true;
190
            $messageKey = 'event.message.cancel_failed_cancel_disabled';
191
            $titleKey = 'cancelRegistration.title.failed';
192
        }
193
194
        if (!$failed && $registration->getEvent()->getCancelDeadline() !== null
195
            && $registration->getEvent()->getCancelDeadline() < new DateTime()
196
        ) {
197
            $failed = true;
198
            $messageKey = 'event.message.cancel_failed_deadline_expired';
199
            $titleKey = 'cancelRegistration.title.failed';
200
        }
201
202
        if (!$failed && $registration->getEvent()->getStartdate() < new DateTime()) {
203
            $failed = true;
204
            $messageKey = 'event.message.cancel_failed_event_started';
205
            $titleKey = 'cancelRegistration.title.failed';
206
        }
207
208
        return [
209
            $failed,
210
            $registration,
211
            $messageKey,
212
            $titleKey,
213
        ];
214
    }
215
216
    /**
217
     * Returns the current frontend user object if available
218
     */
219
    public function getCurrentFeUserObject(): ?FrontendUser
220
    {
221
        $user = null;
222
223
        $userUid = $this->context->getPropertyFromAspect('frontend.user', 'id');
224
        if ($userUid > 0) {
225
            /** @var FrontendUser $user */
226
            $user = $this->frontendUserRepository->findByUid($userUid);
227
        }
228
229
        return $user;
230
    }
231
232
    /**
233
     * Checks, if the registration can successfully be created.
234
     */
235
    public function checkRegistrationSuccess(Event $event, Registration $registration): array
236
    {
237
        $result = RegistrationResult::REGISTRATION_SUCCESSFUL;
238
        $success = true;
239
        if ($event->getEnableRegistration() === false) {
240
            $success = false;
241
            $result = RegistrationResult::REGISTRATION_NOT_ENABLED;
242
        } elseif ($event->getRegistrationDeadline() != null && $event->getRegistrationDeadline() < new DateTime()) {
243
            $success = false;
244
            $result = RegistrationResult::REGISTRATION_FAILED_DEADLINE_EXPIRED;
245
        } elseif (!$event->getAllowRegistrationUntilEnddate() && $event->getStartdate() < new DateTime()) {
246
            $success = false;
247
            $result = RegistrationResult::REGISTRATION_FAILED_EVENT_EXPIRED;
248
        } elseif ($event->getAllowRegistrationUntilEnddate() && $event->getEnddate() && $event->getEnddate() < new DateTime()) {
249
            $success = false;
250
            $result = RegistrationResult::REGISTRATION_FAILED_EVENT_ENDED;
251
        } elseif ($event->getRegistrations()->count() >= $event->getMaxParticipants()
252
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
253
        ) {
254
            $success = false;
255
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_PARTICIPANTS;
256
        } elseif ($event->getFreePlaces() < $registration->getAmountOfRegistrations()
257
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
258
        ) {
259
            $success = false;
260
            $result = RegistrationResult::REGISTRATION_FAILED_NOT_ENOUGH_FREE_PLACES;
261
        } elseif ($event->getMaxRegistrationsPerUser() < $registration->getAmountOfRegistrations()) {
262
            $success = false;
263
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_AMOUNT_REGISTRATIONS_EXCEEDED;
264
        } elseif ($event->getUniqueEmailCheck() &&
265
            $this->emailNotUnique($event, $registration->getEmail())
266
        ) {
267
            $success = false;
268
            $result = RegistrationResult::REGISTRATION_FAILED_EMAIL_NOT_UNIQUE;
269
        } elseif ($event->getRegistrations()->count() >= $event->getMaxParticipants()
270
            && $event->getMaxParticipants() > 0 && $event->getEnableWaitlist()
271
        ) {
272
            $result = RegistrationResult::REGISTRATION_SUCCESSFUL_WAITLIST;
273
        }
274
275
        $modifyCheckRegistrationSuccessEvent = new ModifyCheckRegistrationSuccessEvent(
276
            $success,
277
            $result,
278
            $registration
279
        );
280
        $this->eventDispatcher->dispatch($modifyCheckRegistrationSuccessEvent);
281
282
        return [$modifyCheckRegistrationSuccessEvent->getSuccess(), $modifyCheckRegistrationSuccessEvent->getResult()];
283
    }
284
285
    /**
286
     * Returns if the given email is registered to the given event
287
     */
288
    protected function emailNotUnique(Event $event, string $email): bool
289
    {
290
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
291
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
292
        $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
293
        $registrations = $queryBuilder->count('email')
294
            ->from('tx_sfeventmgt_domain_model_registration')
295
            ->where(
296
                $queryBuilder->expr()->eq(
297
                    'event',
298
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
299
                ),
300
                $queryBuilder->expr()->eq(
301
                    'email',
302
                    $queryBuilder->createNamedParameter($email, Connection::PARAM_STR)
303
                )
304
            )
305
            ->executeQuery()
306
            ->fetchOne();
307
308
        return $registrations >= 1;
309
    }
310
311
    /**
312
     * Returns, if payment redirect for the payment method is enabled
313
     */
314
    public function redirectPaymentEnabled(Registration $registration): bool
315
    {
316
        if ($registration->getEvent()->getEnablePayment() === false) {
317
            return false;
318
        }
319
320
        /** @var AbstractPayment|null $paymentInstance */
321
        $paymentInstance = $this->paymentService->getPaymentInstance($registration->getPaymentmethod());
322
323
        return $paymentInstance !== null && $paymentInstance->isRedirectEnabled();
324
    }
325
326
    /**
327
     * Returns if the given amount of registrations for the event will be registrations for the waitlist
328
     * (depending on the total amount of registrations and free places)
329
     */
330
    public function isWaitlistRegistration(Event $event, int $amountOfRegistrations): bool
331
    {
332
        if ($event->getMaxParticipants() === 0 || !$event->getEnableWaitlist()) {
333
            return false;
334
        }
335
336
        $result = false;
337
        if (($event->getFreePlaces() > 0 && $event->getFreePlaces() < $amountOfRegistrations)
338
            || $event->getFreePlaces() <= 0) {
339
            $result = true;
340
        }
341
342
        return $result;
343
    }
344
345
    /**
346
     * Handles the process of moving registration up from the waitlist.
347
     */
348
    public function moveUpWaitlistRegistrations(RequestInterface $request, Event $event, array $settings): void
349
    {
350
        // Early return if move up not enabled, no registrations on waitlist or no free places left
351
        if (!$event->getEnableWaitlistMoveup() || $event->getRegistrationsWaitlist()->count() === 0 ||
352
            $event->getFreePlaces() <= 0
353
        ) {
354
            return;
355
        }
356
357
        $keepMainRegistrationDependency = $settings['waitlist']['moveUp']['keepMainRegistrationDependency'] ?? false;
358
        $freePlaces = $event->getFreePlaces();
359
        $moveupRegistrations = $this->registrationRepository->findWaitlistMoveUpRegistrations($event);
360
361
        /** @var Registration $registration */
362
        foreach ($moveupRegistrations as $registration) {
363
            $registration->setWaitlist(false);
364
            $registration->setIgnoreNotifications(false);
365
366
            if (!(bool)$keepMainRegistrationDependency) {
367
                $registration->_setProperty('mainRegistration', null);
368
            }
369
370
            $this->registrationRepository->update($registration);
371
372
            // Send messages to user and admin
373
            $this->notificationService->sendUserMessage(
374
                $request,
375
                $event,
376
                $registration,
377
                $settings,
378
                MessageType::REGISTRATION_WAITLIST_MOVE_UP
379
            );
380
            $this->notificationService->sendAdminMessage(
381
                $request,
382
                $registration->getEvent(),
383
                $registration,
384
                $settings,
385
                MessageType::REGISTRATION_WAITLIST_MOVE_UP
386
            );
387
388
            $this->eventDispatcher->dispatch(new AfterRegistrationMovedFromWaitlist($registration, $this, $request));
389
390
            $freePlaces--;
391
            if ($freePlaces === 0) {
392
                break;
393
            }
394
        }
395
    }
396
397
    /**
398
     * Checks, if the given registration belongs to the current logged in frontend user. If not, a
399
     * page not found response is thrown.
400
     */
401
    public function checkRegistrationAccess(ServerRequestInterface $request, Registration $registration): void
402
    {
403
        $isLoggedIn = $this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn');
404
        $userUid = $this->context->getPropertyFromAspect('frontend.user', 'id');
405
406
        if (!$isLoggedIn ||
407
            !$registration->getFeUser() ||
408
            $userUid !== (int)$registration->getFeUser()->getUid()) {
409
            $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
410
                $request,
411
                'Registration not found.'
412
            );
413
            throw new PropagateResponseException($response, 1671627320);
414
        }
415
    }
416
417
    /**
418
     * Evaluates and returns the price for the given registration to the given event.
419
     */
420
    public function evaluateRegistrationPrice(
421
        Event $event,
422
        Registration $registration,
423
        ServerRequestInterface $request
424
    ): float {
425
        $price = $event->getPrice();
426
        if ($registration->getPriceOption()) {
427
            $price = $registration->getPriceOption()->getPrice();
428
        }
429
430
        $modifyRegistrationPriceEvent = new ModifyRegistrationPriceEvent($price, $event, $registration, $request);
431
        $this->eventDispatcher->dispatch($modifyRegistrationPriceEvent);
432
433
        return $modifyRegistrationPriceEvent->getPrice();
434
    }
435
}
436