|
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) { |
|
|
|
|
|
|
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
|
|
|
|
In PHP, under loose comparison (like
==, or!=, orswitchconditions), values of different types might be equal.For
integervalues, zero is a special case, in particular the following results might be unexpected: