2026-01-12 16:53:41 +07:00
|
|
|
# CLQMS Backend - Agent Instructions
|
|
|
|
|
|
|
|
|
|
**Project:** Clinical Laboratory Quality Management System (CLQMS) Backend
|
|
|
|
|
**Framework:** CodeIgniter 4 (PHP 8.1+)
|
|
|
|
|
**Platform:** Windows - Use PowerShell or CMD for terminal commands
|
|
|
|
|
**Frontend:** Alpine.js (views/v2 directory)
|
|
|
|
|
|
|
|
|
|
## Build / Test Commands
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Install dependencies
|
|
|
|
|
composer install
|
|
|
|
|
|
|
|
|
|
# Run all tests
|
|
|
|
|
composer test
|
|
|
|
|
php vendor/bin/phpunit
|
|
|
|
|
|
|
|
|
|
# Run single test file
|
|
|
|
|
php vendor/bin/phpunit tests/feature/Patients/PatientIndexTest.php
|
|
|
|
|
|
|
|
|
|
# Run single test method
|
|
|
|
|
php vendor/bin/phpunit tests/feature/Patients/PatientIndexTest.php --filter=testIndexWithoutParams
|
|
|
|
|
|
|
|
|
|
# Run tests with coverage
|
|
|
|
|
php vendor/bin/phpunit --coverage-html build/logs/html
|
|
|
|
|
|
|
|
|
|
# Run tests in verbose mode
|
|
|
|
|
php vendor/bin/phpunit --verbose
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Test Structure:**
|
|
|
|
|
- Feature tests: `tests/feature/` - API endpoint testing with `FeatureTestTrait`
|
|
|
|
|
- Unit tests: `tests/unit/` - Model/Logic testing
|
|
|
|
|
- Base test case: `Tests\Support\v2\MasterTestCase.php` - Provides JWT auth and helper methods
|
|
|
|
|
|
|
|
|
|
## Code Style Guidelines
|
|
|
|
|
|
|
|
|
|
### PHP Standards
|
|
|
|
|
- **PHP Version:** 8.1 minimum
|
|
|
|
|
- **PSR-4 Autoloading:** Follow namespace-to-path conventions (`App\Controllers\*`, `App\Models\*`)
|
|
|
|
|
- **Line endings:** Unix-style (LF) - configure editor accordingly
|
|
|
|
|
|
|
|
|
|
### Naming Conventions
|
|
|
|
|
| Element | Convention | Examples |
|
|
|
|
|
|---------|------------|----------|
|
|
|
|
|
| Classes | PascalCase | `PatientController`, `BaseModel` |
|
|
|
|
|
| Methods | camelCase | `getPatient()`, `createPatient()` |
|
|
|
|
|
| Variables | camelCase | `$internalPID`, `$patientData` |
|
|
|
|
|
| Constants | UPPER_SNAKE_CASE | `ORDER_PRIORITY`, `TEST_TYPE` |
|
|
|
|
|
| Table names | snake_case | `patient`, `pat_idt`, `valueset` |
|
|
|
|
|
| Column names | PascalCase (original DB) | `InternalPID`, `PatientID` |
|
|
|
|
|
|
|
|
|
|
### File Organization
|
|
|
|
|
```
|
|
|
|
|
app/
|
|
|
|
|
├── Controllers/{Domain}/
|
|
|
|
|
│ └── DomainController.php
|
|
|
|
|
├── Models/{Domain}/
|
|
|
|
|
│ └── DomainModel.php
|
|
|
|
|
├── Libraries/
|
refactor: consolidate migrations and reorganize valueset data structure
Major refactoring to clean up database migrations and reorganize static lookup data:
- Consolidated 13 old migrations (2025) into 10 new numbered migrations (2026-01-01)
- Deleted redundant migrations: Location, Users, Contact, ValueSet, Counter, RefRange,
CRMOrganizations, Organization, AreaGeo, DeviceLogin, EdgeRes
- New consolidated migrations:
- 2026-01-01-000001_CreateLookups: valueset, counter, containerdef, occupation, specialty
- 2026-01-01-000002_CreateOrganization: account, site, location, discipline, department
- 2026-01-01-000003_CreatePatientCore: patient, patidentifier, pataddress, patcontact
- 2026-01-01-000004_CreateSecurity: contact, contactdetail, userdevices, loginattempts
- 2026-01-01-000005_CreatePatientVisits: patvisit, patinsurance
- 2026-01-01-000006_CreateOrders: porder, orderitem
- 2026-01-01-000007_CreateSpecimens: specimen, specmenactivity, containerdef
- 2026-01-01-000008_CreateTestDefinitions: testdefinition, testactivity, refnum, reftxt
- 2026-01-01-000009_CreateResults: patresult, patresultdetail, patresultcomment
- 2026-01-01-000010_CreateLabInfrastructure: edgeres, edgestatus, edgeack, workstation
- Moved 44 JSON files from valuesets/ subdirectory to app/Libraries/Data/ root
- Added new country.json lookup
- Added _meta.json for valueset metadata
- Deleted old valuesets/_meta.json
- Renamed gender.json to sex.json for consistency with patient.Sex column
- Removed duplicate country.json from valuesets/
- AGENTS.md: Updated Lookups library documentation with new methods
- README.md: Complete rewrite of lookup/valueset documentation
- Renamed MVP_TODO.md to TODO.md
- Added VUE_SPA_IMPLEMENTATION_PLAN.md
- Removed deprecated prj_clinical laboratory quality management system_3a.docx
- ValueSet.php: Enhanced with caching and new lookup methods
- Lookups.php: Removed (functionality merged into ValueSet)
Impact: Prepares codebase for 2026 with cleaner migration history and improved
lookup data organization for the name-based valueset system.
2026-01-13 07:22:25 +07:00
|
|
|
│ └── ValueSet.php # Base lookup class (loads from JSON)
|
|
|
|
|
│ └── Lookups.php # Extends ValueSet - use this for lookups
|
2026-01-12 16:53:41 +07:00
|
|
|
└── Views/v2/
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Imports and Namespaces
|
|
|
|
|
```php
|
|
|
|
|
<?php
|
|
|
|
|
namespace App\Controllers\Patient;
|
|
|
|
|
|
|
|
|
|
use CodeIgniter\Controller;
|
|
|
|
|
use CodeIgniter\API\ResponseTrait;
|
|
|
|
|
use App\Models\Patient\PatientModel;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- Use fully qualified class names or `use` statements
|
|
|
|
|
- Group imports logically
|
|
|
|
|
- Avoid unnecessary aliases
|
|
|
|
|
|
|
|
|
|
### Code Formatting
|
|
|
|
|
- **Indentation:** 4 spaces (not tabs)
|
|
|
|
|
- **Braces:** Allman style for classes/functions, K&R for control structures
|
|
|
|
|
- **Line length:** Soft limit 120 characters
|
|
|
|
|
- **Empty lines:** Single blank line between method definitions and logical groups
|
|
|
|
|
|
|
|
|
|
### Type Hints and Return Types
|
|
|
|
|
```php
|
|
|
|
|
// Required for new code
|
|
|
|
|
public function getPatient(int $internalPID): ?array
|
|
|
|
|
protected function createPatient(array $input): int
|
|
|
|
|
private function checkDbError(object $db, string $context): void
|
|
|
|
|
|
|
|
|
|
// Use nullable types for optional returns
|
|
|
|
|
public function findById(?int $id): ?array
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Controller Patterns
|
|
|
|
|
```php
|
|
|
|
|
class PatientController extends Controller {
|
|
|
|
|
use ResponseTrait;
|
|
|
|
|
|
|
|
|
|
protected $db;
|
|
|
|
|
protected $model;
|
|
|
|
|
protected $rules;
|
|
|
|
|
|
|
|
|
|
public function __construct() {
|
|
|
|
|
$this->db = \Config\Database::connect();
|
|
|
|
|
$this->model = new PatientModel();
|
|
|
|
|
$this->rules = [...]; // Validation rules
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function index() {
|
|
|
|
|
try {
|
|
|
|
|
$data = $this->model->findAll();
|
|
|
|
|
return $this->respond([...], 200);
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
return $this->failServerError($e->getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Model Patterns
|
|
|
|
|
```php
|
|
|
|
|
class PatientModel extends BaseModel {
|
|
|
|
|
protected $table = 'patient';
|
|
|
|
|
protected $primaryKey = 'InternalPID';
|
|
|
|
|
protected $allowedFields = [...];
|
|
|
|
|
protected $useSoftDeletes = true;
|
|
|
|
|
protected $deletedField = 'DelDate';
|
|
|
|
|
|
|
|
|
|
public function getPatients(array $filters = []): array {
|
|
|
|
|
// Query builder chain
|
|
|
|
|
$this->select('...');
|
|
|
|
|
$this->join(...);
|
|
|
|
|
if (!empty($filters['key'])) {
|
|
|
|
|
$this->where('key', $filters['key']);
|
|
|
|
|
}
|
|
|
|
|
return $this->findAll();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Error Handling
|
|
|
|
|
- Controllers: Use try-catch with `failServerError()`, `failValidationErrors()`, `failNotFound()`
|
|
|
|
|
- Models: Throw `\Exception` with descriptive messages
|
|
|
|
|
- Database errors: Check `$db->error()` after operations
|
|
|
|
|
- Always validate input before DB operations
|
|
|
|
|
|
|
|
|
|
### Validation Rules
|
|
|
|
|
```php
|
|
|
|
|
protected $rules = [
|
|
|
|
|
'PatientID' => 'required|regex_match[/^[A-Za-z0-9]+$/]|max_length[30]',
|
|
|
|
|
'EmailAddress' => 'permit_empty|valid_email|max_length[100]',
|
|
|
|
|
'Phone' => 'permit_empty|regex_match[/^\+?[0-9]{8,15}$/]',
|
|
|
|
|
];
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Date Handling
|
|
|
|
|
- All dates stored/retrieved in UTC via `BaseModel` callbacks
|
|
|
|
|
- Use `utc` helper functions: `convert_array_to_utc()`, `convert_array_to_utc_iso()`
|
|
|
|
|
- Format: ISO 8601 (`Y-m-d\TH:i:s\Z`) for API responses
|
|
|
|
|
|
|
|
|
|
### API Response Format
|
|
|
|
|
```php
|
|
|
|
|
// Success
|
|
|
|
|
return $this->respond([
|
|
|
|
|
'status' => 'success',
|
|
|
|
|
'message' => 'Data fetched successfully',
|
|
|
|
|
'data' => $rows
|
|
|
|
|
], 200);
|
|
|
|
|
|
|
|
|
|
// Created
|
|
|
|
|
return $this->respondCreated([
|
|
|
|
|
'status' => 'success',
|
|
|
|
|
'message' => 'Record created'
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Error
|
|
|
|
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Database Transactions
|
|
|
|
|
```php
|
|
|
|
|
$db->transBegin();
|
|
|
|
|
try {
|
|
|
|
|
$this->insert($data);
|
|
|
|
|
$this->checkDbError($db, 'Insert operation');
|
|
|
|
|
$db->transCommit();
|
|
|
|
|
return $insertId;
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$db->transRollback();
|
|
|
|
|
throw $e;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Frontend Integration (Alpine.js)
|
|
|
|
|
- API calls use `BASEURL` global variable
|
|
|
|
|
- Include `credentials: 'include'` for authenticated requests
|
|
|
|
|
- Modals use `x-show` with `@click.self` backdrop close
|
|
|
|
|
|
|
|
|
|
### Lookups Library
|
refactor: consolidate migrations and reorganize valueset data structure
Major refactoring to clean up database migrations and reorganize static lookup data:
- Consolidated 13 old migrations (2025) into 10 new numbered migrations (2026-01-01)
- Deleted redundant migrations: Location, Users, Contact, ValueSet, Counter, RefRange,
CRMOrganizations, Organization, AreaGeo, DeviceLogin, EdgeRes
- New consolidated migrations:
- 2026-01-01-000001_CreateLookups: valueset, counter, containerdef, occupation, specialty
- 2026-01-01-000002_CreateOrganization: account, site, location, discipline, department
- 2026-01-01-000003_CreatePatientCore: patient, patidentifier, pataddress, patcontact
- 2026-01-01-000004_CreateSecurity: contact, contactdetail, userdevices, loginattempts
- 2026-01-01-000005_CreatePatientVisits: patvisit, patinsurance
- 2026-01-01-000006_CreateOrders: porder, orderitem
- 2026-01-01-000007_CreateSpecimens: specimen, specmenactivity, containerdef
- 2026-01-01-000008_CreateTestDefinitions: testdefinition, testactivity, refnum, reftxt
- 2026-01-01-000009_CreateResults: patresult, patresultdetail, patresultcomment
- 2026-01-01-000010_CreateLabInfrastructure: edgeres, edgestatus, edgeack, workstation
- Moved 44 JSON files from valuesets/ subdirectory to app/Libraries/Data/ root
- Added new country.json lookup
- Added _meta.json for valueset metadata
- Deleted old valuesets/_meta.json
- Renamed gender.json to sex.json for consistency with patient.Sex column
- Removed duplicate country.json from valuesets/
- AGENTS.md: Updated Lookups library documentation with new methods
- README.md: Complete rewrite of lookup/valueset documentation
- Renamed MVP_TODO.md to TODO.md
- Added VUE_SPA_IMPLEMENTATION_PLAN.md
- Removed deprecated prj_clinical laboratory quality management system_3a.docx
- ValueSet.php: Enhanced with caching and new lookup methods
- Lookups.php: Removed (functionality merged into ValueSet)
Impact: Prepares codebase for 2026 with cleaner migration history and improved
lookup data organization for the name-based valueset system.
2026-01-13 07:22:25 +07:00
|
|
|
Use `App\Libraries\Lookups` (extends `ValueSet`) for all static lookup values. Data is loaded from JSON files in `app/Libraries/Data/valuesets/`:
|
2026-01-12 16:53:41 +07:00
|
|
|
```php
|
|
|
|
|
use App\Libraries\Lookups;
|
|
|
|
|
|
refactor: consolidate migrations and reorganize valueset data structure
Major refactoring to clean up database migrations and reorganize static lookup data:
- Consolidated 13 old migrations (2025) into 10 new numbered migrations (2026-01-01)
- Deleted redundant migrations: Location, Users, Contact, ValueSet, Counter, RefRange,
CRMOrganizations, Organization, AreaGeo, DeviceLogin, EdgeRes
- New consolidated migrations:
- 2026-01-01-000001_CreateLookups: valueset, counter, containerdef, occupation, specialty
- 2026-01-01-000002_CreateOrganization: account, site, location, discipline, department
- 2026-01-01-000003_CreatePatientCore: patient, patidentifier, pataddress, patcontact
- 2026-01-01-000004_CreateSecurity: contact, contactdetail, userdevices, loginattempts
- 2026-01-01-000005_CreatePatientVisits: patvisit, patinsurance
- 2026-01-01-000006_CreateOrders: porder, orderitem
- 2026-01-01-000007_CreateSpecimens: specimen, specmenactivity, containerdef
- 2026-01-01-000008_CreateTestDefinitions: testdefinition, testactivity, refnum, reftxt
- 2026-01-01-000009_CreateResults: patresult, patresultdetail, patresultcomment
- 2026-01-01-000010_CreateLabInfrastructure: edgeres, edgestatus, edgeack, workstation
- Moved 44 JSON files from valuesets/ subdirectory to app/Libraries/Data/ root
- Added new country.json lookup
- Added _meta.json for valueset metadata
- Deleted old valuesets/_meta.json
- Renamed gender.json to sex.json for consistency with patient.Sex column
- Removed duplicate country.json from valuesets/
- AGENTS.md: Updated Lookups library documentation with new methods
- README.md: Complete rewrite of lookup/valueset documentation
- Renamed MVP_TODO.md to TODO.md
- Added VUE_SPA_IMPLEMENTATION_PLAN.md
- Removed deprecated prj_clinical laboratory quality management system_3a.docx
- ValueSet.php: Enhanced with caching and new lookup methods
- Lookups.php: Removed (functionality merged into ValueSet)
Impact: Prepares codebase for 2026 with cleaner migration history and improved
lookup data organization for the name-based valueset system.
2026-01-13 07:22:25 +07:00
|
|
|
// Get formatted for frontend dropdowns [{value: 'X', label: 'Y'}, ...]
|
|
|
|
|
$gender = Lookups::get('gender');
|
|
|
|
|
$priorities = Lookups::get('order_priority');
|
2026-01-12 16:53:41 +07:00
|
|
|
|
refactor: consolidate migrations and reorganize valueset data structure
Major refactoring to clean up database migrations and reorganize static lookup data:
- Consolidated 13 old migrations (2025) into 10 new numbered migrations (2026-01-01)
- Deleted redundant migrations: Location, Users, Contact, ValueSet, Counter, RefRange,
CRMOrganizations, Organization, AreaGeo, DeviceLogin, EdgeRes
- New consolidated migrations:
- 2026-01-01-000001_CreateLookups: valueset, counter, containerdef, occupation, specialty
- 2026-01-01-000002_CreateOrganization: account, site, location, discipline, department
- 2026-01-01-000003_CreatePatientCore: patient, patidentifier, pataddress, patcontact
- 2026-01-01-000004_CreateSecurity: contact, contactdetail, userdevices, loginattempts
- 2026-01-01-000005_CreatePatientVisits: patvisit, patinsurance
- 2026-01-01-000006_CreateOrders: porder, orderitem
- 2026-01-01-000007_CreateSpecimens: specimen, specmenactivity, containerdef
- 2026-01-01-000008_CreateTestDefinitions: testdefinition, testactivity, refnum, reftxt
- 2026-01-01-000009_CreateResults: patresult, patresultdetail, patresultcomment
- 2026-01-01-000010_CreateLabInfrastructure: edgeres, edgestatus, edgeack, workstation
- Moved 44 JSON files from valuesets/ subdirectory to app/Libraries/Data/ root
- Added new country.json lookup
- Added _meta.json for valueset metadata
- Deleted old valuesets/_meta.json
- Renamed gender.json to sex.json for consistency with patient.Sex column
- Removed duplicate country.json from valuesets/
- AGENTS.md: Updated Lookups library documentation with new methods
- README.md: Complete rewrite of lookup/valueset documentation
- Renamed MVP_TODO.md to TODO.md
- Added VUE_SPA_IMPLEMENTATION_PLAN.md
- Removed deprecated prj_clinical laboratory quality management system_3a.docx
- ValueSet.php: Enhanced with caching and new lookup methods
- Lookups.php: Removed (functionality merged into ValueSet)
Impact: Prepares codebase for 2026 with cleaner migration history and improved
lookup data organization for the name-based valueset system.
2026-01-13 07:22:25 +07:00
|
|
|
// Get raw data [{key: 'X', value: 'Y'}, ...]
|
|
|
|
|
$raw = Lookups::getRaw('gender');
|
2026-01-12 16:53:41 +07:00
|
|
|
|
refactor: consolidate migrations and reorganize valueset data structure
Major refactoring to clean up database migrations and reorganize static lookup data:
- Consolidated 13 old migrations (2025) into 10 new numbered migrations (2026-01-01)
- Deleted redundant migrations: Location, Users, Contact, ValueSet, Counter, RefRange,
CRMOrganizations, Organization, AreaGeo, DeviceLogin, EdgeRes
- New consolidated migrations:
- 2026-01-01-000001_CreateLookups: valueset, counter, containerdef, occupation, specialty
- 2026-01-01-000002_CreateOrganization: account, site, location, discipline, department
- 2026-01-01-000003_CreatePatientCore: patient, patidentifier, pataddress, patcontact
- 2026-01-01-000004_CreateSecurity: contact, contactdetail, userdevices, loginattempts
- 2026-01-01-000005_CreatePatientVisits: patvisit, patinsurance
- 2026-01-01-000006_CreateOrders: porder, orderitem
- 2026-01-01-000007_CreateSpecimens: specimen, specmenactivity, containerdef
- 2026-01-01-000008_CreateTestDefinitions: testdefinition, testactivity, refnum, reftxt
- 2026-01-01-000009_CreateResults: patresult, patresultdetail, patresultcomment
- 2026-01-01-000010_CreateLabInfrastructure: edgeres, edgestatus, edgeack, workstation
- Moved 44 JSON files from valuesets/ subdirectory to app/Libraries/Data/ root
- Added new country.json lookup
- Added _meta.json for valueset metadata
- Deleted old valuesets/_meta.json
- Renamed gender.json to sex.json for consistency with patient.Sex column
- Removed duplicate country.json from valuesets/
- AGENTS.md: Updated Lookups library documentation with new methods
- README.md: Complete rewrite of lookup/valueset documentation
- Renamed MVP_TODO.md to TODO.md
- Added VUE_SPA_IMPLEMENTATION_PLAN.md
- Removed deprecated prj_clinical laboratory quality management system_3a.docx
- ValueSet.php: Enhanced with caching and new lookup methods
- Lookups.php: Removed (functionality merged into ValueSet)
Impact: Prepares codebase for 2026 with cleaner migration history and improved
lookup data organization for the name-based valueset system.
2026-01-13 07:22:25 +07:00
|
|
|
// Get single label by key
|
|
|
|
|
$label = Lookups::getLabel('gender', '1'); // Returns 'Female'
|
|
|
|
|
|
|
|
|
|
// Get options with key/value pairs
|
|
|
|
|
$options = Lookups::getOptions('gender');
|
|
|
|
|
// Returns: [['key' => '1', 'value' => 'Female'], ...]
|
|
|
|
|
|
|
|
|
|
// Transform data with lookup labels
|
|
|
|
|
$patients = Lookups::transformLabels($patients, [
|
|
|
|
|
'Sex' => 'gender',
|
|
|
|
|
'Priority' => 'order_priority'
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Clear cache after data changes
|
|
|
|
|
Lookups::clearCache();
|
2026-01-12 16:53:41 +07:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Important Notes
|
|
|
|
|
- **Soft deletes:** Use `DelDate` field instead of hard delete
|
|
|
|
|
- **UTC timezone:** All dates normalized to UTC automatically
|
|
|
|
|
- **JWT auth:** API endpoints require Bearer token in `Authorization` header
|
|
|
|
|
- **No comments:** Do not add comments unless explicitly requested
|