# Sistema de Validação - Framework OfficeGest

## Visão Geral

O **Sistema de Validação** do OfficeGest é um motor robusto e extensível para validação de dados em todas as camadas da aplicação. Baseado nos padrões **Strategy**, **Rule Pattern** e **Chain of Responsibility**, oferece uma API fluente para validação de requests HTTP, forms e dados de API.

```mermaid
flowchart LR
    subgraph Input["Origem dos Dados"]
        HTTP["HTTP Request"]
        Form["Form Data"]
        API["API Payload"]
    end
    
    subgraph Core["Motor de Validação"]
        Validator["Validator"]
        Rules["Rule Sets\n• CommonRule\n• DateRule\n• FormatRule\n• FileRule"]
    end
    
    subgraph Output["Resultado"]
        Valid["validated()"]
        Errors["errors()"]
    end
    
    HTTP --> Validator
    Form --> Validator
    API --> Validator
    Validator --> Rules
    Rules --> Valid
    Rules --> Errors
    
    style Core fill:#e8f4fd,stroke:#0284c7
    style Output fill:#f0fdf4,stroke:#22c55e
```

---

## Estrutura de Ficheiros

```
Modules/Common/Validation/
├── Validator.php                    # Motor principal (702 lines)
├── ValidationServiceProvider.php    # Registo no DI container
├── DotArrayFilter.php               # Filtro de arrays com dot notation
├── Helpers.php                      # Funções globais
├── Contracts/
│   ├── ValidatorInterface.php
│   ├── RuleInterface.php
│   ├── DataAwareRule.php
│   ├── ValidatorAwareRule.php
│   └── CompilableRules.php
├── Rules/
│   ├── Rule.php                     # Factory class
│   ├── CommonRule.php               # Rules básicas (628 lines)
│   ├── DateRule.php                 # Rules temporais (347 lines)
│   ├── FormatRule.php               # Rules de formato (251 lines)
│   ├── FileRule.php                 # Rules de ficheiros
│   ├── Unique.php                   # Unicidade DB
│   ├── Exists.php                   # Existência DB
│   ├── Enum.php                     # Validação de enums
│   ├── In.php                       # Lista de valores
│   ├── RequiredIf.php               # Condicional
│   ├── HasCollision.php             # Colisão de intervalos
│   ├── UniqueCombination.php        # Combinação única
│   └── Traits/
│       ├── Conditionable.php        # Validação condicional
│       └── DatabaseRule.php         # Base para rules DB
└── Language/
    ├── Language.php                 # Sistema i18n
    └── Validation.php               # Mensagens PT
```

---

## Helpers Globais

O ficheiro `Helpers.php` expõe três funções globais:

```php
<?php
// Modules/Common/Validation/Helpers.php

// Valida dados contra regras
function validate(?array $data = null, array $rules = [], array $messages = [], array $attributes = []): mixed
{
    $factory = app(ValidatorInterface::class);

    if (func_num_args() === 0) {
        return $factory; // Retorna instância para encadeamento
    }

    $factory->setRules($rules, $messages);
    return $factory->withRequest($data ?? request()->form ?? request()->all())
        ->validate();
}

// Obtém erros de validação
function validationErrors(): array
{
    return app(ValidatorInterface::class)->errors();
}

// Obtém dados validados
function validated(): array
{
    return app(ValidatorInterface::class)->validated();
}
```

### Exemplos de Uso

```php
// Validação simples
$passes = validate($data, [
    'email' => 'required|email',
    'name' => 'required|min_length[2]|max_length[100]'
]);

if (!$passes) {
    return response()->json(['errors' => validationErrors()], 422);
}

$validData = validated();

// Acesso ao validator para encadeamento
$validator = validate();
$validator->setRule('email', 'Email', 'required|email');
$validator->validate(['email' => 'test@example.com']);
```

---

## BaseRequest - Request Validation

A classe abstrata `BaseRequest` fornece validação automática de HTTP requests:

```php
<?php

namespace Og\Modules\Common\Http;

abstract class BaseRequest extends BaseClass
{
    public Request $request;
    protected bool $validated = true;

    // Métodos abstratos obrigatórios
    abstract public function authorize(): bool;
    abstract public function rules(): array;

    // Opcional: mensagens customizadas
    protected function messages(): array { return []; }
    
    // Opcional: callback após validação
    protected function after(): ?Closure { return null; }

    public function __construct()
    {
        parent::__construct();
        $this->request = app()->make(Request::class);

        // 1. Verificar autorização
        if (!$this->authorize()) {
            $this->handleUnauthorized();
            return;
        }

        // 2. Executar validação
        $passes = $this->validate($this->rules(), $this->messages());
        if (!$passes) {
            $this->validated = false;
            $this->handleValidation();
            return;
        }

        // 3. Executar callback after (opcional)
        $afterCallback = $this->after();
        if ($afterCallback instanceof Closure) {
            $this->validator->after($afterCallback);
            if (!$this->validator->validate()) {
                $this->validated = false;
                $this->handleValidation();
            }
        }
    }

    public function validated(null|string|array $fields = null): array
    {
        return $this->validator->validated($fields);
    }
}
```

### Exemplo Completo de Request

```php
<?php

namespace Og\Modules\Crm\Appointments\Requests;

use Og\Modules\Common\Http\BaseRequest;
use Og\Modules\Common\Validation\Rules\Rule;
use Og\Modules\Entidade\Common\Enums\EntityType;

class AppointmentsCreateRequest extends BaseRequest
{
    public function authorize(): bool
    {
        return true; // Ou lógica de permissão
    }

    public function rules(): array
    {
        return [
            // Campos obrigatórios
            'subject'     => ['required', 'string'],
            'start_date'  => ['required', 'date_format[Y-m-d]'],
            'start_time'  => ['required', 'date_format[H:i]'],
            'end_date'    => ['required', 'date_format[Y-m-d]', 'after_or_equal[start_date]'],
            'end_time'    => ['required', 'date_format[H:i]'],

            // Campos condicionais com if_exist
            'responsible_id' => [
                'if_exist',
                'integer',
                'exists[' . TABLE_EMPREGADOS . ',codempr]'
            ],
            'seller_id' => [
                'if_exist',
                'integer',
                'exists[' . TABLE_COMERCIAIS . ',codrepres]'
            ],

            // Validação de enum
            'entity_type' => ['if_exist', 'string', Rule::enum(EntityType::class)],

            // Validação condicional
            'hour_reception' => [
                'if_exist',
                'required_with[date_reception]',
                'date_format[H]'
            ],

            // Array validation
            'contacts'   => ['if_exist', 'array'],
            'contacts.*' => ['string'],
        ];
    }
}
```

### Uso do Request em Controllers

```php
// No Controller
public function store()
{
    $request = new AppointmentsCreateRequest();
    $data = $request->validated();
    
    return Appointment::create($data);
}

// Com campos específicos
$data = $request->validated(['subject', 'start_date', 'end_date']);
```

---

## Rule Factory Class

A classe `Rule` funciona como factory para criação fluente de rules:

```php
<?php

namespace Og\Modules\Common\Validation\Rules;

class Rule
{
    // Validação de enum PHP 8.1+
    public static function enum($type): Enum
    {
        return new Enum($type);
    }

    // Lista de valores permitidos
    public static function in($values): In
    {
        return new In(is_array($values) ? $values : func_get_args());
    }

    // Required condicional com callback
    public static function requiredIf($callback): RequiredIf
    {
        return new RequiredIf($callback);
    }

    // Unicidade em base de dados
    public static function unique(string $table, string $column = 'id'): Unique
    {
        return new Unique($table, $column);
    }

    // Existência em base de dados
    public static function exists(string $table, string $column = 'id'): Exists
    {
        return new Exists($table, $column);
    }

    // Detecção de colisão de intervalos
    public static function collision(
        string $table,
        string $endField,
        ?string $search = null,
        string $primaryKey = 'id',
        bool $closed = true
    ): HasCollision {
        return new HasCollision($table, $endField, $search, $primaryKey, $closed);
    }

    // Combinação única de campos
    public static function uniqueCombination(string $table, array $columns): UniqueCombination
    {
        return new UniqueCombination($table, $columns);
    }
}
```

### Exemplos de Uso

```php
use Og\Modules\Common\Validation\Rules\Rule;

// Unicidade com ignore para updates
$rules = [
    'email' => [
        'required',
        'email',
        Rule::unique('users', 'email')->ignore($userId, 'id')
    ]
];

// Unicidade com condições adicionais
$rules = [
    'code' => [
        'required',
        Rule::unique('products', 'code')
            ->ignore($productId)
            ->where('company_id', $companyId)
            ->where('active', 1)
    ]
];

// Existência com condições
$rules = [
    'category_id' => [
        'required',
        Rule::exists('categories', 'id')
            ->where('active', 1)
    ]
];

// Validação de enum
$rules = [
    'status' => ['required', Rule::enum(OrderStatus::class)],
    'type'   => ['required', Rule::in(['A', 'B', 'C'])]
];
```

---

## Catálogo de Rules

### CommonRule - Regras Básicas

| Rule | Sintaxe | Descrição |
|------|---------|-----------|
| `required` | `required` | Campo obrigatório |
| `nullable` | `nullable` | Campo pode ser null |
| `equals` | `equals[value]` | Valor exactamente igual |
| `not_equals` | `not_equals[value]` | Valor diferente |
| `differs` | `differs[field]` | Diferente de outro campo |
| `matches` | `matches[field]` | Igual a outro campo |
| `same` | `same[field]` | Idêntico a outro campo |
| `min_length` | `min_length[5]` | Mínimo de caracteres |
| `max_length` | `max_length[255]` | Máximo de caracteres |
| `exact_length` | `exact_length[9]` | Comprimento exacto |
| `greater_than` | `greater_than[0]` | Maior que valor |
| `greater_than_equal_to` | `greater_than_equal_to[1]` | Maior ou igual |
| `less_than` | `less_than[100]` | Menor que valor |
| `less_than_equal_to` | `less_than_equal_to[99]` | Menor ou igual |
| `in_list` | `in_list[A,B,C]` | Valor na lista |
| `not_in_list` | `not_in_list[X,Y,Z]` | Valor fora da lista |
| `is_unique` | `is_unique[table.column]` | Único na tabela |
| `is_not_unique` | `is_not_unique[table.column]` | Existe na tabela |
| `exists` | `exists[table,column]` | Registo existe |
| `required_with` | `required_with[field1,field2]` | Obrigatório se campos preenchidos |
| `required_without` | `required_without[field]` | Obrigatório se campo vazio |
| `required_without_all` | `required_without_all[f1,f2]` | Obrigatório se todos vazios |
| `requiredIf` | `requiredIf[field,value]` | Obrigatório se condição |
| `greaterThanField` | `greaterThanField[field]` | Maior que outro campo |
| `lessThanField` | `lessThanField[field]` | Menor que outro campo |
| `maxValue` | `maxValue[1000]` | Valor máximo numérico |
| `minValue` | `minValue[0]` | Valor mínimo numérico |
| `decimal` | `decimal[10,2]` | Número decimal |

---

### DateRule - Regras Temporais

| Rule | Sintaxe | Descrição |
|------|---------|-----------|
| `before` | `before[field]` | Antes de outra data |
| `before_or_equal` | `before_or_equal[field]` | Antes ou igual |
| `after` | `after[field]` | Depois de outra data |
| `after_or_equal` | `after_or_equal[field]` | Depois ou igual |
| `between` | `between[start,end]` | Entre duas datas |
| `date_equals` | `date_equals[field]` | Igual a outra data |
| `date_format` | `date_format[Y-m-d]` | Formato específico |
| `is_future` | `is_future` | Data no futuro |
| `is_past` | `is_past` | Data no passado |

**Formatos suportados pelo parser automático:**
- `Y-m-d`, `d/m/Y`, `m-d-Y`, `d-m-Y`, `Y/m/d`
- Com hora: `Y-m-d H:i:s`, `d/m/Y H:i:s`, etc.

```php
// Exemplos
$rules = [
    'birth_date' => ['required', 'date_format[d/m/Y]', 'is_past'],
    'event_date' => ['required', 'date_format[Y-m-d]', 'is_future'],
    'end_date'   => ['required', 'after_or_equal[start_date]'],
    'deadline'   => ['required', 'between[start_date,now]'],
];
```

---

### FormatRule - Regras de Formato

| Rule | Sintaxe | Descrição |
|------|---------|-----------|
| `string` | `string` | Deve ser string |
| `boolean` | `boolean` | Deve ser boolean |
| `integer` | `integer` | Deve ser inteiro |
| `numeric` | `numeric` | Deve ser numérico |
| `decimal` | `decimal` | Número decimal |
| `array` | `array` | Deve ser array |
| `email` | `email` | Email válido |
| `emails` | `emails` | Lista de emails |
| `url` | `url` | URL válida |
| `url_strict` | `url_strict[http,https]` | URL com schemes específicos |
| `ip` | `ip` ou `ip[ipv4]` | Endereço IP |
| `regex_match` | `regex_match[/pattern/]` | Match de regex |
| `alpha_space` | `alpha_space` | Letras e espaços |
| `alpha_dash` | `alpha_dash` | Letras, números, _, - |
| `alpha_numeric_space` | `alpha_numeric_space` | Alfanumérico e espaços |
| `alphanumeric` | `alphanumeric` | Apenas alfanumérico |
| `timezone` | `timezone` | Timezone válida |
| `valid_base64` | `valid_base64` | String base64 válida |
| `valid_json` | `valid_json` | JSON válido |
| `date` | `date` ou `date[format]` | Data válida |
| `accepted` | `accepted` | yes, on, 1, true |

```php
$rules = [
    'email'       => ['required', 'email'],
    'website'     => ['url_strict[https]'],
    'code'        => ['required', 'alpha_dash', 'max_length[20]'],
    'config'      => ['valid_json'],
    'terms'       => ['accepted'],
    'phone'       => ['regex_match[/^\+\d{1,3}\d{9,12}$/]'],
];
```

---

### FileRule - Regras de Ficheiros

| Rule | Sintaxe | Descrição |
|------|---------|-----------|
| `uploaded` | `uploaded[field]` | Ficheiro foi uploaded |
| `max_size` | `max_size[field,2048]` | Tamanho máximo (KB) |
| `mime_in` | `mime_in[field,image/png,image/jpeg]` | Tipos MIME permitidos |
| `ext_in` | `ext_in[field,pdf,doc,docx]` | Extensões permitidas |
| `max_dims` | `max_dims[field,1024,768]` | Dimensões máximas |
| `is_image` | `is_image[field]` | É uma imagem |

```php
$rules = [
    'avatar' => [
        'uploaded[avatar]',
        'max_size[avatar,1024]',
        'is_image[avatar]',
        'mime_in[avatar,image/jpeg,image/png,image/webp]'
    ],
    'document' => [
        'uploaded[document]',
        'max_size[document,5120]',
        'ext_in[document,pdf,doc,docx]'
    ],
];
```

---

## Modificadores Especiais

### if_exist / if_exists

Aplica validação apenas se o campo existir nos dados:

```php
$rules = [
    'optional_field' => ['if_exist', 'string', 'max_length[100]'],
    'phone' => ['if_exist', 'regex_match[/^\+?\d{9,15}$/]'],
];

// Com if_exist: campo ausente = válido
// Sem if_exist: campo ausente = erro
```

---

### permit_empty

Permite campos vazios, mas valida se preenchidos:

```php
$rules = [
    'nickname' => ['permit_empty', 'string', 'min_length[3]'],
    'bio' => ['permit_empty', 'string', 'max_length[500]'],
];

// Campo vazio: válido
// Campo preenchido: aplica min_length, max_length
```

---

### sometimes

Aplica regras apenas se o campo existir no dataset:

```php
$rules = [
    'email' => ['sometimes', 'required', 'email'],
];

// Campo ausente: não valida
// Campo presente: Must be valid email
```

---

### checkbox

Trata valores de checkbox como boolean:

```php
$rules = [
    'newsletter' => ['checkbox[0]'], // Default 0 se vazio
    'terms' => ['required', 'checkbox[1]'],
];
```

---

## Validação Condicional

### Trait Conditionable

```php
use Og\Modules\Common\Validation\Rules\Traits\Conditionable;

trait Conditionable
{
    // Aplicação condicional estática
    public static function when(
        callable|bool $condition,
        string|array|RuleInterface $rules,
        string|array $elseRules = ''
    ): string|array|RuleInterface {
        $result = is_callable($condition) ? $condition() : $condition;
        return $result ? $rules : $elseRules;
    }

    // Negação da condição
    public function unless(callable|bool $condition): self
    {
        $this->condition = is_callable($condition)
            ? fn() => !$condition()
            : !$condition;
        return $this;
    }
}
```

### Exemplos de Uso

```php
use Og\Modules\Common\Validation\Rules\Rule;

// Condição simples
$isUpdate = $request->has('id');

$rules = [
    'email' => Rule::when(
        $isUpdate,
        ['sometimes', 'email', Rule::unique('users')->ignore($userId)],
        ['required', 'email', Rule::unique('users')]
    ),
];

// Com callback
$rules = [
    'phone' => Rule::when(
        fn() => $data['contact_method'] === 'phone',
        ['required', 'regex_match[/^\+\d+$/]'],
        ['nullable']
    ),
];

// Rule com unless
$rules = [
    'category_id' => Rule::exists('categories', 'id')
        ->unless($data['use_default_category'] ?? false),
];
```

---

## Dot Notation e Wildcards

O sistema suporta validação de estruturas nested:

```php
$data = [
    'user' => [
        'name' => 'João',
        'profile' => [
            'bio' => 'Developer'
        ]
    ],
    'items' => [
        ['name' => 'Item 1', 'price' => 10.00],
        ['name' => 'Item 2', 'price' => 20.00],
    ]
];

$rules = [
    // Acesso nested
    'user.name' => ['required', 'string'],
    'user.profile.bio' => ['string', 'max_length[500]'],
    
    // Wildcard para arrays
    'items.*.name' => ['required', 'string'],
    'items.*.price' => ['required', 'numeric', 'greater_than[0]'],
];
```

---

## Placeholders

Substituição dinâmica de valores nas rules:

```php
$data = [
    'min_value' => 10,
    'max_value' => 100,
    'value' => 50,
];

$rules = [
    'min_value' => ['required', 'numeric'],
    'max_value' => ['required', 'numeric', 'greater_than[{min_value}]'],
    'value' => ['required', 'numeric', 'greater_than[{min_value}]', 'less_than[{max_value}]'],
];

// O validator substitui {min_value} por 10 e {max_value} por 100
```

---

## Callbacks After

Validação adicional após as rules padrão:

```php
use Og\Modules\Common\Http\BaseRequest;

class OrderRequest extends BaseRequest
{
    public function rules(): array
    {
        return [
            'items' => ['required', 'array'],
            'items.*.product_id' => ['required', 'exists[products,id]'],
            'items.*.quantity' => ['required', 'integer', 'greater_than[0]'],
        ];
    }

    protected function after(): ?\Closure
    {
        return function ($validator) {
            // Validação customizada após todas as rules
            $items = $this->validated()['items'] ?? [];
            $totalQuantity = array_sum(array_column($items, 'quantity'));
            
            if ($totalQuantity > 100) {
                $validator->addError('items', 'O total de itens não pode exceder 100 unidades.');
            }
            
            // Verificar stock
            foreach ($items as $index => $item) {
                $product = Product::find($item['product_id']);
                if ($product->stock < $item['quantity']) {
                    $validator->addError(
                        "items.{$index}.quantity",
                        "Stock insuficiente para {$product->name}"
                    );
                }
            }
        };
    }
}
```

---

## Custom Rules

### Interface RuleInterface

```php
<?php

namespace Og\Modules\Common\Validation\Contracts;

interface RuleInterface
{
    public function passes($attribute, $value);
    public function message();
}
```

### Criar Custom Rule

```php
<?php

namespace Og\Modules\Billing\Rules;

use Og\Modules\Common\Validation\Contracts\RuleInterface;

class ValidNIF implements RuleInterface
{
    public function passes($attribute, $value): bool
    {
        // Remover espaços
        $nif = preg_replace('/\s+/', '', $value);
        
        // Validar formato
        if (!preg_match('/^[0-9]{9}$/', $nif)) {
            return false;
        }
        
        // Validar dígito de controlo
        $checkDigit = 0;
        for ($i = 0; $i < 8; $i++) {
            $checkDigit += $nif[$i] * (9 - $i);
        }
        $checkDigit = 11 - ($checkDigit % 11);
        
        if ($checkDigit >= 10) {
            $checkDigit = 0;
        }
        
        return (int)$nif[8] === $checkDigit;
    }

    public function message(): string
    {
        return 'O campo {field} deve conter um NIF válido.';
    }
}
```

### Usar Custom Rule

```php
use Og\Modules\Billing\Rules\ValidNIF;

$rules = [
    'nif' => ['required', new ValidNIF()],
];
```

---

### Rules com Acesso a Dados

```php
<?php

namespace Og\Modules\Billing\Rules;

use Og\Modules\Common\Validation\Contracts\DataAwareRule;
use Og\Modules\Common\Validation\Contracts\RuleInterface;

class UniqueInvoiceNumber implements RuleInterface, DataAwareRule
{
    private array $data = [];
    
    public function defineData(array $data): void
    {
        $this->data = $data;
    }

    public function passes($attribute, $value): bool
    {
        // Acesso a outros campos
        $companyId = $this->data['company_id'] ?? null;
        $year = $this->data['year'] ?? date('Y');
        
        return !Invoice::where('company_id', $companyId)
            ->where('year', $year)
            ->where('number', $value)
            ->exists();
    }

    public function message(): string
    {
        return 'Este número de factura já existe para o ano seleccionado.';
    }
}
```

---

### Rules com Acesso ao Validator

```php
<?php

namespace Og\Modules\Common\Rules;

use Og\Modules\Common\Validation\Contracts\RuleInterface;
use Og\Modules\Common\Validation\Contracts\ValidatorAwareRule;
use Og\Modules\Common\Validation\Contracts\ValidatorInterface;

class ConditionalRequired implements RuleInterface, ValidatorAwareRule
{
    private ValidatorInterface $validator;
    private string $dependsOn;
    private mixed $dependsValue;

    public function __construct(string $dependsOn, mixed $dependsValue)
    {
        $this->dependsOn = $dependsOn;
        $this->dependsValue = $dependsValue;
    }

    public function defineValidator(ValidatorInterface $validator): void
    {
        $this->validator = $validator;
    }

    public function passes($attribute, $value): bool
    {
        $data = $this->validator->getData();
        
        if (($data[$this->dependsOn] ?? null) !== $this->dependsValue) {
            return true; // Não é obrigatório
        }
        
        return !empty($value);
    }

    public function message(): string
    {
        return "O campo {field} é obrigatório quando {$this->dependsOn} é {$this->dependsValue}.";
    }
}
```

---

## Mensagens de Erro Customizadas

### Por Campo

```php
$rules = [
    'email' => [
        'label' => 'Endereço de Email',
        'rules' => ['required', 'email'],
        'errors' => [
            'required' => 'Por favor, preencha o seu email.',
            'email' => 'O formato do email não é válido.',
        ]
    ]
];
```

### Em BaseRequest

```php
class UserCreateRequest extends BaseRequest
{
    public function rules(): array
    {
        return [
            'email' => ['required', 'email', Rule::unique('users')],
            'password' => ['required', 'min_length[8]'],
        ];
    }

    protected function messages(): array
    {
        return [
            'email' => [
                'required' => 'O email é obrigatório.',
                'email' => 'Forneça um email válido.',
                'is_unique' => 'Este email já está registado.',
            ],
            'password' => [
                'min_length' => 'A password deve ter pelo menos 8 caracteres.',
            ],
        ];
    }
}
```

---

## Validator API

### Métodos Principais

```php
$validator = app(ValidatorInterface::class);

// Definir rules
$validator->setRule('field', 'Label', 'required|email');
$validator->setRules([
    'field1' => 'required',
    'field2' => ['required', 'string'],
]);

// Carregar dados
$validator->withRequest($data);
$validator->defineData($data);

// Executar validação
$passes = $validator->validate();
$passes = $validator->validate($data); // Com dados inline

// Verificar resultados
$errors = $validator->errors();
$hasError = $validator->hasError('field');
$error = $validator->error('field');

// Obter dados validados
$all = $validator->validated();
$specific = $validator->validated('field');
$some = $validator->validated(['field1', 'field2']);

// Filtrar dados validados
$only = $validator->only(['field1', 'field2'])->validated();
$except = $validator->except(['password'])->validated();

// Adicionar erro manual
$validator->addError('field', 'Mensagem de erro');
$validator->setError('field', 'Mensagem de erro');

// Callback after
$validator->after(function ($v) {
    // Validação adicional
});

// Reset
$validator->reset();

// Validação rápida de valor único
$passes = $validator->check($value, ['required', 'email']);
```

---

## Referência de Ficheiros

| Componente | Caminho |
|------------|---------|
| Validator | [Validator.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Validator.php) |
| BaseRequest | [BaseRequest.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Http/BaseRequest.php) |
| Rule Factory | [Rule.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/Rule.php) |
| CommonRule | [CommonRule.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/CommonRule.php) |
| DateRule | [DateRule.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/DateRule.php) |
| FormatRule | [FormatRule.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/FormatRule.php) |
| FileRule | [FileRule.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/FileRule.php) |
| Unique | [Unique.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/Unique.php) |
| Exists | [Exists.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/Exists.php) |
| Conditionable | [Conditionable.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/Traits/Conditionable.php) |
| DatabaseRule | [DatabaseRule.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Rules/Traits/DatabaseRule.php) |
| Helpers | [Helpers.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Helpers.php) |
| ServiceProvider | [ValidationServiceProvider.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/ValidationServiceProvider.php) |
| Language | [Validation.php](file:///home/paulo/www/guisoft/ogdevel/Modules/Common/Validation/Language/Validation.php) |

---

## Request Classes no Sistema

O sistema possui mais de **75 Request classes** em diferentes módulos:

| Módulo | Requests |
|--------|----------|
| Crm | `AppointmentsCreateRequest`, `AppointmentsIndexRequest`, `ParametersUpdateRequest` |
| Entidade | `CustomerCreateRequest`, `CustomerIndexRequest`, `ContactRequest`, `EmployeesIndexRequest` |
| Ams | `AmsHandlingStoreRequest`, `AmsHandlingUpdateRequest`, `AmsHandlingAttachmentRequest` |
| SaftAo | `SaftAoElectronicInvoicingRequest` |
| Auth | `AuthApiRequest`, `LoginRequest` |
| Developer | `FailedJobsListRequest` |
| Oficinas | `ReorderStatusRequest`, `SignRequest`, `AwaitingAuthorizationRequest` |

---

## Conclusão

O Sistema de Validação do OfficeGest oferece:

- **Flexibilidade**: 50+ rules built-in cobrindo todos os cenários comuns
- **Extensibilidade**: Custom rules com interfaces DataAware e ValidatorAware
- **Integração HTTP**: BaseRequest para validação automática de requests
- **Dot Notation**: Validação de estruturas nested com wildcards
- **Condicionais**: Rules dinâmicas baseadas em outros campos
- **i18n**: Mensagens de erro internacionalizadas
- **Performance**: Validação sequencial com early exit