PluginProbe ʕ •ᴥ•ʔ
FAPI Member / trunk
FAPI Member vtrunk
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
AdminMenuService.php 2 years ago ApiService.php 1 day ago ElementService.php 7 months ago EmailService.php 1 day ago FormService.php 1 year ago LevelOrderService.php 1 year ago LevelService.php 1 year ago MembershipService.php 1 year ago RedirectService.php 1 year ago SanitizationService.php 2 years ago StatisticsService.php 1 year ago UserService.php 1 year 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