PluginProbe ʕ •ᴥ•ʔ
FAPI Member / 2.2.24
FAPI Member v2.2.24
2.2.33 2.2.32 trunk 1.9.47 2.1.18 2.2.24 2.2.25 2.2.26 2.2.28 2.2.29 2.2.30 2.2.31
fapi-member / src / Service / MembershipService.php
fapi-member / src / Service Last commit date
ApiService.php 11 months ago ElementService.php 11 months ago EmailService.php 11 months ago FormService.php 11 months ago LevelOrderService.php 11 months ago LevelService.php 11 months ago MembershipService.php 11 months ago RedirectService.php 11 months ago SanitizationService.php 11 months ago StatisticsService.php 11 months ago UserService.php 11 months ago
MembershipService.php
468 lines
1 <?php declare(strict_types=1);
2
3 namespace FapiMember\Service;
4
5 use DateInterval;
6 use DateTimeImmutable;
7 use FapiMember\Container\Container;
8 use FapiMember\Model\Enums\Format;
9 use FapiMember\Model\Enums\Keys\MetaKey;
10 use FapiMember\Model\Enums\Types\LevelUnlockType;
11 use FapiMember\Model\Membership;
12 use FapiMember\Repository\LevelRepository;
13 use FapiMember\Repository\MembershipRepository;
14 use FapiMember\Repository\UserRepository;
15 use FapiMember\Utils\DateTimeHelper;
16
17 class MembershipService
18 {
19 private MembershipRepository $membershipRepository;
20 private LevelRepository $levelRepository;
21 private UserRepository $userRepository;
22
23 public function __construct()
24 {
25 $this->membershipRepository = Container::get(MembershipRepository::class);
26 $this->levelRepository = Container::get(LevelRepository::class);
27 $this->userRepository = Container::get(UserRepository::class);
28 }
29
30 /**
31 * @return array<Membership>
32 */
33 public function getActiveWithAccessByUserId(int $userId): array
34 {
35 return $this->membershipRepository->getActiveByUserId($userId, true);
36 }
37
38 /**
39 * @return array<Membership>
40 */
41 public function getActiveByUserIdAndUpdate(int $userId): array
42 {
43 $memberships = $this->membershipRepository->getActiveByUserId($userId);
44 $this->membershipRepository->saveAll($userId, $memberships);
45
46 return $memberships;
47 }
48
49 /**
50 * @param array<Membership> $memberships
51 */
52 public function saveAll(int $userId, array $memberships): void
53 {
54 $this->membershipRepository->saveAll($userId, $memberships);
55 $this->fixMemberships($userId);
56 }
57
58 public function fixMemberships(int $userId): void
59 {
60 $this->extendMembershipsToSections($userId);
61 $this->timeUnlockLevelsForUser($userId);
62 $this->extendSectionsDates($userId);
63 }
64
65 public function saveOne(Membership $newMembership): void
66 {
67 if (
68 $this->membershipRepository->getOneByUserIdAndLevelId(
69 $newMembership->getUserId(),
70 $newMembership->getLevelId(),
71 ) !== null
72 ) {
73 return;
74 }
75
76 $memberships = $this->getActiveByUserIdAndUpdate($newMembership->getUserId());
77 $memberships[] = $newMembership;
78
79 $this->membershipRepository->saveAll($newMembership->getUserId(), $memberships);
80 }
81
82 public function update(Membership $updatedMembership): void
83 {
84 $memberships = $this->getActiveByUserIdAndUpdate($updatedMembership->getUserId());
85 $found = false;
86
87 foreach ($memberships as $key => $membership) {
88 if ($membership->getLevelId() === $updatedMembership->getLevelId()) {
89 $memberships[$key] = $updatedMembership;
90 $found = true;
91 break;
92 }
93 }
94
95 if ($found) {
96 $this->saveAll($updatedMembership->getUserId(), $memberships);
97 }
98 }
99
100 /** @return array<mixed> */
101 public function createOrUpdateMembership(
102 int $userId,
103 int $levelId,
104 DateTimeImmutable|null $registered,
105 DateTimeImmutable|null $until = null,
106 ): array
107 {
108 $oldMembership = $this->membershipRepository->getOneByUserIdAndLevelId($userId, $levelId);
109
110 $newMembership = new Membership([
111 'level_id' => $levelId,
112 'user_id' => $userId,
113 'registered' => $registered,
114 'until' => $until,
115 'is_unlimited' => $until === null,
116 ]);
117
118 if ($oldMembership !== null) {
119 $this->update($newMembership);
120 } else {
121 $this->saveOne($newMembership);
122 }
123
124 $this->fixMemberships($userId);
125
126 return [
127 'oldMembership' => $oldMembership,
128 'newMembership' => $newMembership,
129 ];
130 }
131
132 /** @return array<mixed> */
133 public function createOrProlongMembershipByDays(
134 int $userId,
135 int $levelId,
136 int|null $days = null,
137 ): array
138 {
139 $membership = $this->membershipRepository->getOneByUserIdAndLevelId($userId, $levelId);
140
141 $isUnlimited = $days === null;
142
143 if ($membership !== null) {
144 $props = $this->prolongMembership($membership, $isUnlimited, $days);
145 } else {
146 $props = $this->createMembershipFromDays($userId, $levelId, $isUnlimited, $days);
147 }
148
149 $this->fixMemberships($userId);
150
151 return $props;
152 }
153
154 /**
155 * @return array<mixed>
156 * @throws \Exception
157 */
158 public function createMembershipFromDays(
159 int $userId,
160 int $levelId,
161 $isUnlimited,
162 int|null $days = null,
163 ): array
164 {
165 $props = [];
166 $props['oldMembership'] = null;
167
168 $registered = DateTimeHelper::getNow();
169
170 if ($isUnlimited) {
171 $until = null;
172 } else {
173 $until = $registered;
174 $until = $until->modify(sprintf('+ %s days', $days ));
175 }
176
177 $newMembership = new Membership([
178 'level_id' => $levelId,
179 'user_id' => $userId,
180 'registered' => $registered,
181 'until' => $until,
182 'is_unlimited' => $isUnlimited
183 ]);
184
185 $props['newMembership'] = $newMembership;
186
187 $this->saveOne($newMembership);
188
189 return $props;
190 }
191
192 /** @return array<mixed> */
193 public function prolongMembership(
194 Membership $membership,
195 bool $isUnlimited,
196 int|null $days = null,
197 ): array
198 {
199 $props = [];
200 $props['oldMembership'] = $membership;
201
202 if ($isUnlimited || $membership->isUnlimited()) {
203 $membership->setIsUnlimited(true);
204 } else {
205 $membership->setUntil(
206 $membership->getUntil()->modify(sprintf('+ %s days', $days))
207 );
208 }
209
210 $props['newMembership'] = $membership;
211
212 $this->update($membership);
213
214 return $props;
215 }
216
217 public function extendMembershipsToSections(int $userId): void
218 {
219 $activeMemberships = $this->getActiveByUserIdAndUpdate($userId);
220
221 if ($activeMemberships === []) {
222 return;
223 }
224
225 /** @var array<Membership> $extendedMembership */
226 $extendedMemberships = $activeMemberships;
227 $sectionsToExtend = [];
228
229 foreach ($activeMemberships as $activeMembership) {
230 $level = $this->levelRepository->getLevelById($activeMembership->getLevelId());
231
232 if ($level !== null && !$level->isSection()) {
233 $sectionsToExtend[$level->getParentId()] = $this->levelRepository->getSectionById($level->getParentId());
234 }
235 }
236
237 foreach ($sectionsToExtend as $section) {
238 $childLevelIds = [];
239
240 foreach ($section->getLevels() as $level) {
241 $childLevelIds[] = $level->getId();
242 }
243
244 $LevelMemberships = array_filter(
245 $activeMemberships,
246 static function ( $membership ) use ( $childLevelIds ) {
247 return in_array( $membership->getLevelId(), $childLevelIds, true );
248 }
249 );
250
251 $childIsUnlimited = false;
252 $childMaxUntil = null;
253 $childMinRegistered = null;
254
255 foreach ($LevelMemberships as $levelMembership ) {
256 if ( $levelMembership->isUnlimited() === true ) {
257 $childIsUnlimited = true;
258 }
259
260 if ($levelMembership->getUntil() !== null) {
261 $childMaxUntil = max($childMaxUntil, $levelMembership->getUntil());
262 }
263
264 if ($levelMembership->getRegistered() !== null) {
265 if ($childMinRegistered === null) {
266 $childMinRegistered = $levelMembership->getRegistered();
267 } else {
268 $childMinRegistered = min(
269 $childMinRegistered,
270 $levelMembership->getRegistered(),
271 );
272 }
273 }
274 }
275
276 $wasParentTermExtended = null;
277
278 foreach ($extendedMemberships as $extendedMembership) {
279 if ($extendedMembership->getLevelId() === $section->getId()) {
280 if ($extendedMembership->isUnlimited()) {
281 $wasParentTermExtended = true;
282 break;
283 }
284
285 if ($childIsUnlimited) {
286 $extendedMembership->setIsUnlimited(true);
287 $extendedMembership->setUntil(null);
288 $wasParentTermExtended = true;
289 break;
290 }
291
292 $extendedMembership->setUntil(
293 max($extendedMembership->getUntil(), $childMaxUntil),
294 );
295 $wasParentTermExtended = true;
296 }
297 }
298
299 if (!$wasParentTermExtended) {
300 $newMembership = new Membership([
301 'level_id' => $section->getId(),
302 'user_id' => $userId,
303 'registered' => $childMinRegistered,
304 'until' => $childMaxUntil,
305 'is_unlimited' => $childIsUnlimited,
306 ]);
307
308 $this->saveOne($newMembership);
309 }
310 }
311 }
312
313 private function extendSectionsDates(int $userId): void
314 {
315 $sections = $this->levelRepository->getAllSections();
316
317 foreach ($sections as $section) {
318 $parentMembership = $this->membershipRepository->getOneByUserIdAndLevelId($userId, $section->getId());
319
320 $memberships = $this->membershipRepository->getAllLevelMembershipsByUserIdAndSectionId($userId, $section->getId());
321 $newRegistered = null;
322 $newUntil = null;
323 $newIsUnlimited = null;
324
325
326 foreach ($memberships as $membership) {
327 if ($membership?->getRegistered() === null) {
328 continue;
329 }
330
331 if (
332 $membership->getRegistered() < $parentMembership->getRegistered()
333 && ($newRegistered === null || $membership->getRegistered() < $newRegistered)
334 ) {
335 $newRegistered = $membership->getRegistered();
336 }
337
338 if (
339 $parentMembership->isUnlimited() !== true
340 && $membership->getUntil() !== null
341 && $membership->getUntil() > $parentMembership->getUntil()
342 && ($newUntil === null || $membership->getUntil() > $newUntil)
343 ) {
344 $newUntil = $membership->getUntil();
345 }
346
347 if ($membership->isUnlimited() && !$parentMembership->isUnlimited()) {
348 $newIsUnlimited = true;
349 }
350 }
351
352 if ($newRegistered !== null) {
353 $parentMembership->setRegistered($newRegistered);
354 }
355
356 if ($newIsUnlimited === true) {
357 $parentMembership->setIsUnlimited(true);
358 $parentMembership->setUntil(null);
359 } else if ($newUntil !== null) {
360 $parentMembership->setUntil($newUntil);
361 }
362
363 if ($newUntil !== null || $newRegistered !== null || $newIsUnlimited === true) {
364 $this->update($parentMembership);
365 }
366 }
367 }
368
369 public function timeUnlockLevelsForAllUsers(): void
370 {
371 $users = $this->userRepository->getAllUsers();
372
373 foreach ($users as $user) {
374 $this->timeUnlockLevelsForUser($user->getId());
375 }
376 }
377
378 public function timeUnlockLevelsForUser(int $userId): void
379 {
380 $memberships = $this->getActiveByUserIdAndUpdate($userId);
381
382 foreach ($memberships as $membership){
383 $section = $this->levelRepository->getSectionById($membership->getLevelId());
384
385 if ($section === null) {
386 continue;
387 }
388
389 foreach ($section->getLevels() as $level) {
390 $unlockDate = null;
391 $unlockHour = $this->levelRepository->getHourUnlock($level->getId());
392
393 if ($level->getUnlockType() === LevelUnlockType::DAYS) {
394 $daysToUnlock = (int) get_term_meta($level->getId(), MetaKey::DAYS_TO_UNLOCK, true);
395 $date = $membership->getRegistered()->getTimestamp();
396 $unlockDate = $date - ($date % 86400) + (86400 * $daysToUnlock) + (3600 * $unlockHour);
397 } elseif ($level->getUnlockType() === LevelUnlockType::DATE) {
398 $date = strtotime(get_term_meta($level->getId(), MetaKey::DATE_UNLOCK, true));
399 $date = $date - ($date % 86400) + (3600 * $unlockHour);
400
401 if (
402 $this->levelRepository->getAfterDateUnlock($level->getId()) === false
403 || $membership->getRegistered()->getTimestamp() < $date
404 ) {
405 $unlockDate = $date;
406 }
407 }
408
409 if ($unlockDate === null) {
410 continue;
411 }
412
413 $unlockDate -= 60;
414
415 if (DateTimeHelper::getNowTimestamp() >= $unlockDate) {
416 $this->saveOne(new Membership([
417 'level_id' => $level->getId(),
418 'user_id' => $userId,
419 'registered' => DateTimeHelper::getNow()->format(Format::DATE_TIME),
420 'until' => $membership->getUntil()?->format(Format::DATE_TIME),
421 'is_unlimited' => $membership->isUnlimited(),
422 ]));
423 }
424 }
425 }
426 }
427
428 /**
429 * @throws \Exception
430 */
431 public function getUnlockDate(int $levelId, int $userId, DateTimeImmutable|null $registrationDate = null): DateTimeImmutable|null
432 {
433 $level = $this->levelRepository->getLevelById($levelId);
434
435 if ($level?->getParentId() === null || $level->getUnlockType() === null || $level->getUnlockType() === LevelUnlockType::NONE) {
436 return null;
437 }
438
439 $date = null;
440
441 if ($level->getUnlockType() === LevelUnlockType::DATE) {
442 $date = DateTimeHelper::createOrNull($this->levelRepository->getDateUnlock($level->getId()), Format::DATE);
443
444 } elseif ($level->getUnlockType() === LevelUnlockType::DAYS) {
445 if ($registrationDate === null) {
446 $parentMembership = $this->membershipRepository->getOneByUserIdAndLevelId($userId, $level->getParentId());
447
448 if ($parentMembership === null) {
449 return null;
450 }
451 $registrationDate = $parentMembership->getRegistered();
452 }
453
454 $date = $registrationDate->add(
455 new DateInterval('P' . $this->levelRepository->getDaysUnlock($level->getId()) . 'D'),
456 );
457 }
458
459 if ($date !== null) {
460 $hour = $this->levelRepository->getHourUnlock($level->getId());
461 $date = $date->setTime($hour, 0);
462 }
463
464 return $date;
465 }
466
467 }
468