- Add audit-logging-plan.md with comprehensive logging implementation guide - Update AGENTS.md with project guidelines - Refactor test models: remove RefTHoldModel, RefVSetModel, TestDefTechModel - Update TestDefSiteModel and related migrations - Update seeder and test data files - Update API documentation (OpenAPI specs)
329 lines
7.4 KiB
Markdown
329 lines
7.4 KiB
Markdown
# 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
|
|
|
|
### Running Tests
|
|
```bash
|
|
# 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
|
|
```
|
|
|
|
### CodeIgniter CLI Commands
|
|
```bash
|
|
# Run spark commands
|
|
php spark <command>
|
|
|
|
# Generate migration
|
|
php spark make:migration <name>
|
|
|
|
# Generate model
|
|
php spark make:model <name>
|
|
|
|
# Generate controller
|
|
php spark make:controller <name>
|
|
|
|
# Generate seeder
|
|
php spark make:seeder <name>
|
|
|
|
# Run migrations
|
|
php spark migrate
|
|
|
|
# Rollback migrations
|
|
php spark migrate:rollback
|
|
```
|
|
|
|
### Composer Commands
|
|
```bash
|
|
# Install dependencies
|
|
composer install
|
|
|
|
# Run tests via composer
|
|
composer test
|
|
|
|
# Update autoloader
|
|
composer dump-autoload
|
|
```
|
|
|
|
---
|
|
|
|
## Code Style Guidelines
|
|
|
|
### PHP Standards
|
|
- **PHP Version**: 8.1+
|
|
- **PSR-4 Autoloading**: `App\` maps to `app/`, `Config\` maps to `app/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"` |
|
|
|
|
### File Organization
|
|
```
|
|
app/
|
|
├── Config/ # Configuration files
|
|
├── Controllers/ # API controllers (grouped by feature)
|
|
│ ├── Patient/
|
|
│ ├── Organization/
|
|
│ └── Specimen/
|
|
├── Models/ # Data models
|
|
├── Filters/ # Request filters (Auth, CORS)
|
|
├── Traits/ # Reusable traits
|
|
├── Libraries/ # Custom libraries
|
|
├── Helpers/ # Helper functions
|
|
└── Database/
|
|
├── Migrations/
|
|
└── Seeds/
|
|
```
|
|
|
|
### Imports & Namespaces
|
|
- Always use fully qualified namespaces at the top
|
|
- Group imports: Framework first, then App, then external
|
|
- Use statements must be in alphabetical order within groups
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use CodeIgniter\Controller;
|
|
use CodeIgniter\HTTP\ResponseInterface;
|
|
use App\Traits\ResponseTrait;
|
|
use Firebase\JWT\JWT;
|
|
```
|
|
|
|
### Controller Structure
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Traits\ResponseTrait;
|
|
|
|
class ExampleController extends BaseController
|
|
{
|
|
use ResponseTrait;
|
|
|
|
protected $model;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->model = new \App\Models\ExampleModel();
|
|
}
|
|
|
|
// GET /example
|
|
public function index()
|
|
{
|
|
// Implementation
|
|
}
|
|
|
|
// POST /example
|
|
public function create()
|
|
{
|
|
// Implementation
|
|
}
|
|
}
|
|
```
|
|
|
|
### Response Format
|
|
All API responses must use the standardized format:
|
|
|
|
```php
|
|
// Success response
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Operation completed',
|
|
'data' => $data
|
|
], 200);
|
|
|
|
// Error response
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'message' => 'Error description',
|
|
'data' => []
|
|
], 400);
|
|
```
|
|
|
|
### Error Handling
|
|
- Use try-catch for JWT operations and external calls
|
|
- Return structured error responses with appropriate HTTP status codes
|
|
- Log errors using CodeIgniter's logging: `log_message('error', $message)`
|
|
|
|
```php
|
|
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's Query Builder or Model methods
|
|
- Prefer parameterized queries over raw SQL
|
|
- Use transactions for multi-table operations
|
|
|
|
```php
|
|
$this->db->transStart();
|
|
// ... database operations
|
|
$this->db->transComplete();
|
|
|
|
if ($this->db->transStatus() === false) {
|
|
return $this->respond(['status' => 'error', 'message' => 'Transaction failed'], 500);
|
|
}
|
|
```
|
|
|
|
### Testing Guidelines
|
|
|
|
#### Test Structure
|
|
```php
|
|
<?php
|
|
|
|
namespace Tests\Feature\Patients;
|
|
|
|
use CodeIgniter\Test\FeatureTestTrait;
|
|
use CodeIgniter\Test\CIUnitTestCase;
|
|
|
|
class PatientCreateTest extends CIUnitTestCase
|
|
{
|
|
use FeatureTestTrait;
|
|
|
|
protected $endpoint = 'api/patient';
|
|
|
|
public function testCreatePatientSuccess()
|
|
{
|
|
$payload = [...];
|
|
$result = $this->withBodyFormat('json')
|
|
->post($this->endpoint, $payload);
|
|
|
|
$result->assertStatus(201);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Test Naming
|
|
- Use descriptive method names: `test<Action><Scenario><ExpectedResult>`
|
|
- Example: `testCreatePatientValidationFail`, `testCreatePatientSuccess`
|
|
|
|
#### Test Status Codes
|
|
- 200: Success (GET, PATCH)
|
|
- 201: Created (POST)
|
|
- 400: Validation Error
|
|
- 401: Unauthorized
|
|
- 404: Not Found
|
|
- 500: Server Error
|
|
|
|
### API Design
|
|
- **Base URL**: `/api/`
|
|
- **Authentication**: JWT token via HttpOnly cookie
|
|
- **Content-Type**: `application/json`
|
|
- **HTTP Methods**:
|
|
- `GET` - Read
|
|
- `POST` - Create
|
|
- `PATCH` - Update (partial)
|
|
- `DELETE` - Delete
|
|
|
|
### Routes Pattern
|
|
```php
|
|
$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 Guidelines
|
|
- Always use the `auth` filter for protected routes
|
|
- Sanitize all user inputs
|
|
- Use parameterized queries to prevent SQL injection
|
|
- Store JWT secret in `.env` file
|
|
- Never commit `.env` files
|
|
|
|
---
|
|
|
|
## Project-Specific Conventions
|
|
|
|
### Legacy Field Naming
|
|
Database uses PascalCase for column names (legacy convention):
|
|
- `PatientID`, `NameFirst`, `NameLast`
|
|
- `Birthdate`, `CreatedAt`, `UpdatedAt`
|
|
|
|
### ValueSet System
|
|
Use the `App\Libraries\Lookups` class for static dropdown values:
|
|
```php
|
|
use App\Libraries\Lookups;
|
|
|
|
$genders = Lookups::get('gender');
|
|
$options = Lookups::getOptions('gender');
|
|
```
|
|
|
|
### Models
|
|
Extend `BaseModel` for automatic UTC date handling:
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
class PatientModel extends BaseModel
|
|
{
|
|
protected $table = 'patients';
|
|
protected $primaryKey = 'PatientID';
|
|
protected $allowedFields = ['NameFirst', 'NameLast', ...];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Environment Configuration
|
|
|
|
### Database (`.env`)
|
|
```ini
|
|
database.default.hostname = localhost
|
|
database.default.database = clqms01
|
|
database.default.username = root
|
|
database.default.password = adminsakti
|
|
database.default.DBDriver = MySQLi
|
|
```
|
|
|
|
### JWT Secret (`.env`)
|
|
```ini
|
|
JWT_SECRET = '5pandaNdutNdut'
|
|
```
|
|
|
|
---
|
|
|
|
## Additional Notes
|
|
|
|
- **API-Only**: No view layer - this is a 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.*
|