Major Features: - Add comprehensive audit logging system with AuditService - Create AuditLogs database migration for tracking changes - Implement TestValidationService for test data validation - Add FRONTEND_TEST_MANAGEMENT_PROMPT.md documentation Controllers: - Update TestsController with improved test management Models: - Enhance PatientModel with additional functionality - Update TestDefSiteModel for better site management Database: - Add CreateAuditLogs migration (2026-02-20-000011) - Update TestSeeder with new test data Services: - Add AuditService for comprehensive audit trail logging Documentation: - Update AGENTS.md with improved guidelines - Update audit-logging-plan.md with implementation details - Add FRONTEND_TEST_MANAGEMENT_PROMPT.md for frontend guidance API Documentation: - Update api-docs.bundled.yaml - Update tests.yaml schema definitions - Update tests.yaml paths Testing: - Enhance TestsControllerTest with new test cases - Update TestDefModelsTest for model coverage
6.8 KiB
6.8 KiB
AGENTS.md - Code Guidelines for CLQMS
CLQMS (Clinical Laboratory Quality Management System) - A headless REST API backend for clinical laboratory workflows built with CodeIgniter 4.
Build, Test & Lint Commands
# Run all tests
./vendor/bin/phpunit
# Run a specific test file
./vendor/bin/phpunit tests/feature/Patients/PatientCreateTest.php
# Run a specific test method
./vendor/bin/phpunit --filter testCreatePatientSuccess tests/feature/Patients/PatientCreateTest.php
# Run tests with coverage
./vendor/bin/phpunit --coverage-html build/logs/html
# Run tests by suite
./vendor/bin/phpunit --testsuite App
# Generate scaffolding
php spark make:migration <name>
php spark make:model <name>
php spark make:controller <name>
# Database migrations
php spark migrate
php spark migrate:rollback
Code Style Guidelines
PHP Standards
- PHP Version: 8.1+
- PSR-4 Autoloading:
App\maps toapp/,Config\maps toapp/Config/ - PSR-12 Coding Style (follow where applicable)
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Classes | PascalCase | PatientController |
| Methods | camelCase | createPatient() |
| Properties | snake_case (legacy) / camelCase (new) | $patient_id / $patientId |
| Constants | UPPER_SNAKE_CASE | MAX_RETRY_COUNT |
| Tables | snake_case | patient_visits |
| Columns | PascalCase (legacy) | PatientID, NameFirst |
| JSON fields | PascalCase | "PatientID": "123" |
Imports & Namespaces
- Fully qualified namespaces at the top
- Group imports: Framework first, then App, then external
- Alphabetical order within groups
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\ResponseInterface;
use App\Traits\ResponseTrait;
use Firebase\JWT\JWT;
Controller Structure
<?php
namespace App\Controllers;
use App\Traits\ResponseTrait;
class ExampleController extends Controller
{
use ResponseTrait;
protected $model;
protected $db;
public function __construct()
{
$this->db = \Config\Database::connect();
$this->model = new \App\Models\ExampleModel();
}
public function index() { /* ... */ }
public function create() { /* ... */ }
}
Response Format
All API responses use standardized format:
// Success
return $this->respond([
'status' => 'success',
'message' => 'Operation completed',
'data' => $data
], 200);
// Error
return $this->respond([
'status' => 'failed',
'message' => 'Error description',
'data' => []
], 400);
Note: Custom ResponseTrait automatically converts empty strings to null.
Error Handling
- Use try-catch for JWT and external calls
- Log errors:
log_message('error', $message) - Return structured error responses with appropriate HTTP status codes
try {
$decoded = JWT::decode($token, new Key($key, 'HS256'));
} catch (\Firebase\JWT\ExpiredException $e) {
return $this->respond(['status' => 'failed', 'message' => 'Token expired'], 401);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'message' => 'Invalid token'], 401);
}
Database Operations
- Use CodeIgniter Query Builder or Model methods
- Use
helper('utc')for UTC date conversion - Wrap multi-table operations in transactions
$this->db->transStart();
// ... database operations
$this->db->transComplete();
if ($this->db->transStatus() === false) {
return $this->respond(['status' => 'error', 'message' => 'Transaction failed'], 500);
}
Model Patterns
- Extend
BaseModelfor automatic UTC date handling - Use
checkDbError()for database error detection
<?php
namespace App\Models;
class PatientModel extends BaseModel
{
protected $table = 'patients';
protected $primaryKey = 'PatientID';
protected $allowedFields = ['NameFirst', 'NameLast', ...];
private function checkDbError($db, string $context) {
$error = $db->error();
if (!empty($error['code'])) {
throw new \Exception("{$context} failed: {$error['code']} - {$error['message']}");
}
}
}
Testing Guidelines
<?php
namespace Tests\Feature\Patients;
use CodeIgniter\Test\FeatureTestTrait;
use CodeIgniter\Test\CIUnitTestCase;
use Faker\Factory;
class PatientCreateTest extends CIUnitTestCase
{
use FeatureTestTrait;
protected $endpoint = 'api/patient';
public function testCreatePatientSuccess()
{
$faker = Factory::create('id_ID');
$payload = [...];
$result = $this->withBodyFormat('json')->post($this->endpoint, $payload);
$result->assertStatus(201);
}
}
Test Naming: test<Action><Scenario><ExpectedResult> (e.g., testCreatePatientValidationFail)
Test Status Codes: 200 (GET/PATCH), 201 (POST), 400 (Validation), 401 (Unauthorized), 404 (Not Found), 500 (Server Error)
API Design
- Base URL:
/api/ - Authentication: JWT token via HttpOnly cookie
- Content-Type:
application/json - Methods: GET (read), POST (create), PATCH (partial update), DELETE (delete)
Routes Pattern
$routes->group('api/patient', function ($routes) {
$routes->get('/', 'Patient\PatientController::index');
$routes->post('/', 'Patient\PatientController::create');
$routes->get('(:num)', 'Patient\PatientController::show/$1');
$routes->patch('/', 'Patient\PatientController::update');
$routes->delete('/', 'Patient\PatientController::delete');
});
Security
- Use
authfilter for protected routes - Sanitize user inputs
- Use parameterized queries
- Store secrets in
.env, never commit
Project-Specific Conventions
Legacy Field Naming
Database uses PascalCase columns: PatientID, NameFirst, Birthdate, CreatedAt, UpdatedAt
ValueSet System
use App\Libraries\Lookups;
$genders = Lookups::get('gender');
$label = Lookups::getLabel('gender', '1'); // Returns 'Female'
$options = Lookups::getOptions('gender');
$labeled = Lookups::transformLabels($data, ['Sex' => 'gender']);
Nested Data Handling
For entities with nested data (PatIdt, PatCom, PatAtt):
- Extract nested arrays before filtering
- Use transactions for multi-table operations
- Handle empty/null arrays appropriately
Environment Configuration
Database (.env)
database.default.hostname = localhost
database.default.database = clqms01
database.default.username = root
database.default.password = adminsakti
database.default.DBDriver = MySQLi
JWT Secret (.env)
JWT_SECRET = '5pandaNdutNdut'
Additional Notes
- API-Only: No view layer - headless REST API
- Frontend Agnostic: Any client can consume these APIs
- Stateless: JWT-based authentication per request
- UTC Dates: All dates stored in UTC, converted for display
© 2025 5Panda Team. Engineering Precision in Clinical Diagnostics.