# Routing system

Previously, `index.php` directly included the module file to process the action, based on the `action` and `file` request parameters.

With the new architecture, the resolution logic has been moved to the `<strong>IndexRouter</strong>` class, which handles action path resolution and processing.

#### **Action resolution**

##### **Searched paths (in order)**

When an action is requested, `IndexRouter` searches for the file in the following paths:

1. `modules/{MODULE}/{ACTION}.php`
2. `modules/{MODULE}/{ACTION}/{ACTION}.php`
3. `modules/{MODULE}/controllers/{ACTION}.php` <span style="color: rgb(45, 194, 107);">**(NEW)**</span>
4. `modules/VteCore/{ACTION}.php`
5. `modules/VteCore/controllers/{ACTION}.php` <span style="color: rgb(45, 194, 107);">**(NEW)**</span>

##### **Removal of `{MODULE}Ajax.php`**

It is no longer necessary to create a `{MODULE}Ajax.php` file in the module folder. AJAX requests can be handled directly by BaseAction classes, which automatically detect the request type and desired output format.

##### **Execution process**

After resolving and including the action file:

1. `IndexRouter` checks if the file contains a class that extends `BaseAction`
2. If found, it calls the `fromRequest()` method to create an instance of the class
3. During `fromRequest()`, class properties are automatically populated from request parameters
4. `processFromIndex()` is called, which performs validation and processing
5. The result is formatted according to the requested content-type and sent to the client

If the file included in `index.php` does not contain a `BaseAction` class, then the included code is executed and entirely managed by the file (previous behavior).

#### **Creating a new BaseAction**

##### **Basic structure**

To create a new action, create a file in:

- `modules/{MODULE}/controllers/{ACTION}.php`
- or `modules/VteCore/controllers/{ACTION}.php` (for global actions)

The class must:

- Extend `BaseAction`
- Implement the `process()` method
- Declare properties with appropriate attributes

```php
<?php

class MyAction extends BaseAction {
    
    protected function process() {
        // Your logic here
        return ['success' => true, 'data' => []];
    }
}

```

#### **Declaring properties**

Class properties can be automatically populated from the request using PHP 8 attributes:

<table border="1" cellpadding="10" cellspacing="0" id="bkmrk-attribute-source-des"><thead><tr><th>Attribute</th><th>Source</th><th>Description</th></tr></thead><tbody><tr><td>`RequestParam`</td><td>$\_REQUEST</td><td>Parameter from request (GET or POST)</td></tr><tr><td>`RequestRawParam`</td><td>$\_REQUEST</td><td>Raw parameter from request</td></tr><tr><td>`GetParam`</td><td>$\_GET</td><td>Parameter from GET</td></tr><tr><td>`GetRawParam`</td><td>$\_GET</td><td>Raw parameter from GET</td></tr><tr><td>`PostParam`</td><td>$\_POST</td><td>Parameter from POST</td></tr><tr><td>`PostRawParam`</td><td>$\_POST</td><td>Raw parameter from POST</td></tr><tr><td>`CookieParam`</td><td>$\_COOKIE</td><td>Parameter from cookie</td></tr></tbody></table>

#### **Attribute parameters**

- **name**: Parameter name in the request (default: property name)
- **filter**: Filter to apply (e.g. `F::int`, `F::str`, `F::bool`). For more details, see [Basic filters](https://usermanual.vtenext.com/books/developers/page/requesthandler#bkmrk-basic-filters).
- **required**: Whether the parameter is required (default: based on nullable type)
- **default**: Default value if the parameter is not present
- **validation**: Regex or callable to validate the value
- **description**: Parameter description (for documentation)

```php
<?php
class MyAction extends BaseAction {
    
    // Required parameter (not nullable)
    #[RequestParam()]
    protected int $record;
    
    // Optional parameter (nullable)
    #[RequestParam()]
    protected ?string $searchText;
    
    // Parameter with different name
    #[RequestParam(name: 'return_module', filter: F::mod)]
    protected ?string $returnModule;
    
    // Parameter with regex validation
    #[RequestParam(validation: '/^(true|false)$/')]
    protected ?string $isDuplicate;
    
    // Parameter with callable validation
    #[RequestParam(validation: 'is_numeric')]
    protected ?string $amount;
    
    // Parameter with default value
    #[RequestParam(default: 10)]
    protected int $limit;
    
    // Parameter from POST
    #[PostParam()]
    protected ?string $comment;
    
    // Parameter from GET
    #[GetParam()]
    protected ?int $page;
    
    protected function process() {
        // Properties are already populated here
        return [
            'record' => $this->record,
            'search' => $this->searchText,
        ];
    }
}

```

#### **Main methods**

##### **process()**

Main method that contains the action logic. Must return data that will be formatted and sent to the client.

```php
protected abstract function process();
```

##### **validate(?string &amp;$error): bool**

Called before `process()` to validate the request. If it returns `false`, execution stops and an error is sent.

```php
protected function validate(?string &$error): bool {
    if ($this->record <= 0) {
        $error = "Invalid record ID";
        return false;
    }
    return true;
}
```

##### **beforeProcess()**

Called before `process()`, after validation.

```php
protected function beforeProcess(): void {
    // Initialization, logging, etc.
}
```

##### **afterProcess(&amp;$result)**

Called after `process()`, allows modifying the result before sending.

```php
protected function afterProcess(&$result): void {
    // Modify result, logging, etc.
    $result['timestamp'] = time();
}
```

#### **Output**

The output format is automatically determined based on:

1. The class's `$outputFormat` property (if set)
2. The `format` or `_format` parameter in the request
3. The `Accept` header of the request
4. Whether the request is AJAX (default: `json`) or not (default: `html`)

##### **Supported formats**

- **json** - JSON response (automatic for AJAX)
- **html** - HTML response (automatic for standard requests)

You can force a specific format by setting the `$outputFormat` property:

```php
class MyAction extends BaseAction {
    
    protected ?string $outputFormat = 'json';
    
    protected function process() {
        return ['data' => 'Always JSON'];
    }
}
```

Or by using the `setOutputFormat()` method:

```php
class MyAction extends BaseAction {
    
    protected function beforeProcess(): void {
        $this->setOutputFormat('html');
    }
    
    protected function process() {
        return '<h1>HTML Output</h1>';
    }
}
```

##### **JSON response format**

On success:

```json
{
    "success": true,
    "result": { /* data returned by process() */ }
}
```

On error:

```json
{
    "success": false,
    "error": {
        "message": "Error message",
        "code": 400
    }
}
```

##### **Requesting JSON from client**

To request a JSON response, the client can:

- Add the parameter `?format=json` or `?_format=json` to the URL
- Set the header `Accept: application/json`
- Make an AJAX request (automatically detected)

#### **Example**

```php
<?php

use RequestParam;
use PostParam;

class SaveData extends BaseAction {
    
    // We don't specify outputFormat, it will be determined automatically
    
    #[RequestParam()]
    protected ?int $record;
    
    #[PostParam(required: true)]
    protected string $name;
    
    #[PostParam(filter: F::int, default: 1)]
    protected int $status;
    
    #[PostParam()]
    protected ?string $description;
    
    protected function validate(?string &$error): bool {
        if (strlen($this->name) < 3) {
            $error = "Name must be at least 3 characters long";
            return false;
        }
        return true;
    }
    
    protected function beforeProcess(): void {
        // Log the operation
        global $log;
        $log->info("Saving record: " . $this->name);
    }
    
    protected function process() {
        global $adb;
        
        // Save logic
        if ($this->record) {
            // Update logic
        } else {
            // Insert logic
        }
        
        // Return data
        // If the request is AJAX, it will be automatically formatted as JSON
        // Otherwise as HTML
        return [
            'record_id' => $id,
            'message' => 'Save completed'
        ];
    }
    
    protected function afterProcess(&$result): void {
        // Add timestamp to result
        $result['timestamp'] = time();
    }
}

```

#### **Differences from the previous version**

<table border="1" cellpadding="10" cellspacing="0" id="bkmrk-aspect-before-now-ac"><thead><tr><th>Aspect</th><th>Before</th><th>Now</th></tr></thead><tbody><tr><td>Action resolution</td><td>In index.php</td><td>In IndexRouter.php</td></tr><tr><td>Ajax files</td><td>`{MODULE}Ajax.php` required</td><td>No longer necessary</td></tr><tr><td>Controllers paths</td><td>Did not exist</td><td>`modules/{MODULE}/controllers/`  
`modules/VteCore/controllers/`</td></tr><tr><td>Parameter population</td><td>Manual with $\_REQUEST</td><td>Automatic with PHP attributes</td></tr><tr><td>Validation</td><td>Manual</td><td>validate() method + attribute validation</td></tr><tr><td>Output format</td><td>Manually handled</td><td>Automatic</td></tr><tr><td>Error handling</td><td>Manual</td><td>Automatic with try/catch in processFromIndex()</td></tr></tbody></table>

#### **Best Practices**

- Create new actions as BaseAction classes in `modules/{MODULE}/controllers/`
- Use attributes to declare parameters explicitly
- Always specify the appropriate filter (`F::int`, `F::str`, etc.)
- Implement the `validate()` method for business logic validations
- Use attribute validation for simple validations (regex, callable)
- Let the system automatically determine the output format (JSON/HTML)
- Force `$outputFormat` only when necessary
- Use `beforeProcess()` for initialization and `afterProcess()` for post-processing