Passed
Push — master ( 35e116...03a9f3 )
by Angel Fernando Quiroz
10:35 queued 19s
created

SessionRepository::countUsersBySession()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 2
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Repository;
8
9
use Chamilo\CoreBundle\Entity\AccessUrl;
10
use Chamilo\CoreBundle\Entity\Course;
11
use Chamilo\CoreBundle\Entity\Session;
12
use Chamilo\CoreBundle\Entity\SessionRelCourse;
13
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
14
use Chamilo\CoreBundle\Entity\SessionRelUser;
15
use Chamilo\CoreBundle\Entity\User;
16
use Chamilo\CoreBundle\Settings\SettingsManager;
17
use DateTime;
18
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
19
use Doctrine\Common\Collections\ArrayCollection;
20
use Doctrine\ORM\Query\Expr\Join;
21
use Doctrine\ORM\QueryBuilder;
22
use Doctrine\Persistence\ManagerRegistry;
23
use Exception;
24
25
/**
26
 * @author Julio Montoya <[email protected]>
27
 */
28
class SessionRepository extends ServiceEntityRepository
29
{
30
    public function __construct(
31
        ManagerRegistry $registry,
32
        private readonly SettingsManager $settingsManager,
33
    ) {
34
        parent::__construct($registry, Session::class);
35
    }
36
37
    public function create(): ?Session
38
    {
39
        return new Session();
40
    }
41
42
    public function update(Session $session): void
43
    {
44
        $this->getEntityManager()->persist($session);
45
        $this->getEntityManager()->flush();
46
    }
47
48
    /**
49
     * @return array<SessionRelUser>
50
     */
51
    public function getUsersByAccessUrl(Session $session, AccessUrl $url, array $relationTypeList = []): array
52
    {
53
        if (0 === $session->getUsers()->count()) {
54
            return [];
55
        }
56
57
        $qb = $this->addSessionRelUserFilterByUrl($session, $url);
58
        $qb->orderBy('sru.relationType');
59
60
        if ($relationTypeList) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $relationTypeList of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
61
            $qb->andWhere(
62
                $qb->expr()->in('sru.relationType', $relationTypeList)
63
            );
64
        }
65
66
        return $qb->getQuery()->getResult();
67
    }
68
69
    public function getSessionsByUser(User $user, AccessUrl $url): QueryBuilder
70
    {
71
        $qb = $this->createQueryBuilder('s');
72
        $qb
73
            ->innerJoin('s.users', 'sru')
74
            ->leftJoin('s.urls', 'urls')
75
            ->where($qb->expr()->eq('sru.user', ':user'))
76
            ->andWhere($qb->expr()->eq('urls.url', ':url'))
77
            ->setParameters([
78
                'user' => $user,
79
                'url' => $url,
80
            ])
81
            ->orderBy('s.displayStartDate', 'ASC')
82
            ->addOrderBy('s.title', 'ASC')
83
            ->addOrderBy('s.position', 'ASC')
84
        ;
85
86
        return $qb;
87
    }
88
89
    /**
90
     * @return array<int, Session>
91
     *
92
     * @throws Exception
93
     */
94
    public function getPastSessionsOfUserInUrl(User $user, AccessUrl $url): array
95
    {
96
        $sessions = $this->getSubscribedSessionsOfUserInUrl($user, $url);
97
98
        $filterPastSessions = function (Session $session) use ($user) {
99
            $now = new DateTime();
100
            // Determine if the user is a coach
101
            $userIsCoach = $session->hasCoach($user);
102
103
            // Check if the session has a duration
104
            if ($session->getDuration() > 0) {
105
                $daysLeft = $session->getDaysLeftByUser($user);
106
                $session->setTitle($session->getTitle().'<-'.$daysLeft);
107
108
                return $daysLeft < 0 && !$userIsCoach;
109
            }
110
111
            // Get the appropriate end date based on whether the user is a coach
112
            $sessionEndDate = $userIsCoach && $session->getCoachAccessEndDate()
113
                ? $session->getCoachAccessEndDate()
114
                : $session->getAccessEndDate();
115
116
            // If there's no end date, the session is not considered past
117
            if (!$sessionEndDate) {
118
                return false;
119
            }
120
121
            // Check if the current date is after the end date
122
            return $now > $sessionEndDate;
123
        };
124
125
        return array_filter($sessions, $filterPastSessions);
126
    }
127
128
    /**
129
     * @return array<int, Session>
130
     *
131
     * @throws Exception
132
     */
133
    public function getCurrentSessionsOfUserInUrl(
134
        User $user,
135
        AccessUrl $url,
136
        int $currentPage = 1,
137
        int $itemsPerPage = 10
138
    ): array {
139
        $qb = $this->getSessionsByUser($user, $url);
140
141
        return $qb
142
            ->andWhere(
143
                $qb->expr()->orX(
144
                    $qb->expr()->isNull('sru.accessStartDate'),
145
                    $qb->expr()->isNull('sru.accessEndDate'),
146
                    $qb->expr()->andX(
147
                        $qb->expr()->lte('sru.accessStartDate', ':now'),
148
                        $qb->expr()->gte('sru.accessEndDate', ':now'),
149
                    ),
150
                )
151
            )
152
            ->setParameter('now', new DateTime())
153
            ->setFirstResult(($currentPage - 1) * $itemsPerPage)
154
            ->setMaxResults($itemsPerPage)
155
            ->getQuery()
156
            ->getResult();
157
        ;
158
    }
159
160
    /**
161
     * @return array<int, Session>
162
     *
163
     * @throws Exception
164
     */
165
    public function getUpcomingSessionsOfUserInUrl(
166
        User $user,
167
        AccessUrl $url,
168
        int $currentPage = 1,
169
        int $itemsPerPage = 10
170
    ): array {
171
        $qb = $this->getSessionsByUser($user, $url);
172
173
        return $qb
174
            ->andWhere(
175
                $qb->expr()->gt('sru.accessStartDate', ':now'),
176
            )
177
            ->setParameter('now', new DateTime())
178
            ->setFirstResult(($currentPage - 1) * $itemsPerPage)
179
            ->setMaxResults($itemsPerPage)
180
            ->getQuery()
181
            ->getResult();
182
        ;
183
    }
184
185
    public function addUserInCourse(int $relationType, User $user, Course $course, Session $session): void
186
    {
187
        if (!$user->isActive()) {
188
            throw new Exception('User not active');
189
        }
190
191
        if (!$session->hasCourse($course)) {
192
            $msg = \sprintf('Course %s is not subscribed to the session %s', $course->getTitle(), $session->getTitle());
193
194
            throw new Exception($msg);
195
        }
196
197
        if (!\in_array($relationType, Session::getRelationTypeList(), true)) {
198
            throw new Exception(\sprintf('Cannot handle relationType %s', $relationType));
199
        }
200
201
        $entityManager = $this->getEntityManager();
202
        $existingRecord = $entityManager->getRepository(SessionRelUser::class)->findOneBy([
203
            'session' => $session,
204
            'user' => $user,
205
            'relationType' => $relationType,
206
        ]);
207
208
        if ($existingRecord) {
209
            $entityManager->remove($existingRecord);
210
            $entityManager->flush();
211
        }
212
213
        switch ($relationType) {
214
            case Session::DRH:
215
                if ($user->hasRole('ROLE_HR')) {
216
                    $session->addUserInSession(Session::DRH, $user);
217
                }
218
219
                break;
220
221
            case Session::STUDENT:
222
                $session
223
                    ->addUserInSession(Session::STUDENT, $user)
224
                    ->addUserInCourse(Session::STUDENT, $user, $course)
225
                ;
226
227
                break;
228
229
            case Session::COURSE_COACH:
230
                if ($user->hasRole('ROLE_TEACHER')) {
231
                    $session
232
                        ->addUserInSession(Session::COURSE_COACH, $user)
233
                        ->addUserInCourse(
234
                            Session::COURSE_COACH,
235
                            $user,
236
                            $course
237
                        )
238
                    ;
239
                }
240
241
                break;
242
        }
243
244
        $entityManager->persist($session);
245
        $entityManager->flush();
246
    }
247
248
    /**
249
     * @return array<SessionRelCourse>
250
     */
251
    public function getSessionCoursesByStatusInUserSubscription(User $user, Session $session, int $relationType, ?AccessUrl $url = null): array
252
    {
253
        $qb = $this->getEntityManager()->createQueryBuilder();
254
255
        $qb->select('src')
256
            ->from(SessionRelCourse::class, 'src')
257
            ->innerJoin(
258
                SessionRelUser::class,
259
                'sru',
260
                Join::WITH,
261
                'src.session = sru.session'
262
            )
263
            ->innerJoin('src.session', 'session')
264
            ->where(
265
                $qb->expr()->eq('session', ':session')
266
            )
267
            ->andWhere(
268
                $qb->expr()->eq('sru.user', ':user')
269
            )
270
            ->andWhere(
271
                $qb->expr()->eq('sru.relationType', ':relation_type')
272
            )
273
        ;
274
275
        $parameters = [
276
            'session' => $session,
277
            'user' => $user,
278
            'relation_type' => $relationType,
279
        ];
280
281
        if ($url) {
282
            $qb->innerJoin('session.urls', 'urls')
283
                ->andWhere(
284
                    $qb->expr()->eq('urls.url', ':url')
285
                )
286
            ;
287
288
            $parameters['url'] = $url;
289
        }
290
291
        $qb->setParameters($parameters);
292
293
        return $qb->getQuery()->getResult();
294
    }
295
296
    /**
297
     * @return array<SessionRelCourse>
298
     */
299
    public function getSessionCoursesByStatusInCourseSubscription(User $user, Session $session, int $status, ?AccessUrl $url = null): array
300
    {
301
        $qb = $this->getEntityManager()->createQueryBuilder();
302
303
        $qb->select('src')
304
            ->from(SessionRelCourse::class, 'src')
305
            ->innerJoin(
306
                SessionRelCourseRelUser::class,
307
                'srcru',
308
                Join::WITH,
309
                'src.session = srcru.session AND src.course = srcru.course'
310
            )
311
            ->innerJoin('srcru.session', 'session')
312
            ->where(
313
                $qb->expr()->eq('session', ':session')
314
            )
315
            ->andWhere(
316
                $qb->expr()->eq('srcru.user', ':user')
317
            )
318
            ->andWhere(
319
                $qb->expr()->eq('srcru.status', ':status')
320
            )
321
        ;
322
323
        $parameters = [
324
            'session' => $session,
325
            'user' => $user,
326
            'status' => $status,
327
        ];
328
329
        if ($url) {
330
            $qb->innerJoin('session.urls', 'urls')
331
                ->andWhere(
332
                    $qb->expr()->eq('urls.url', ':url')
333
                )
334
            ;
335
336
            $parameters['url'] = $url;
337
        }
338
339
        $qb->setParameters($parameters);
340
341
        return $qb->getQuery()->getResult();
342
    }
343
344
    private function addSessionRelUserFilterByUrl(Session $session, AccessUrl $url): QueryBuilder
345
    {
346
        $qb = $this->getEntityManager()->createQueryBuilder();
347
        $qb
348
            ->select('sru')
349
            ->from(SessionRelUser::class, 'sru')
350
            ->innerJoin('sru.user', 'u')
351
            ->innerJoin('u.portals', 'p')
352
            ->andWhere('sru.session = :session AND p.url = :url')
353
            ->setParameters([
354
                'session' => $session,
355
                'url' => $url,
356
            ])
357
        ;
358
359
        return $qb;
360
    }
361
362
    public function getUserFollowedSessionsInAccessUrl(User $user, AccessUrl $url): QueryBuilder
363
    {
364
        $callback = fn (Session $session) => $session->getId();
365
366
        if ($user->isHRM()) {
367
            $idList = array_map($callback, $user->getDRHSessions());
368
        } elseif ($user->isTeacher() || COURSEMANAGER === $user->getStatus()) {
369
            $idListAsCoach = $user
370
                ->getSessionsByStatusInCourseSubscription(Session::COURSE_COACH)
371
                ->map($callback)
372
                ->getValues()
373
            ;
374
            $idListAsGeneralCoach = array_map($callback, $user->getSessionsAsGeneralCoach());
375
            $idList = array_merge($idListAsCoach, $idListAsGeneralCoach);
376
        } elseif ($user->isSessionAdmin()) {
377
            $idList = array_map($callback, $user->getSessionsAsAdmin());
378
        } else {
379
            $idList = array_map($callback, $user->getSessionsAsStudent());
380
        }
381
382
        $qb = $this->createQueryBuilder('s');
383
        $qb
384
            ->innerJoin('s.urls', 'u')
385
            ->where($qb->expr()->eq('u.url', $url->getId()))
386
            ->andWhere($qb->expr()->in('s.id', ':id_list'))
387
            ->setParameter('id_list', $idList)
388
        ;
389
390
        return $qb;
391
    }
392
393
    /**
394
     * @return array<int, Session>
395
     *
396
     * @throws Exception
397
     */
398
    public function getSubscribedSessionsOfUserInUrl(
399
        User $user,
400
        AccessUrl $url,
401
        bool $ignoreVisibilityForAdmins = false,
402
    ): array {
403
        $sessions = $this->getSessionsByUser($user, $url)->getQuery()->getResult();
404
405
        $filterSessions = function (Session $session) use ($user, $ignoreVisibilityForAdmins) {
406
            $visibility = $session->setAccessVisibilityByUser($user, $ignoreVisibilityForAdmins);
407
408
            if (Session::VISIBLE !== $visibility) {
409
                $closedOrHiddenCourses = $session->getClosedOrHiddenCourses();
410
411
                if ($closedOrHiddenCourses->count() === $session->getCourses()->count()) {
412
                    $visibility = Session::INVISIBLE;
413
                }
414
            }
415
416
            switch ($visibility) {
417
                case Session::READ_ONLY:
418
                case Session::VISIBLE:
419
                case Session::AVAILABLE:
420
                    break;
421
422
                case Session::INVISIBLE:
423
                    if (!$ignoreVisibilityForAdmins) {
424
                        return false;
425
                    }
426
            }
427
428
            return true;
429
        };
430
431
        return array_filter($sessions, $filterSessions);
432
    }
433
434
    /**
435
     * Finds a valid child session based on access dates and reinscription days.
436
     */
437
    public function findValidChildSession(Session $session): ?Session
438
    {
439
        $childSessions = $this->findChildSessions($session);
440
        $now = new DateTime();
441
442
        foreach ($childSessions as $childSession) {
443
            $startDate = $childSession->getAccessStartDate();
444
            $endDate = $childSession->getAccessEndDate();
445
            $daysToReinscription = $childSession->getDaysToReinscription();
446
447
            if (empty($daysToReinscription) || $daysToReinscription <= 0) {
448
                continue;
449
            }
450
451
            $adjustedEndDate = (clone $endDate)->modify('-'.$daysToReinscription.' days');
452
453
            if ($startDate <= $now && $adjustedEndDate >= $now) {
454
                return $childSession;
455
            }
456
        }
457
458
        return null;
459
    }
460
461
    /**
462
     * Finds a valid parent session based on access dates and reinscription days.
463
     */
464
    public function findValidParentSession(Session $session): ?Session
465
    {
466
        $parentSession = $this->findParentSession($session);
467
        if ($parentSession) {
468
            $now = new DateTime();
469
            $startDate = $parentSession->getAccessStartDate();
470
            $endDate = $parentSession->getAccessEndDate();
471
            $daysToReinscription = $parentSession->getDaysToReinscription();
472
473
            // Return null if days to reinscription is not set
474
            if (null === $daysToReinscription || '' === $daysToReinscription) {
475
                return null;
476
            }
477
478
            // Adjust the end date by days to reinscription
479
            $endDate = $endDate->modify('-'.$daysToReinscription.' days');
480
481
            // Check if the current date falls within the session's validity period
482
            if ($startDate <= $now && $endDate >= $now) {
483
                return $parentSession;
484
            }
485
        }
486
487
        return null;
488
    }
489
490
    /**
491
     * Finds child sessions based on the parent session.
492
     */
493
    public function findChildSessions(Session $parentSession): array
494
    {
495
        return $this->createQueryBuilder('s')
496
            ->where('s.parentId = :parentId')
497
            ->setParameter('parentId', $parentSession->getId())
498
            ->getQuery()
499
            ->getResult()
500
        ;
501
    }
502
503
    /**
504
     * Finds the parent session for a given session.
505
     */
506
    public function findParentSession(Session $session): ?Session
507
    {
508
        if ($session->getParentId()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $session->getParentId() of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
509
            return $this->find($session->getParentId());
510
        }
511
512
        return null;
513
    }
514
515
    /**
516
     * Find sessions without child and ready for repetition.
517
     *
518
     * @return Session[]
519
     */
520
    public function findSessionsWithoutChildAndReadyForRepetition()
521
    {
522
        $currentDate = new DateTime();
523
524
        $qb = $this->createQueryBuilder('s')
525
            ->where('s.daysToNewRepetition IS NOT NULL')
526
            ->andWhere('s.lastRepetition = :false')
527
            ->andWhere(':currentDate BETWEEN DATE_SUB(s.accessEndDate, s.daysToNewRepetition, \'DAY\') AND s.accessEndDate')
528
            ->andWhere('NOT EXISTS (
529
                SELECT 1
530
                FROM Chamilo\CoreBundle\Entity\Session child
531
                WHERE child.parentId = s.id
532
                AND child.accessEndDate >= :currentDate
533
            )')
534
            ->setParameter('false', false)
535
            ->setParameter('currentDate', $currentDate)
536
        ;
537
538
        return $qb->getQuery()->getResult();
539
    }
540
541
    public function countUsersBySession(int $sessionId, int $relationType = Session::STUDENT): int
542
    {
543
        $qb = $this->createQueryBuilder('s');
544
        $qb->select('COUNT(sru.id)')
545
            ->innerJoin('s.users', 'sru')
546
            ->where('s.id = :sessionId')
547
            ->andWhere('sru.relationType = :relationType')
548
            ->setParameter('sessionId', $sessionId)
549
            ->setParameter('relationType', $relationType)
550
        ;
551
552
        return (int) $qb->getQuery()->getSingleScalarResult();
553
    }
554
}
555