Completed
Push — master ( 2700b4...4ccb05 )
by Torben
04:03 queued 02:37
created

updateEventRegistrationCounters()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 0
cts 0
cp 0
rs 9.52
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
namespace DERHANSEN\SfEventMgt\Service;
3
4
/*
5
 * This file is part of the Extension "sf_event_mgt" for TYPO3 CMS.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.txt file that was distributed with this source code.
9
 */
10
11
use DERHANSEN\SfEventMgt\Domain\Model\Event;
12
use DERHANSEN\SfEventMgt\Domain\Model\Registration;
13
use DERHANSEN\SfEventMgt\Payment\AbstractPayment;
14
use DERHANSEN\SfEventMgt\Utility\RegistrationResult;
15
use TYPO3\CMS\Core\Database\Connection;
16
use TYPO3\CMS\Core\Database\ConnectionPool;
17
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
20
21
/**
22
 * RegistrationService
23
 *
24
 * @author Torben Hansen <[email protected]>
25
 */
26
class RegistrationService
27
{
28
    /**
29
     * The object manager
30
     *
31
     * @var \TYPO3\CMS\Extbase\Object\ObjectManager
32
     * */
33
    protected $objectManager;
34
35
    /**
36
     * RegistrationRepository
37
     *
38
     * @var \DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository
39
     * */
40
    protected $registrationRepository;
41
42
    /**
43
     * FrontendUserRepository
44
     *
45
     * @var \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository
46
     * */
47
    protected $frontendUserRepository;
48
49
    /**
50
     * Hash Service
51
     *
52
     * @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
53
     * */
54
    protected $hashService;
55
56
    /**
57
     * Payment Service
58
     *
59
     * @var \DERHANSEN\SfEventMgt\Service\PaymentService
60
     * */
61
    protected $paymentService;
62
63
    /**
64
     * DI for $frontendUserRepository
65
     *
66
     * @param \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository $frontendUserRepository
67
     */
68
    public function injectFrontendUserRepository(
69
        \TYPO3\CMS\Extbase\Domain\Repository\FrontendUserRepository $frontendUserRepository
70
    ) {
71
        $this->frontendUserRepository = $frontendUserRepository;
72
    }
73
74
    /**
75
     * DI for $hashService
76
     *
77
     * @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
78 4
     */
79
    public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService)
80 4
    {
81 4
        $this->hashService = $hashService;
82 4
    }
83
84 4
    /**
85 2
     * DI for $objectManager
86 2
     *
87 2
     * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
88 2
     */
89
    public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
90 4
    {
91 4
        $this->objectManager = $objectManager;
92 4
    }
93
94
    /**
95
     * DI for $paymentService
96
     *
97
     * @param \DERHANSEN\SfEventMgt\Service\PaymentService $paymentService
98
     */
99
    public function injectPaymentService(\DERHANSEN\SfEventMgt\Service\PaymentService $paymentService)
100
    {
101
        $this->paymentService = $paymentService;
102 2
    }
103
104 2
    /**
105 2
     * DI for $registrationRepository
106
     *
107 2
     * @param \DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository $registrationRepository
108 2
     */
109 2
    public function injectRegistrationRepository(
110 2
        \DERHANSEN\SfEventMgt\Domain\Repository\RegistrationRepository $registrationRepository
111 2
    ) {
112 2
        $this->registrationRepository = $registrationRepository;
113 2
    }
114 2
115 2
    /**
116 2
     * Duplicates (all public accessable properties) the given registration the
117 2
     * amount of times configured in amountOfRegistrations
118
     *
119
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Registration $registration Registration
120
     *
121
     * @return void
122
     */
123
    public function createDependingRegistrations($registration)
124
    {
125
        $registrations = $registration->getAmountOfRegistrations();
126 2
        for ($i = 1; $i <= $registrations - 1; $i++) {
127
            /** @var \DERHANSEN\SfEventMgt\Domain\Model\Registration $newReg */
128 2
            $newReg = $this->objectManager->get(Registration::class);
129 2
            $properties = ObjectAccess::getGettableProperties($registration);
130
            foreach ($properties as $propertyName => $propertyValue) {
131 2
                ObjectAccess::setProperty($newReg, $propertyName, $propertyValue);
132 2
            }
133 2
            $newReg->setMainRegistration($registration);
134 2
            $newReg->setAmountOfRegistrations(1);
135
            $newReg->setIgnoreNotifications(true);
136
            $this->registrationRepository->add($newReg);
137
        }
138
    }
139
140
    /**
141
     * Confirms all depending registrations based on the given main registration
142
     *
143
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Registration $registration Registration
144 8
     *
145
     * @return void
146
     */
147 8
    public function confirmDependingRegistrations($registration)
148 8
    {
149 8
        $registrations = $this->registrationRepository->findByMainRegistration($registration);
0 ignored issues
show
Documentation Bug introduced by
The method findByMainRegistration does not exist on object<DERHANSEN\SfEvent...RegistrationRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
150 8
        foreach ($registrations as $foundRegistration) {
151
            /** @var \DERHANSEN\SfEventMgt\Domain\Model\Registration $foundRegistration */
152 8
            $foundRegistration->setConfirmed(true);
153 2
            $this->registrationRepository->update($foundRegistration);
154 2
        }
155 2
    }
156 2
157 6
    /**
158
     * Checks if the registration can be confirmed and returns an array of variables
159
     *
160 8
     * @param int $reguid UID of registration
161 2
     * @param string $hmac HMAC for parameters
162 2
     *
163 2
     * @return array
164 2
     */
165
    public function checkConfirmRegistration($reguid, $hmac)
166 8
    {
167 2
        /* @var $registration Registration */
168 2
        $registration = null;
169 2
        $failed = false;
170 2
        $messageKey = 'event.message.confirmation_successful';
171
        $titleKey = 'confirmRegistration.title.successful';
172 8
173 2
        if (!$this->hashService->validateHmac('reg-' . $reguid, $hmac)) {
174 2
            $failed = true;
175 2
            $messageKey = 'event.message.confirmation_failed_wrong_hmac';
176 2
            $titleKey = 'confirmRegistration.title.failed';
177
        } else {
178 8
            $registration = $this->registrationRepository->findByUid($reguid);
179
        }
180
181
        if (!$failed && is_null($registration)) {
182
            $failed = true;
183
            $messageKey = 'event.message.confirmation_failed_registration_not_found';
184 8
            $titleKey = 'confirmRegistration.title.failed';
185 8
        }
186 8
187
        if (!$failed && $registration->getConfirmationUntil() < new \DateTime()) {
188 8
            $failed = true;
189
            $messageKey = 'event.message.confirmation_failed_confirmation_until_expired';
190
            $titleKey = 'confirmRegistration.title.failed';
191
        }
192
193
        if (!$failed && $registration->getConfirmed() === true) {
194
            $failed = true;
195
            $messageKey = 'event.message.confirmation_failed_already_confirmed';
196
            $titleKey = 'confirmRegistration.title.failed';
197
        }
198 2
199
        if (!$failed && $registration->getWaitlist()) {
200 2
            $messageKey = 'event.message.confirmation_waitlist_successful';
201 2
            $titleKey = 'confirmRegistrationWaitlist.title.successful';
202 2
        }
203 2
204 2
        return [
205
            $failed,
206
            $registration,
207
            $messageKey,
208
            $titleKey
209
        ];
210
    }
211
212
    /**
213
     * Cancels all depending registrations based on the given main registration
214 8
     *
215
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Registration $registration Registration
216
     *
217 8
     * @return void
218 8
     */
219 8
    public function cancelDependingRegistrations($registration)
220 8
    {
221
        $registrations = $this->registrationRepository->findByMainRegistration($registration);
0 ignored issues
show
Documentation Bug introduced by
The method findByMainRegistration does not exist on object<DERHANSEN\SfEvent...RegistrationRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
222 8
        foreach ($registrations as $foundRegistration) {
223 2
            $this->registrationRepository->remove($foundRegistration);
224 2
        }
225 2
    }
226 2
227 6
    /**
228
     * Checks if the registration can be cancelled and returns an array of variables
229
     *
230 8
     * @param int $reguid UID of registration
231 2
     * @param string $hmac HMAC for parameters
232 2
     *
233 2
     * @return array
234 2
     */
235
    public function checkCancelRegistration($reguid, $hmac)
236 8
    {
237 2
        /* @var $registration Registration */
238 2
        $registration = null;
239 2
        $failed = false;
240 2
        $messageKey = 'event.message.cancel_successful';
241
        $titleKey = 'cancelRegistration.title.successful';
242 8
243 8
        if (!$this->hashService->validateHmac('reg-' . $reguid, $hmac)) {
244 8
            $failed = true;
245 2
            $messageKey = 'event.message.cancel_failed_wrong_hmac';
246 2
            $titleKey = 'cancelRegistration.title.failed';
247 2
        } else {
248 2
            $registration = $this->registrationRepository->findByUid($reguid);
249
        }
250
251 8
        if (!$failed && is_null($registration)) {
252 8
            $failed = true;
253 8
            $messageKey = 'event.message.cancel_failed_registration_not_found_or_cancelled';
254
            $titleKey = 'cancelRegistration.title.failed';
255 8
        }
256
257
        if (!$failed && $registration->getEvent()->getEnableCancel() === false) {
258
            $failed = true;
259
            $messageKey = 'event.message.confirmation_failed_cancel_disabled';
260
            $titleKey = 'cancelRegistration.title.failed';
261
        }
262
263 10
        if (!$failed && $registration->getEvent()->getCancelDeadline() > 0
264
            && $registration->getEvent()->getCancelDeadline() < new \DateTime()
265 10
        ) {
266 2
            $failed = true;
267
            $messageKey = 'event.message.cancel_failed_deadline_expired';
268 8
            $titleKey = 'cancelRegistration.title.failed';
269
        }
270
271
        if (!$failed && $registration->getEvent()->getStartdate() < new \DateTime()) {
272
            $failed = true;
273
            $messageKey = 'event.message.cancel_failed_event_started';
274
            $titleKey = 'cancelRegistration.title.failed';
275
        }
276
277
        return [
278
            $failed,
279
            $registration,
280
            $messageKey,
281
            $titleKey
282 38
        ];
283
    }
284 38
285 38
    /**
286 4
     * Returns the current frontend user object if available
287 4
     *
288 38
     * @return mixed \TYPO3\CMS\Extbase\Domain\Model\FrontendUser|null
289 4
     */
290 4
    public function getCurrentFeUserObject()
291 34
    {
292 4
        if (isset($GLOBALS['TSFE']->fe_user->user['uid'])) {
293 4
            return $this->frontendUserRepository->findByUid($GLOBALS['TSFE']->fe_user->user['uid']);
294 30
        }
295 26
296 26
        return null;
297 4
    }
298 4
299 26
    /**
300 22
     * Checks, if the registration can successfully be created. Note, that
301 22
     * $result is passed by reference!
302 4
     *
303 4
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Event $event Event
304 22
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Registration $registration Registration
305 4
     * @param int $result Result
306 4
     *
307 18
     * @return array
308 4
     */
309 14
    public function checkRegistrationSuccess($event, $registration, $result)
310 4
    {
311 4
        $success = true;
312 14
        if ($event->getEnableRegistration() === false) {
313 10
            $success = false;
314 10
            $result = RegistrationResult::REGISTRATION_NOT_ENABLED;
315 2
        } elseif ($event->getRegistrationDeadline() != null && $event->getRegistrationDeadline() < new \DateTime()) {
316 2
            $success = false;
317 38
            $result = RegistrationResult::REGISTRATION_FAILED_DEADLINE_EXPIRED;
318
        } elseif ($event->getStartdate() < new \DateTime()) {
319
            $success = false;
320
            $result = RegistrationResult::REGISTRATION_FAILED_EVENT_EXPIRED;
321
        } elseif ($event->getRegistration()->count() >= $event->getMaxParticipants()
322
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
323
        ) {
324
            $success = false;
325
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_PARTICIPANTS;
326
        } elseif ($event->getFreePlaces() < $registration->getAmountOfRegistrations()
327 4
            && $event->getMaxParticipants() > 0 && !$event->getEnableWaitlist()
328
        ) {
329 4
            $success = false;
330 4
            $result = RegistrationResult::REGISTRATION_FAILED_NOT_ENOUGH_FREE_PLACES;
331
        } elseif ($event->getMaxRegistrationsPerUser() < $registration->getAmountOfRegistrations()) {
332
            $success = false;
333
            $result = RegistrationResult::REGISTRATION_FAILED_MAX_AMOUNT_REGISTRATIONS_EXCEEDED;
334
        } elseif ($event->getUniqueEmailCheck() &&
335
            $this->emailNotUnique($event, $registration->getEmail())
336
        ) {
337
            $success = false;
338
            $result = RegistrationResult::REGISTRATION_FAILED_EMAIL_NOT_UNIQUE;
339 4
        } elseif ($event->getRegistration()->count() >= $event->getMaxParticipants()
340
            && $event->getMaxParticipants() > 0 && $event->getEnableWaitlist()
341 4
        ) {
342 2
            $result = RegistrationResult::REGISTRATION_SUCCESSFUL_WAITLIST;
343
        }
344
345
        return [$success, $result];
346 2
    }
347 2
348 2
    /**
349
     * Returns if the given e-mail is registered to the given event
350
     *
351
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Event $event
352
     * @param string $email
353
     * @return bool
354
     */
355
    protected function emailNotUnique($event, $email)
356
    {
357
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
358
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
359
        $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
360
        $registrations = $queryBuilder->count('email')
361
            ->from('tx_sfeventmgt_domain_model_registration')
362 14
            ->where(
363
                $queryBuilder->expr()->eq(
364 14
                    'event',
365 6
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
366
                ),
367
                $queryBuilder->expr()->eq(
368 8
                    'email',
369 8
                    $queryBuilder->createNamedParameter($email, Connection::PARAM_STR)
370 2
                )
371 8
            )
372 4
            ->execute()
373 4
            ->fetchColumn();
374 8
375
        return $registrations >= 1;
376
    }
377
378
    /**
379
     * Returns, if payment redirect for the payment method is enabled
380
     *
381
     * @param Registration $registration
382
     * @return bool
383
     */
384
    public function redirectPaymentEnabled($registration)
385
    {
386
        if ($registration->getEvent()->getEnablePayment() === false) {
387
            return false;
388
        }
389
390
        /** @var AbstractPayment $paymentInstance */
391
        $paymentInstance = $this->paymentService->getPaymentInstance($registration->getPaymentmethod());
392
        if ($paymentInstance !== null && $paymentInstance->isRedirectEnabled()) {
393
            return true;
394
        }
395
396
        return false;
397
    }
398
399
    /**
400
     * Returns if the given amount of registrations for the event will be registrations for the waitlist
401
     * (depending on the total amount of registrations and free places)
402
     *
403
     * @param \DERHANSEN\SfEventMgt\Domain\Model\Event $event
404
     * @param int $amountOfRegistrations
405
     * @return bool
406
     */
407
    public function isWaitlistRegistration($event, $amountOfRegistrations)
408
    {
409
        if ($event->getMaxParticipants() === 0 || !$event->getEnableWaitlist()) {
410
            return false;
411
        }
412
413
        $result = false;
414
        if (($event->getFreePlaces() > 0 && $event->getFreePlaces() < $amountOfRegistrations)
415
            || $event->getFreePlaces() <= 0) {
416
            $result = true;
417
        }
418
419
        return $result;
420
    }
421
422
    /**
423
     * Fixes the event uid of a registration if the event has been saved as a child of a translated event.
424
     *
425
     * Since TYPO3 9.5 (#82363), registrations for events are saved to the translated event record
426
     *
427
     * Example:
428
     *
429
     * When a registration is saved for a translated event, the registration $registration->setEvent($event) will
430
     * now save the UID of the translated event instead of the uid of the event in default language.
431
     *
432
     * This behavior breaks limitations on events (e.g. max participants). Therefore, the registration must always
433
     * be related to the default event language (Extbase behavior before TYPO3 9.5)
434
     *
435
     * @param Registration $registration
436
     * @param Event $event
437
     * @return void
438
     */
439
    public function fixRegistrationEvent(Registration $registration, Event $event)
440
    {
441
        // Early return when event is in default language
442
        if ((int)$event->_getProperty('_languageUid') === 0) {
443
            return;
444
        }
445
        $this->updateRegistrationEventUid($registration, $event);
446
        $this->updateEventRegistrationCounters($event);
447
    }
448
449
    /**
450
     * Sets the "event" field of the given registration to the uid of the given event
451
     *
452
     * @param Registration $registration
453
     * @param Event $event
454
     * @return void
455
     */
456
    protected function updateRegistrationEventUid(Registration $registration, Event $event)
457
    {
458
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
459
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
460
        $queryBuilder->update('tx_sfeventmgt_domain_model_registration')
461
            ->set('event', $event->getUid())
462
            ->where(
463
                $queryBuilder->expr()->eq(
464
                    'uid',
465
                    $queryBuilder->createNamedParameter($registration->getUid(), Connection::PARAM_INT)
466
                )
467
            )
468
            ->execute();
469
    }
470
471
    /**
472
     * Updates registration/waitlist registration counters for the given event
473
     *
474
     * @param Event $event
475
     * @return void
476
     */
477
    protected function updateEventRegistrationCounters(Event $event)
478
    {
479
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
480
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_event');
481
482
        $countRegistrations = $this->getEventRegistrationCount($event, 0);
483
        $countRegistrationsWaitlist = $this->getEventRegistrationCount($event, 1);
484
485
        $queryBuilder->update('tx_sfeventmgt_domain_model_event')
486
            ->set('registration', $countRegistrations)
487
            ->set('registration_waitlist', $countRegistrationsWaitlist)
488
            ->where(
489
                $queryBuilder->expr()->eq(
490
                    'uid',
491
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
492
                )
493
            )
494
            ->orWhere(
495
                $queryBuilder->expr()->eq(
496
                    'l10n_parent',
497
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
498
                )
499
            )
500
            ->execute();
501
    }
502
503
    /**
504
     * Returns the total amount of registrations/waitlist registrations for an event
505
     *
506
     * @param Event $event
507
     * @param int $waitlist
508
     * @return mixed
509
     */
510
    protected function getEventRegistrationCount(Event $event, int $waitlist = 0)
511
    {
512
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
513
            ->getQueryBuilderForTable('tx_sfeventmgt_domain_model_registration');
514
        $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
515
516
        return $queryBuilder->count('uid')
517
            ->from('tx_sfeventmgt_domain_model_registration')
518
            ->where(
519
                $queryBuilder->expr()->eq(
520
                    'event',
521
                    $queryBuilder->createNamedParameter($event->getUid(), Connection::PARAM_INT)
522
                ),
523
                $queryBuilder->expr()->eq(
524
                    'waitlist',
525
                    $queryBuilder->createNamedParameter($waitlist, Connection::PARAM_INT)
526
                )
527
            )
528
            ->execute()
529
            ->fetchColumn();
530
    }
531
}
532