Passed
Pull Request — master (#7026)
by
unknown
09:30
created

CourseRelUserRepository::countAllByCourseId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 2
dl 0
loc 14
rs 9.9666
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\CourseRelUser;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CourseBundle\Entity\CLp;
12
use Chamilo\CourseBundle\Entity\CLpView;
13
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
14
use Doctrine\Persistence\ManagerRegistry;
15
16
class CourseRelUserRepository extends ServiceEntityRepository
17
{
18
    public function __construct(ManagerRegistry $registry)
19
    {
20
        parent::__construct($registry, CourseRelUser::class);
21
    }
22
23
    /**
24
     * Retrieves users from a course and their LP progress (without session).
25
     */
26
    public function getCourseUsers(int $courseId, array $lpIds): array
27
    {
28
        $qb = $this->createQueryBuilder('cu')
29
            ->select('u.id AS userId, c.title AS courseTitle, lp.iid AS lpId, COALESCE(lpv.progress, 0) AS progress')
30
            ->innerJoin('cu.user', 'u')
31
            ->innerJoin('cu.course', 'c')
32
            ->leftJoin(CLpView::class, 'lpv', 'WITH', 'lpv.user = u.id AND lpv.course = cu.course AND lpv.lp IN (:lpIds)')
33
            ->leftJoin(CLp::class, 'lp', 'WITH', 'lp.iid IN (:lpIds)')
34
            ->innerJoin('lp.resourceNode', 'rn')
35
            ->where('cu.course = :courseId')
36
            ->andWhere('rn.parent = c.resourceNode')
37
            ->andWhere('(lpv.progress < 100 OR lpv.progress IS NULL)')
38
            ->setParameter('courseId', $courseId)
39
            ->setParameter('lpIds', $lpIds)
40
        ;
41
42
        return $qb->getQuery()->getResult();
43
    }
44
45
    /**
46
     * Count distinct courses where the given user is a teacher (status == TEACHER).
47
     */
48
    public function countTaughtCoursesForUser(User $user): int
49
    {
50
        return (int) $this->createQueryBuilder('cru')
51
            ->select('COUNT(DISTINCT c.id)')
52
            ->innerJoin('cru.course', 'c')
53
            ->andWhere('cru.user = :user')
54
            ->andWhere('cru.status = :teacher')
55
            ->setParameter('user', $user)
56
            ->setParameter('teacher', CourseRelUser::TEACHER)
57
            ->getQuery()
58
            ->getSingleScalarResult();
59
    }
60
61
    /**
62
     * Count all subscriptions (any status) for the given course ID.
63
     * Optionally exclude a specific relationType (e.g., RRHH).
64
     *
65
     * @param int        $courseId
66
     * @param int|null   $excludeRelationType If provided, rows with this relationType are excluded
67
     */
68
    public function countAllByCourseId(int $courseId, ?int $excludeRelationType = null): int
69
    {
70
        $qb = $this->createQueryBuilder('cru')
71
            ->select('COUNT(cru.id)')
72
            ->innerJoin('cru.course', 'c')
73
            ->andWhere('c.id = :cid')
74
            ->setParameter('cid', $courseId);
75
76
        if (null !== $excludeRelationType) {
77
            $qb->andWhere('cru.relationType <> :rt')
78
                ->setParameter('rt', $excludeRelationType);
79
        }
80
81
        return (int) $qb->getQuery()->getSingleScalarResult();
82
    }
83
84
    /**
85
     * Compute a capacity snapshot for UI (limit/used/left/is_full).
86
     * This only considers the global hosting limit and an optional per-course limit
87
     * already computed by caller. A limit of 0 means "no limit".
88
     *
89
     * @param int        $courseId
90
     * @param int|null   $perCourseLimit  Optional course-level limit (e.g., extra field). If provided, the effective
91
     *                                    limit will be min(global, per-course). If null, only global limit is used.
92
     *
93
     * @return array{limit:int, used:int, left:int|null, is_full:bool}
94
     */
95
    public function getCapacitySnapshot(int $courseId, ?int $perCourseLimit = null): array
96
    {
97
        // Read global limit (0 means unlimited)
98
        $global = (int) api_get_setting('hosting_limit_users_per_course');
99
100
        // Effective limit resolution rule:
101
        // - if per-course limit is provided and > 0, use min(global, per-course) when global > 0, else per-course
102
        // - else use global as-is (0 => unlimited)
103
        $limit = 0;
104
        if ($perCourseLimit && $perCourseLimit > 0) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $perCourseLimit 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...
105
            $limit = $global > 0 ? min($global, $perCourseLimit) : $perCourseLimit;
106
        } else {
107
            $limit = $global;
108
        }
109
110
        if ($limit <= 0) {
111
            return ['limit' => 0, 'used' => 0, 'left' => null, 'is_full' => false];
112
        }
113
114
        // Exclude RRHH relation type if available (keeps legacy behavior)
115
        $exclude = \defined('COURSE_RELATION_TYPE_RRHH') ? COURSE_RELATION_TYPE_RRHH : null;
116
        $used = $this->countAllByCourseId($courseId, $exclude);
117
118
        return [
119
            'limit'   => $limit,
120
            'used'    => $used,
121
            'left'    => max(0, $limit - $used),
122
            'is_full' => $used >= $limit,
123
        ];
124
    }
125
}
126