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