233 lines
7.6 KiB
PHP
233 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\ConfigurationModel;
|
|
use App\Models\StaffModel;
|
|
use App\Models\UserModel;
|
|
use CodeIgniter\Database\BaseConnection;
|
|
|
|
final class StaffDirectorySyncService
|
|
{
|
|
private const WORK_EMAIL_DOMAIN = '@alrahmaisgl.org';
|
|
|
|
public function __construct(
|
|
private readonly StaffModel $staffModel,
|
|
private readonly UserModel $userModel,
|
|
private readonly ConfigurationModel $configurationModel,
|
|
private readonly BaseConnection $db,
|
|
) {
|
|
}
|
|
|
|
public function syncAll(): void
|
|
{
|
|
$rows = $this->db->table('users')
|
|
->select('id')
|
|
->get()
|
|
->getResultArray();
|
|
|
|
foreach ($rows as $row) {
|
|
$userId = (int) ($row['id'] ?? 0);
|
|
if ($userId > 0) {
|
|
$this->syncUser($userId);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function syncUser(int $userId): void
|
|
{
|
|
if ($userId <= 0) {
|
|
return;
|
|
}
|
|
|
|
$user = $this->userModel->find($userId);
|
|
if (! $user) {
|
|
return;
|
|
}
|
|
|
|
$roles = $this->rolesForUser($userId);
|
|
$staffRoles = array_values(array_filter($roles, static function (array $role): bool {
|
|
return strtolower((string) ($role['name'] ?? '')) !== 'parent';
|
|
}));
|
|
|
|
$now = utc_now();
|
|
$schoolYear = (string) ($this->configurationModel->getConfig('school_year') ?? '');
|
|
$roleNames = array_values(array_unique(array_map(
|
|
static fn (array $role): string => strtolower((string) ($role['name'] ?? '')),
|
|
$roles
|
|
)));
|
|
|
|
$activeRole = ! empty($staffRoles)
|
|
? strtolower((string) ($staffRoles[0]['name'] ?? ''))
|
|
: 'inactive';
|
|
|
|
$this->staffModel->upsert([
|
|
'user_id' => $userId,
|
|
'firstname' => (string) ($user['firstname'] ?? ''),
|
|
'lastname' => (string) ($user['lastname'] ?? ''),
|
|
'email' => $this->generateUniqueStaffEmail(
|
|
(string) ($user['firstname'] ?? ''),
|
|
(string) ($user['lastname'] ?? ''),
|
|
$userId
|
|
),
|
|
'phone' => (string) ($user['cellphone'] ?? ''),
|
|
'role_name' => implode(', ', $roleNames),
|
|
'active_role' => $activeRole,
|
|
'school_year' => $schoolYear,
|
|
'updated_at' => $now,
|
|
]);
|
|
}
|
|
|
|
public function activeStaffForSchoolYear(string $schoolYear): array
|
|
{
|
|
$this->syncAll();
|
|
|
|
$staffRows = $this->db->table('users u')
|
|
->select([
|
|
'u.id AS user_id',
|
|
'u.school_id',
|
|
'u.firstname',
|
|
'u.lastname',
|
|
'u.email AS personal_email',
|
|
'u.cellphone',
|
|
's.id AS staff_id',
|
|
's.email AS work_email',
|
|
's.active_role',
|
|
's.role_name',
|
|
"GROUP_CONCAT(DISTINCT r.name ORDER BY COALESCE(r.priority, 999), r.name SEPARATOR ', ') AS roles",
|
|
])
|
|
->join('user_roles ur', 'ur.user_id = u.id', 'inner')
|
|
->join('roles r', 'r.id = ur.role_id', 'inner')
|
|
->join('staff s', 's.user_id = u.id', 'left')
|
|
->where("LOWER(r.name) != 'parent'", null, false)
|
|
->where('COALESCE(r.is_active, 1) = 1', null, false);
|
|
|
|
if ($this->hasField('user_roles', 'deleted_at')) {
|
|
$staffRows->where('ur.deleted_at', null);
|
|
}
|
|
|
|
$rows = $staffRows
|
|
->groupBy('u.id, u.school_id, u.firstname, u.lastname, u.email, u.cellphone, s.id, s.email, s.active_role, s.role_name')
|
|
->orderBy('u.lastname', 'ASC')
|
|
->orderBy('u.firstname', 'ASC')
|
|
->get()
|
|
->getResultArray();
|
|
|
|
$assignments = $this->assignmentsByTeacher($schoolYear);
|
|
$issuesCount = 0;
|
|
|
|
foreach ($rows as &$row) {
|
|
$userId = (int) ($row['user_id'] ?? 0);
|
|
$activeRole = strtolower((string) ($row['active_role'] ?? ''));
|
|
$roleText = strtolower((string) ($row['roles'] ?? ''));
|
|
$isTeacher = in_array($activeRole, ['teacher', 'teacher_assistant'], true)
|
|
|| str_contains($roleText, 'teacher')
|
|
|| preg_match('/(^|, )ta($|,)/', $roleText);
|
|
|
|
$labels = $assignments[$userId] ?? [];
|
|
if ($isTeacher) {
|
|
if ($labels !== []) {
|
|
$row['class_section'] = implode(', ', array_unique($labels));
|
|
$row['verification_issue'] = false;
|
|
} else {
|
|
$row['class_section'] = 'No class assigned';
|
|
$row['verification_issue'] = true;
|
|
$issuesCount++;
|
|
}
|
|
} else {
|
|
$row['class_section'] = '-';
|
|
$row['verification_issue'] = false;
|
|
}
|
|
}
|
|
unset($row);
|
|
|
|
return [
|
|
'staff' => $rows,
|
|
'issues_count' => $issuesCount,
|
|
];
|
|
}
|
|
|
|
private function rolesForUser(int $userId): array
|
|
{
|
|
$builder = $this->db->table('user_roles ur')
|
|
->select('r.name, r.priority')
|
|
->join('roles r', 'r.id = ur.role_id', 'inner')
|
|
->where('ur.user_id', $userId)
|
|
->where('COALESCE(r.is_active, 1) = 1', null, false)
|
|
->orderBy('COALESCE(r.priority, 999)', 'ASC', false)
|
|
->orderBy('r.name', 'ASC');
|
|
|
|
if ($this->hasField('user_roles', 'deleted_at')) {
|
|
$builder->where('ur.deleted_at', null);
|
|
}
|
|
|
|
return $builder->get()->getResultArray();
|
|
}
|
|
|
|
private function assignmentsByTeacher(string $schoolYear): array
|
|
{
|
|
if ($schoolYear === '' || ! $this->db->tableExists('teacher_class')) {
|
|
return [];
|
|
}
|
|
|
|
$rows = $this->db->table('teacher_class tc')
|
|
->select('tc.teacher_id, tc.position, cs.class_section_name')
|
|
->join('classSection cs', 'cs.class_section_id = tc.class_section_id', 'left')
|
|
->where('tc.school_year', $schoolYear)
|
|
->get()
|
|
->getResultArray();
|
|
|
|
$assignments = [];
|
|
foreach ($rows as $row) {
|
|
$teacherId = (int) ($row['teacher_id'] ?? 0);
|
|
$name = trim((string) ($row['class_section_name'] ?? ''));
|
|
$position = strtolower((string) ($row['position'] ?? ''));
|
|
|
|
if ($teacherId <= 0 || $name === '' || ! in_array($position, ['main', 'ta'], true)) {
|
|
continue;
|
|
}
|
|
|
|
$assignments[$teacherId][] = $name . ' (' . $position . ')';
|
|
}
|
|
|
|
return $assignments;
|
|
}
|
|
|
|
private function generateUniqueStaffEmail(string $firstname, string $lastname, int $userId): string
|
|
{
|
|
$first = strtolower((string) preg_replace('/[^a-z]/i', '', $firstname));
|
|
$last = strtolower((string) preg_replace('/[^a-z]/i', '', $lastname));
|
|
|
|
if ($last === '') {
|
|
$last = 'user' . $userId;
|
|
}
|
|
|
|
$base = $last;
|
|
$max = max(1, strlen($first));
|
|
for ($i = 1; $i <= $max; $i++) {
|
|
$base = substr($first, 0, $i) . $last;
|
|
$email = $base . self::WORK_EMAIL_DOMAIN;
|
|
|
|
$exists = $this->staffModel
|
|
->where('email', $email)
|
|
->where('user_id !=', $userId)
|
|
->first();
|
|
|
|
if (! $exists) {
|
|
return $email;
|
|
}
|
|
}
|
|
|
|
return $base . $userId . self::WORK_EMAIL_DOMAIN;
|
|
}
|
|
|
|
private function hasField(string $table, string $field): bool
|
|
{
|
|
try {
|
|
return $this->db->tableExists($table) && $this->db->fieldExists($field, $table);
|
|
} catch (\Throwable) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|