devsofttech_hms_ci4/app/Models/UserProfileModel.php
DevSoftTech Server a3c544a53c feat(user-management): enhance user management, permission handling, and charges invoice workflows
- Add UserProfileModel and migration for user profile data
- Enhance User Management views, controller, and doctor/nurse search integration
- Refactor handleAuthError to separate 401 session expiry from 403 access restriction
- Apply granular permission checks to sidebar navigation links
- Open Bed Status view for all authenticated users
- Support GET and POST on charges item routes and exempt billing charges from CSRF desync
2026-10-14 20:08:10 +05:30

213 lines
6.7 KiB
PHP

<?php
namespace App\Models;
use CodeIgniter\Model;
class UserProfileModel extends Model
{
protected $table = 'user_profiles';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [
'user_id',
'full_name',
'phone_no',
'doctor_id',
'nurse_id',
'department',
'designation',
'employee_code',
'avatar',
'created_at',
'updated_at',
];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
/**
* Get profile for a specific user.
*/
public function getProfile(int $userId): ?array
{
if ($userId <= 0) {
return null;
}
return $this->where('user_id', $userId)->first();
}
/**
* Save or update user profile.
*/
public function saveProfile(int $userId, array $data): bool
{
if ($userId <= 0) {
return false;
}
$existing = $this->where('user_id', $userId)->first();
$fields = [
'full_name' => isset($data['full_name']) ? trim((string) $data['full_name']) : null,
'phone_no' => isset($data['phone_no']) ? trim((string) $data['phone_no']) : null,
'doctor_id' => ! empty($data['doctor_id']) ? (int) $data['doctor_id'] : null,
'nurse_id' => ! empty($data['nurse_id']) ? (int) $data['nurse_id'] : null,
'department' => isset($data['department']) ? trim((string) $data['department']) : null,
'designation' => isset($data['designation']) ? trim((string) $data['designation']) : null,
'employee_code' => isset($data['employee_code']) ? trim((string) $data['employee_code']) : null,
];
if (isset($data['avatar'])) {
$fields['avatar'] = trim((string) $data['avatar']);
}
if ($existing) {
return (bool) $this->update($existing['id'], $fields);
}
$fields['user_id'] = $userId;
return (bool) $this->insert($fields);
}
/**
* Delete profile for a specific user.
*/
public function deleteProfile(int $userId): bool
{
if ($userId <= 0) {
return false;
}
return (bool) $this->where('user_id', $userId)->delete();
}
/**
* Get users with full profile, clinical link, and identity information.
*/
public function getUsersList(array $filters = []): array
{
$builder = $this->db->table('users u')
->select('u.id, u.username, u.active, u.status, u.created_at, u.deleted_at')
->select('p.full_name, p.phone_no, p.doctor_id, p.nurse_id, p.department, p.designation, p.employee_code, p.avatar')
->select('ai.secret as email, ai.force_reset')
->select('(CASE WHEN p.doctor_id IS NOT NULL AND d.id IS NOT NULL THEN TRIM(CONCAT(COALESCE(d.p_title, "Dr."), " ", COALESCE(d.p_fname, ""), " ", COALESCE(d.p_lname, ""))) ELSE NULL END) as linked_doctor_name')
->select('d.doctor_reg_no as linked_doctor_reg')
->select('(CASE WHEN p.nurse_id IS NOT NULL AND n.id IS NOT NULL THEN n.name ELSE NULL END) as linked_nurse_name, n.nurse_code as linked_nurse_code')
->join('user_profiles p', 'p.user_id = u.id', 'left')
->join('auth_identities ai', 'ai.user_id = u.id AND ai.type = "email_password"', 'left')
->join('doctor_master d', 'd.id = p.doctor_id', 'left')
->join('nurse_master n', 'n.id = p.nurse_id', 'left');
// By default show non-deleted users
if (! empty($filters['include_deleted'])) {
// Include soft-deleted
} else {
$builder->where('u.deleted_at IS NULL');
}
if (isset($filters['active']) && $filters['active'] !== '') {
$builder->where('u.active', (int) $filters['active']);
}
if (! empty($filters['search'])) {
$s = trim((string) $filters['search']);
$builder->groupStart()
->like('u.username', $s)
->orLike('p.full_name', $s)
->orLike('p.phone_no', $s)
->orLike('ai.secret', $s)
->orLike('p.employee_code', $s)
->orLike('p.department', $s)
->groupEnd();
}
$builder->orderBy('u.id', 'ASC');
$users = $builder->get()->getResultArray();
// Attach groups/roles
$groups = $this->db->table('auth_groups_users')->get()->getResultArray();
$userGroups = [];
foreach ($groups as $g) {
$userGroups[(int) $g['user_id']][] = $g['group'];
}
foreach ($users as &$user) {
$uid = (int) $user['id'];
$user['groups'] = $userGroups[$uid] ?? [];
}
unset($user);
// Filter by role if requested
if (! empty($filters['role'])) {
$roleFilter = strtolower(trim((string) $filters['role']));
$users = array_values(array_filter($users, fn($u) => in_array($roleFilter, $u['groups'], true)));
}
return $users;
}
/**
* Get active doctors for dropdown mapping.
*/
public function getActiveDoctors(): array
{
if (! $this->db->tableExists('doctor_master')) {
return [];
}
return $this->db->table('doctor_master')
->select('id, p_title, p_fname, p_lname, email1, mphone1, doctor_reg_no, active')
->where('active', '1')
->orWhere('active', 1)
->orderBy('p_fname', 'ASC')
->get()
->getResultArray();
}
/**
* Get active nurses for dropdown mapping.
*/
public function getActiveNurses(): array
{
if (! $this->db->tableExists('nurse_master')) {
return [];
}
return $this->db->table('nurse_master')
->select('id, nurse_code, name, email, contact_no, designation, is_active')
->where('is_active', 1)
->orderBy('name', 'ASC')
->get()
->getResultArray();
}
/**
* Get user profile by linked doctor ID.
*/
public function getByDoctorId(int $doctorId): ?array
{
if ($doctorId <= 0) {
return null;
}
return $this->where('doctor_id', $doctorId)->first();
}
/**
* Get user profile by linked nurse ID.
*/
public function getByNurseId(int $nurseId): ?array
{
if ($nurseId <= 0) {
return null;
}
return $this->where('nurse_id', $nurseId)->first();
}
}