Add AGENTS.md for agent

Fix areazone edit form validation error
- Fix wrong field name in areazone_newrow.php (contactid → zoneid)
- Move POST variable assignments before validation in Areas::areazone_edit()
The user already made the edits in the previous turn. This commit message is ready to use.
This commit is contained in:
mahdahar 2026-01-13 09:19:14 +07:00
parent 4021a2a5f2
commit 35c948beb6
4 changed files with 317 additions and 33 deletions

285
AGENTS.md Normal file
View File

@ -0,0 +1,285 @@
# AGENTS.md
## Build, Lint, and Test Commands
### Running Tests
```bash
# Run all tests
composer test
./phpunit
# Run all tests (Windows)
vendor\bin\phpunit
# Run a single test file
./phpunit tests/unit/HealthTest.php
# Run a single test method
./phpunit --filter testIsDefinedAppPath
# Run tests in a specific directory
./phpunit tests/unit/
# Run tests with coverage report
./phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m
# Generate HTML coverage report
./phpunit --coverage-html build/logs/html
```
### Dependencies
```bash
# Install dependencies
composer install
# Update dependencies
composer update
```
### CodeIgniter Spark Commands
```bash
# List available spark commands
php spark list
# Clear all caches
php spark cache:clear
# Run migrations
php spark migrate
# Rollback migrations
php spark migrate:rollback
```
## Code Style Guidelines
### General Principles
- Write clean, readable code without unnecessary comments
- Follow CodeIgniter 4 conventions and PSR-12 PHP standards
- Keep methods focused and under 100 lines when possible
- Avoid deep nesting (max 3-4 levels)
### Naming Conventions
| Element | Convention | Examples |
|---------|------------|----------|
| Classes | PascalCase | `ProductsModel`, `BaseController`, `UsersLogModel` |
| Files | Match class name | `ProductsModel.php`, `BaseController.php` |
| Methods | camelCase | `getProductAlias()`, `productslog_edit()` |
| Variables | camelCase | `$productid`, `$data`, `$results` |
| Properties | snake_case | `$table`, `$primaryKey`, `$allowedFields` |
| Constants | UPPER_SNAKE_CASE | `APPPATH`, `HOMEPATH` |
| Namespaces | PascalCase | `App\Controllers`, `App\Models`, `Config` |
### File Structure
```php
<?php
namespace App\Controllers;
use App\Models\ProductsModel;
use CodeIgniter\Controller;
class Products extends BaseController
{
protected array $data;
public function index()
{
// method body
}
}
```
### Imports and Namespaces
- Use explicit imports for all external classes
- Namespace should reflect file location (`App\Controllers\*`, `App\Models\*`)
- Place namespace at top, followed by imports, then class definition
- Do not use fully qualified class names inline
```php
<?php
namespace App\Controllers;
use App\Models\ProductsModel;
use App\Models\ProductsLogModel;
use CodeIgniter\Controller;
```
### Indentation and Spacing
- Use tabs for indentation (4 spaces per tab is acceptable)
- No trailing whitespace
- One blank line between method definitions
- Space around operators: `$productid = $value;`
- No space between function name and parentheses: `func()` not `func ()`
### Arrays and Configuration
```php
// Short array syntax for simple arrays
protected array $data;
$this->data['productowners'] = ['S' => 'Summit', 'C' => 'Customer'];
// Multi-line array with proper alignment
protected $allowedFields = [
'siteid',
'productnumber',
'productname',
'catalogid',
];
```
### Control Structures
```php
// Single-line condition with braces
if ($productid !== null) {
// code
}
// Multi-line conditions
if ($areaid !== '' || $producttypeid !== '') {
$sql .= ' AND ';
}
// Ternary for simple conditions
$locationstartdate = ($this->request->getVar('locationstartdate') === '')
? null
: $this->request->getVar('locationstartdate');
```
### Database Operations
- Use `$db = \Config\Database::connect();` for direct queries
- Use Query Builder when possible: `$db->table()->get()->getResultArray()`
- Always escape user input: `$db->escapeString($value)`
- Use parameterized queries to prevent SQL injection
```php
$builder = $db->table('products');
$builder->where('productid', $productid);
$query = $builder->get();
$results = $query->getResultArray();
```
### Controllers
- All controllers extend `BaseController`
- Use `$this->request->getMethod()` to check request type
- Use `$this->request->getVar()` or `$this->request->getPost()` for input
- Return views with `return view('view_name', $data);`
- Validate input with `$this->validate($rules)` before processing
```php
public function edit($productid = null)
{
if ($this->request->getMethod() === 'POST') {
$rules = [
'productid' => 'required',
'productnumber' => 'required',
];
if ($this->validate($rules)) {
// process form
}
}
return view('products_edit', $data);
}
```
### Models
- All models extend `CodeIgniter\Model`
- Define `$table`, `$primaryKey`, `$allowedFields`
- Use `$useSoftDeletes` for soft delete functionality
- Define `$useTimestamps` if using created_at/updated_at
```php
<?php namespace App\Models;
use CodeIgniter\Model;
class ProductsModel extends Model
{
protected $table = 'products';
protected $primaryKey = 'productid';
protected $allowedFields = ['siteid', 'productnumber', 'productname'];
}
```
### Error Handling
- Use CodeIgniter's built-in validation: `$this->validate($rules)`
- Return appropriate HTTP status codes in API controllers
- Use `try/catch` for operations that may throw exceptions
- Log errors using CodeIgniter's logging: `log_message('error', $message)`
```php
try {
$productsModel = new ProductsModel();
$productsModel->update($productid, $data['new_value']);
return view('form_success', $data);
} catch (\Exception $e) {
log_message('error', $e->getMessage());
return view('error_page');
}
```
### Views
- Place views in `app/Views/` directory
- Use `.php` extension
- Access data via associative array: `$products`, `$data['products']`
- Use CodeIgniter's view parser or raw PHP
### API Responses
```php
// Return JSON response
return $this->response->setJSON([
'status' => 'success',
'data' => $data,
]);
// Error response
return $this->response->setJSON([
'status' => 'error',
'message' => 'No data found',
]);
```
### Testing
- Extend `CodeIgniter\Test\CIUnitTestCase` for all tests
- Test methods must start with `test` prefix
- Use `@internal` docblock for test classes
```php
<?php
use CodeIgniter\Test\CIUnitTestCase;
final class HealthTest extends CIUnitTestCase
{
public function testIsDefinedAppPath(): void
{
$this->assertTrue(defined('APPPATH'));
}
}
```
### PHP Requirements
- PHP 8.1 or higher
- Extensions required: `intl`, `mbstring`, `json`
- MySQLi database driver by default

View File

@ -77,14 +77,16 @@ class Areas extends BaseController {
$data['areazone'] = $results; $data['areazone'] = $results;
if ($this->request->getMethod() === 'POST') { if ($this->request->getMethod() === 'POST') {
$areaid = $this->request->getVar('areaid');
$zoneid = $this->request->getVar('zoneid');
$areazoneid_delete = $this->request->getVar('areazoneid_delete');
$rules = [ $rules = [
'areaid' => 'required', 'areaid' => 'required',
'zoneid' => 'required' 'zoneid' => 'required'
]; ];
if($this->validate($rules)){ if($this->validate($rules)){
$areaid = $this->request->getVar('areaid');
$zoneid = $this->request->getVar('zoneid');
$areazoneid_delete = $this->request->getVar('areazoneid_delete');
if($areazoneid_delete!='') { if($areazoneid_delete!='') {
$areazoneid_delete = explode(' ',$areazoneid_delete); $areazoneid_delete = explode(' ',$areazoneid_delete);
foreach( $areazoneid_delete as $areazoneid ) { foreach( $areazoneid_delete as $areazoneid ) {

View File

@ -6,9 +6,9 @@
<div class="row page-titles"> <div class="row page-titles">
<div class="col-md-5 align-self-center"> <div class="col-md-5 align-self-center">
<h4 class="text-themecolor">Area Management Page</h4> <h4 class="text-themecolor">Area Management Page</h4>
</div> </div>
<div class="col-md-7 align-self-center text-end"> <div class="col-md-7 align-self-center text-end">
<a class='btn btn-info text-white btn-sm' href='areas/create/' <a class='btn btn-info text-white btn-sm' href='areas/create/'
onclick="window.open(this.href, 'Create','width=900,height=600,toolbar=1,resizable=0'); return false;"> onclick="window.open(this.href, 'Create','width=900,height=600,toolbar=1,resizable=0'); return false;">
<i class="fas fa-plus-circle"></i> Create <i class="fas fa-plus-circle"></i> Create
</a> </a>
@ -21,29 +21,36 @@
<div class="table-responsive"> <div class="table-responsive">
<table id="myTable" class="table display table-striped border"> <table id="myTable" class="table display table-striped border">
<thead> <thead>
<th>ID</th> <th>Type</th> <th>Name</th> <th>Description</th> <th></th> <th>ID</th>
<th>Type</th>
<th>Name</th>
<th>Description</th>
<th></th>
</thead> </thead>
<tbody> <tbody>
<?php <?php
foreach($areas as $data) { foreach ($areas as $data) {
$areaid = $data['areaid']; $areaid = $data['areaid'];
$areatype = $data['areatype']; $areatype = $data['areatype'];
$areaname = $data['areaname']; $areaname = $data['areaname'];
$description = $data['description']; $description = $data['description'];
?> ?>
<tr> <tr>
<td><?=$areaid;?></td> <td><?=$areatype;?></td> <td><?=$areaname;?></td> <td><?=$description;?></td> <td><?= $areaid; ?></td>
<td> <td><?= $areatype; ?></td>
<a class='btn btn-warning btn-sm' href='areas/edit/<?=$areaid;?>' <td><?= $areaname; ?></td>
onclick="window.open(this.href, 'Editor','width=900,height=800,toolbar=1,resizable=0'); return false;"> <td><?= $description; ?></td>
<i class="fas fa-pencil-alt"></i> Edit <td>
</a> <a class='btn btn-warning btn-sm' href='areas/edit/<?= $areaid; ?>'
<a class='btn btn-dark btn-sm' href='areazone/edit/<?=$areaid;?>' onclick="window.open(this.href, 'Editor','width=900,height=800,toolbar=1,resizable=0'); return false;">
onclick="window.open(this.href, 'Editor','width=900,height=800,toolbar=1,resizable=0'); return false;"> <i class="fas fa-pencil-alt"></i> Edit
<i class="fa-solid fa-location-dot"></i> Zone </a>
</a> <a class='btn btn-dark btn-sm' href='areazone/edit/<?= $areaid; ?>'
</td> onclick="window.open(this.href, 'Editor','width=900,height=800,toolbar=1,resizable=0'); return false;">
</tr> <i class="fa-solid fa-location-dot"></i> Zone
</a>
</td>
</tr>
<?php } ?> <?php } ?>
</tbody> </tbody>
</table> </table>
@ -54,14 +61,4 @@
</div> </div>
</div> </div>
</div> </div>
<?= $this->endSection() ?>
<?= $this->section('script') ?>
<script>
$(function () {
$('#myTable').DataTable({
"order" : []
});
});
</script>
<?= $this->endSection() ?> <?= $this->endSection() ?>

View File

@ -1,6 +1,6 @@
<tr> <tr>
<td style='min-width:300px;'> <td style='min-width:300px;'>
<select class='form-control form-control-sm select2' name='contactid[]' style='width:100%;'> <select class='form-control form-control-sm select2' name='zoneid[]' style='width:100%;'>
<option value=''>--</option> <option value=''>--</option>
<?php <?php
foreach($zones as $data) { foreach($zones as $data) {