Passed
Pull Request — master (#6896)
by
unknown
09:34
created

SessionRepository::getSessionsByCourse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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