# Helpers System - Framework OfficeGest

> **Nível**: Básico a Avançado
> **Pré-requisitos**: PHP 8.3+
> **Tempo de leitura**: ~45 minutos

---

## 📋 Índice

1. [Arquitectura](#arquitectura)
2. [Global Helpers (Helpers.php)](#global-helpers)
    - [Container & Configuração](#container--configuração)
    - [Request & Response](#request--response)
    - [Strings](#strings)
    - [Validação & Encoding](#validação--encoding)
    - [Logging](#logging)
    - [Notificações](#notificações)
    - [Diversos](#diversos)
3. [App Helpers (`App/`)](#app-helpers)
    - [Filesystem Paths](#filesystem-paths)
    - [Date Helpers](#date-helpers)
    - [Auth Helpers](#auth-helpers)
    - [Environment & Forms](#environment--forms)
    - [Smart Search (DataTables)](#smart-search-datatables)
4. [Classes Estáticas](#classes-estáticas)
    - [ArrayHelper](#arrayhelper)
    - [StringHelper](#stringhelper)
    - [DataTableHelper](#datatablehelper)
    - [Response](#response)

---

## Arquitectura

O sistema de Helpers está organizado em:

```
Modules/Common/Helpers/
├── Helpers.php              # Funções globais principais (~950 linhas)
├── ArrayHelper.php          # Classe estática para arrays
├── StringHelper.php         # Classe estática para strings
├── DataTableHelper.php      # Integração jQuery DataTables
├── Response.php             # Respostas HTTP especializadas
├── HelperServiceProvider.php
└── App/                     # Helpers contextuais
    ├── filesystem.php       # Paths do sistema
    ├── dates.php            # Operações com Carbon
    ├── auth.php             # Autenticação (Guard/Auth)
    ├── environment.php      # Detecção de ambiente
    ├── forms.php            # Helpers de formulário
    ├── dt_smart_search.php  # Pesquisa inteligente de datas
    ├── app.php              # Cache, session, VAT
    └── modules.php          # Auto-loader de helpers de módulos
```

---

## Global Helpers

Localização: `Modules/Common/Helpers/Helpers.php`

### Container & Configuração

#### `app($abstract = null, array $parameters = [])`
Acede ao Service Container (DI).

```php
$container = app();                    // Instância Container
$service = app(UserRepository::class); // Resolve dependência
```

#### `config($path, $default = null)`
Lê configurações com dot-notation. Suporta arrays para definir valores.

```php
$debug = config('app.debug');
config(['app.timezone' => 'Europe/Lisbon']); // Define valor
```

#### `envVar($key, $default = null)`
Lê variáveis de ambiente (.env) com **conversão automática de tipos**.
- `"true"` → `true` (boolean)
- `"null"` → `null`
- `"(empty)"` → `""`

```php
$debug = envVar('APP_DEBUG', false); // bool
```

#### `value($value)`
Resolve Closures. Se não for Closure, retorna o valor.

```php
$val = value(fn() => expensive_computation()); // Executa closure
$val = value('simple');                        // Retorna 'simple'
```

---

### Request & Response

#### `request($key = null, $default = null)`
Acede ao Request atual.

```php
$request = request();           // Instância Request
$name = request('name', 'N/A'); // Input específico
$only = request(['id', 'name']); // Múltiplos inputs
```

#### `response($content = '', $status = 200, $headers = [])`
Cria Response HTTP.

```php
return response('OK', 200);
return response()->json(['data' => $result]);
```

#### `abort($code, $message = '', $headers = [])`
Para execução com erro HTTP.

```php
abort(404, 'Recurso não encontrado');
```

#### `abortIf($condition, $code, $message = '')`
Abort condicional. Mais limpo que `if (...) abort(...)`.

```php
abortIf(!$user->canEdit($post), 403);
```

#### `abortUnless($condition, $code, $message = '')`
Inverso de `abortIf`.

```php
abortUnless(Auth::check(), 401);
```

#### `isApi()`
Detecta se request é API (JSON headers ou URI `/api/`).

```php
if (isApi()) {
    return response()->json($error);
}
return view('error');
```

#### `csrf_token()`
Gera/obtém token CSRF da sessão.

#### `route($name, $parameters = [], $absolute = true)`
Gera URL de rota nomeada.

```php
$url = route('users.show', ['id' => 5]);
```

#### `url($path = null, $parameters = [])`
Gera URL absoluta.

```php
$home = url('/');
$asset = url('css/app.css');
```

#### `websiteUrl($path = null)`
URL base do site (configurado em `app.url`).

---

### Strings

#### `camelCase($str)`, `snakeCase($str)`, `kebabCase($str)`, `studlyCase($str)`, `titleCase($str)`
Conversões rápidas de case.

```php
camelCase('user_name');  // userName
snakeCase('UserName');   // user_name
kebabCase('userName');   // user-name
studlyCase('user_name'); // UserName
titleCase('hello world'); // Hello World
```

#### `replace($search, $replace, $subject)`
Wrapper para `str_replace` com null-safety.

#### `randomString($length = 16)`
Gera string alfanumérica aleatória.

```php
$token = randomString(32);
```

---

### Validação & Encoding

#### `isJson($string)`
Valida se string é JSON válido.

```php
if (isJson($payload)) {
    $data = json_decode($payload, true);
}
```

#### `isBoolean($value)`
Verifica se valor é ou representa boolean (`true`, `false`, `0`, `1`, `"true"`, `"false"`).

#### `forceBoolean($value)`
Força conversão para boolean. Strings como `"false"`, `"no"`, `"off"`, `"0"` retornam `false`.

```php
$active = forceBoolean(request('active')); // Garante tipo bool
```

#### `isBase64($string)` e `isBase64Image($string)`
Validação de Base64, com verificação de magic bytes para imagens.

#### `utf8Encode($str)` e `utf8Decode($str)`
Conversão ISO-8859-1 ↔ UTF-8.

#### `encoding($from, $to, $string)`
Wrapper para `mb_convert_encoding`.

#### `sanitizeUrl($url)`
Limpa URL, adiciona protocolo se faltar, valida formato.

---

### Logging

#### `logger($path = null)`
Retorna instância Monolog. Usa path configurado ou customizado.

```php
logger()->info('Operação concluída');
logger('/path/custom.log')->error('Falha crítica');
```

#### `loggerBatch($level, ...$logs)`
Log de múltiplas mensagens em batch (performance I/O).

```php
loggerBatch('error', 'Erro 1', 'Erro 2', json_encode($context));
```

#### `loggerException(...$logs)`
Alias para `loggerBatch('error', ...)`.

#### `queueLogger($level, ...$logs)`
Logging específico para workers de queue.

#### `requestDebugger($request = null)`
Despeja detalhes completos do request no log (headers, body, query).

#### `debugBacktrace($limit = 10)`
Stack trace formatado para debug.

#### `logToSystem($message)`
Escreve em `error_log` e `syslog` (para monitorização de sistema).

---

### Notificações

#### `notify($message, $subject, $userId, $date, $url, $type, $options)`
Sistema unificado de notificações. Suporta múltiplos canais (Database, Mattermost).

```php
notify(
    message: 'Novo pedido recebido',
    subject: 'Pedido #123',
    userId: 5,
    type: 'info',
    options: ['channels' => [DatabaseChannel::class]]
);
```

---

### Diversos

#### `fake($locale = null)`
Instância Faker para dados de teste.

```php
$name = fake()->name;
$email = fake('pt_PT')->email;
```

#### `enumValue($value, $default = null)` e `enum_value($value)`
Extrai valor de Backed/Unit Enums.

```php
$status = enumValue(OrderStatus::Pending); // 'pending'
```

#### `isStatusOk($status)`
Verifica se código HTTP está entre 200-299.

#### `htmlToWhatsapp($text)`
Converte HTML para formatação WhatsApp (`<b>` → `*texto*`).

#### `fileUrlHash($filePath)`
Gera URL com hash para download seguro via OGStorage.

#### `isCli()`
Detecta execução CLI.

#### `isAjaxServerRequest()`
Detecta se chamada vem do AjaxServer legacy.

---

## App Helpers

### Filesystem Paths

Localização: `App/filesystem.php`

| Função | Exemplo Retorno | Descrição |
|--------|-----------------|-----------|
| `rootPath($path)` | `/var/www/ogdevel/config` | Raiz do projeto |
| `modulePath($module, $path)` | `/.../Modules/Wmso/Routes` | Caminho de módulo |
| `filePath($path)` | `/.../_files/uploads` | Pasta `_files` |
| `cachePath($path)` | `/.../_cache/domain.com/routes.json` | Cache (por tenant) |
| `logPath($path)` | `/.../_logs/domain.com/app.log` | Logs (por tenant) |
| `viewPath($path)` | `/.../_views/admin` | Templates |
| `databasePath($path)` | `/.../_files/database` | Database files |
| `migrationsPath($path)` | `/.../Modules/database/migrations` | Migrations |
| `configPath($path)` | `/.../Modules/Common/Config` | Configurações |

#### `isFileIncluded($file)`
Verifica se ficheiro já foi incluído via `require/include`.

#### `setConstantInFile($file, $constant, $value)`
Altera valor de constante definida em ficheiro PHP.

---

### Date Helpers

Localização: `App/dates.php`

#### `now()`
Instância Carbon do momento atual.

#### `isDate($string)`
Valida string de data em múltiplos formatos (`Y-m-d`, `d/m/Y`, etc).

#### `formatDateToHuman($date, $format = 'd/m/Y')`
Formata para exibição.

```php
formatDateToHuman('2024-12-25'); // "25/12/2024"
```

#### `formatDateToDb($date, $withTime = false)`
Normaliza para formato MySQL.

```php
formatDateToDb('25/12/2024');       // "2024-12-25"
formatDateToDb('25/12/2024', true); // "2024-12-25 00:00:00"
```

#### `diffForHumans($date, $other = null)`
Texto relativo.

```php
diffForHumans('2024-12-20'); // "há 5 dias"
```

#### `isExpired($date)`
Verifica se data está no passado.

#### `getAgeFromDate($date)`
Calcula idade em anos.

#### `addWorkDays($date, $days)`
Adiciona dias úteis (ignora fins de semana).

#### `getFirstDayOfMonth($date)` e `getLastDayOfMonth($date)`
Limites do mês.

#### `isWorkDay($date)`
Verifica se não é fim de semana.

#### `getWorkDaysInMonth($date)`
Conta dias úteis no mês.

---

### Auth Helpers

Localização: `App/auth.php`

#### Estado de Autenticação
```php
authCheck()  // bool - está logado?
authUser()   // User|null - instância
authId()     // int|null - ID do utilizador
```

#### Permissões
```php
can('articles.create')  // Verifica permissão RBAC
tokenCan('read')        // Verifica ability do token API
```

#### Gestão de Tokens
```php
createApiToken($userId, $name, $abilities, $expiresIn)
spaToken($userId, $name, $reuseExisting)  // Reutiliza tokens válidos
webToken($name)                            // Token para sessão web
revokeToken()                              // Revoga token atual
revokeAllTokens()                          // Revoga todos
userTokens($userId)                        // Lista tokens
pruneTokens()                              // Limpa expirados
cleanupUserTokens($userId, $keepCount)     // Mantém N mais recentes
```

#### Login/Logout
```php
loginAttempt($username, $password)  // Tenta login
loginForSpa($username, $password)   // Login + cria token SPA
guardLogout()                        // Logout (revoga token)
```

#### Helpers de Script
```php
generateAuthScript($includeToken)   // JS com window.OG = {...}
renderAuthScript($includeToken)     // <script>...</script>
```

#### Debug
```php
guardDebug()  // Array com estado completo do Guard
```

---

### Environment & Forms

#### `environment()`
Retorna `'local'` ou `'production'` baseado no hostname.

#### `isLocal()`
Shortcut: `environment() === 'local'`.

#### `method_field($method)`
Gera campo hidden para method spoofing em forms HTML.

```php
echo method_field('DELETE');
// <input type="hidden" name="_method" value="DELETE">
```

---

### Smart Search (DataTables)

Localização: `App/dt_smart_search.php`

Parsing inteligente de datas para pesquisa em DataTables.

#### `parseSmartDate($value)`
Interpreta input de pesquisa e gera condição SQL apropriada.

```php
parseSmartDate('2024');      // YEAR(field) = 2024
parseSmartDate('12/2024');   // YEAR(...) = 2024 AND MONTH(...) = 12
parseSmartDate('25/12/2024'); // DATE(field) = '2024-12-25'
```

#### `detectDateField($field, $value)`
Detecta se campo é de data pelo nome (`*_at`, `data_*`, `dt_*`) ou pelo valor.

#### `replaceSqlParams($condition, $params)`
Substitui `?` por valores (escaped) para debug de queries.

---

## Classes Estáticas

### ArrayHelper

Classe: `Og\Modules\Common\Helpers\ArrayHelper`

#### Dot Notation (Acesso Profundo)

```php
// GET - Acesso seguro a arrays aninhados
$name = ArrayHelper::get($data, 'user.profile.name', 'default');

// SET - Define valores criando estrutura
ArrayHelper::set($data, 'config.db.host', 'localhost');

// EXISTS - Verifica existência
ArrayHelper::dotKeyExists('user.profile.name', $data);

// SEARCH com Wildcards
ArrayHelper::dotSearch('users.*.email', $data);
```

#### Transformações

```php
// FLATTEN - Achata array multidimensional
ArrayHelper::flatten([[1, 2], [3, [4, 5]]]);
// [1, 2, 3, 4, 5]

// DOT - Converte para notação ponto
ArrayHelper::dot(['a' => ['b' => 'c']]);
// ['a.b' => 'c']

// UNDOT - Reverte dot notation
ArrayHelper::undot(['a.b' => 'c']);
// ['a' => ['b' => 'c']]

// WRAP - Garante que é array
ArrayHelper::wrap('single');  // ['single']
ArrayHelper::wrap(['arr']);   // ['arr']
ArrayHelper::wrap(null);      // []
```

#### Filtragem

```php
// WHERE - Filtra com callback
ArrayHelper::where($users, fn($u) => $u['active']);

// FIRSTWHERE - Primeiro match
ArrayHelper::firstWhere($users, fn($u) => $u['role'] === 'admin');

// FILTER - Alias de where
ArrayHelper::filter($users, fn($v, $k) => $k > 0);

// REJECT - Inverso de filter (remove matches)
ArrayHelper::reject($users, fn($u) => $u['banned']);

// ONLY - Mantém apenas chaves especificadas
ArrayHelper::only($user, ['id', 'name']);

// EXCEPT - Remove chaves especificadas
ArrayHelper::except($user, ['password', 'token']);

// FILTERBYVALUES - Filtra por lista de valores
ArrayHelper::filterByValues($users, [1, 5, 9], 'id');
```

#### Manipulação

```php
// MAP - Preserva chaves
ArrayHelper::map($arr, fn($v) => $v * 2);

// MAPWITHKEYS - Permite alterar chaves
ArrayHelper::mapWithKeys($users, fn($u) => [$u['id'] => $u['name']]);

// PLUCK - Extrai coluna
ArrayHelper::pluck($users, 'name');        // ['Ana', 'Rui']
ArrayHelper::pluck($users, 'name', 'id');  // [1 => 'Ana', 2 => 'Rui']

// GROUPBY - Agrupa por chave
ArrayHelper::groupBy($orders, 'status');

// FORGET - Remove chaves (por referência)
ArrayHelper::forget($data, ['temp', 'cache']);
```

#### Validação

```php
// ISASSOCIATIVE - Verifica se é array associativo
ArrayHelper::isAssociative(['a' => 1]); // true
ArrayHelper::isAssociative([1, 2, 3]);  // false

// SUBSET - Verifica se é subconjunto
ArrayHelper::subset([1, 2], [1, 2, 3, 4]); // true

// VALUESEXISTASKEYS
ArrayHelper::valuesExistAsKeys(['id', 'name'], $columns);
```

---

### StringHelper

Classe: `Og\Modules\Common\Helpers\StringHelper`

#### Case Detection & Conversion

```php
StringHelper::detectCase('userName');   // 'camelCase'
StringHelper::detectCase('user_name');  // 'snake_case'
StringHelper::detectCase('UserName');   // 'PascalCase'
StringHelper::detectCase('user-name');  // 'kebab-case'

StringHelper::toSnakeCase('UserName');   // 'user_name'
StringHelper::toCamelCase('user_name');  // 'userName'
StringHelper::toPascalCase('user_name'); // 'UserName'
StringHelper::toKebabCase('UserName');   // 'user-name'

StringHelper::convertCase('userName', StringHelper::SNAKE_CASE);
StringHelper::isCase('userName', StringHelper::CAMEL_CASE); // true
```

#### Manipulação de Texto

```php
// SLUG - URL-friendly
StringHelper::slug('Café & Leite'); // 'cafe-leite'

// TRUNCATE - Preserva palavras
StringHelper::truncatePreserveWords('Lorem ipsum dolor', 10);
// 'Lorem...'

// SPLIT INTO WORDS
StringHelper::splitIntoWords('userName'); // ['user', 'name']

// EXTRACT NUMBERS
StringHelper::extractNumbers('Tel: (351) 912-345-678');
// '351912345678'

// REMOVE SPECIAL CHARS
StringHelper::removeSpecialCharacters('Hello@World!', ['@']);
// 'HelloWorld'

// IS ASCII
StringHelper::isAscii('Hello');  // true
StringHelper::isAscii('Olá');    // false

// UUID v4
$uuid = StringHelper::uuid();
```

---

### DataTableHelper

Classe: `Og\Modules\Common\Helpers\DataTableHelper`

#### `prepareParams($data, $returnAsArray = false)`
Converte payload do DataTables jQuery em parâmetros normalizados.

```php
$params = DataTableHelper::prepareParams($_POST);
// [
//   'page' => ['number' => 1, 'size' => 10],
//   'sort' => '-created_at',
//   'filter' => ['search' => 'termo', 'status' => 'active']
// ]
```

#### `returnToDataTable($draw, $totalLines, $data, $status = 200)`
Formata resposta JSON padrão do DataTables.

```php
return DataTableHelper::returnToDataTable(
    draw: (int)$_POST['draw'],
    totalLines: 150,
    data: $rows
);
```

#### `returnException($exception)` e `returnEmpty()`
Respostas de erro ou vazias mantendo formato DataTables.

---

### Response

Classe: `Og\Modules\Common\Helpers\Response`

#### Respostas de Conteúdo

```php
Response::json(['data' => $result], 200);
Response::text('Plain text content');
Response::html('<h1>Hello</h1>');
Response::jsonp('callback', ['data' => $result]);
```

#### Streaming

```php
// Stream genérico
Response::stream(function() {
    echo 'chunk1';
    flush();
    echo 'chunk2';
});

// Stream JSON
Response::streamJson(function() {
    echo json_encode($largeData);
});

// Stream Download (ficheiros grandes sem carregar em memória)
Response::streamDownload(function() {
    $handle = fopen('php://output', 'w');
    foreach (cursor() as $row) {
        fputcsv($handle, $row);
    }
}, 'export.csv');
```

#### Ficheiros

```php
// Download forçado
Response::download('/path/to/file.pdf', 'documento.pdf');

// Serve ficheiro inline (visualização no browser)
Response::file('/path/to/image.jpg');
```

#### Redirect

```php
Response::redirectTo('/dashboard', 302);
```

---

## Referência Rápida

| Categoria | Funções Principais |
|-----------|-------------------|
| **Container** | `app()`, `config()`, `envVar()` |
| **Request** | `request()`, `abort()`, `isApi()`, `isCli()` |
| **Response** | `response()`, `route()`, `url()` |
| **Auth** | `authCheck()`, `authUser()`, `can()`, `tokenCan()` |
| **Datas** | `now()`, `formatDateToHuman()`, `diffForHumans()` |
| **Paths** | `rootPath()`, `modulePath()`, `cachePath()` |
| **Logging** | `logger()`, `loggerBatch()`, `requestDebugger()` |
| **Arrays** | `ArrayHelper::get()`, `::set()`, `::pluck()` |
| **Strings** | `StringHelper::slug()`, `::toSnakeCase()` |
