@extends('layouts.docs') @section('title', 'Resources (Output DTOs) - OG Framework') @section('body')
API
O sistema de Resources implementa o padrão Resource/Transformer para serialização consistente de dados em APIs. Transforma dados brutos de entidades, base de dados e arrays numa representação JSON estruturada e previsível.
O fluxo de transformação passa pelos seguintes componentes:
{{-- SVG: Data Flow Diagram --}}| Padrão | Implementação |
|---|---|
| Resource Pattern | Classe base JsonResource com método toArray() |
| Factory Pattern | Métodos estáticos make() e collection() |
| Decorator Pattern | Traits adicionam comportamentos (validação, serialização) |
| Singleton Pattern | MissingValue::getInstance() para valores ausentes |
A classe JsonResource é o coração do sistema, fornecendo a estrutura base para transformação de dados.
use Og\Modules\Common\Resource\JsonResource;
final class UserOutputDTO extends JsonResource
{
public function toArray(): array
{
return [
'id' => $this->codempr,
'email' => $this->email,
'name' => $this->nome,
'short_name' => $this->nomeabreviado,
];
}
}
// Uso
$result = UserOutputDTO::make($userData);
// Resultado: ['id' => 1, 'email' => 'joao@ex.com', ...]
Transforma um único objecto/array. Retorna array vazio se $data for null.
Transforma listas. Detecta paginação automaticamente e inclui metadata.
O trait ConditionalAttributesTrait permite controlar quais campos aparecem na saída JSON.
public function toArray(): array
{
return [
// Sintaxe simples
'name' => $this->whenHas('name'),
// Com callback de transformação
'created_at' => $this->whenHas('created_at',
fn ($v) => Carbon::parse($v)->toIso8601String()
),
// Dot notation para dados aninhados
'city' => $this->whenHas('address.city'),
// DTO aninhado com lazy loading
'customer' => $this->whenHas('customer',
fn () => CustomerOutputDTO::make($this->customer)
),
// Com valor default
'status' => $this->whenHas('status', null, 'active'),
];
}
| Entrada | Resultado | Campo na Saída? |
|---|---|---|
| ['name' => 'João'] | 'João' | ✅ Sim |
| ['name' => ''] | MissingValue | ❌ Não |
| ['name' => null] | MissingValue | ❌ Não |
| [] (ausente) | MissingValue | ❌ Não |
return [
'id' => $this->id,
// Só aparece se documento estiver anulado
'cancelReason' => $this->when(
$this->resource['anulado'] === 'T',
$this->resource['cancel_reason']
),
// Só aparece para admins
'adminNotes' => $this->when(
auth()->user()?->isAdmin(),
fn () => $this->fetchAdminNotes()
),
// Com valor default
'badge' => $this->when($this->score >= 100, 'gold', 'standard'),
];
Importante: O operador spread (...) é obrigatório antes de merge() e mergeWhen().
return [
'id' => $this->id,
// Mescla múltiplos campos se for admin
...$this->mergeWhen($this->isAdmin, [
'email' => $this->email,
'permissions' => $this->permissions,
'last_login' => $this->last_login,
]),
// Com closure para lazy loading
...$this->mergeWhen($this->includeFinancials, fn () => [
'total_spent' => $this->orders()->sum('total'),
'credit_limit' => $this->credit_limit,
]),
];
O sistema inclui validação robusta de tipos para garantir integridade de dados na saída.
{{-- SVG: Type Validation Hierarchy --}}'id' => $this->isInteger('id'),
'name' => $this->isString('name'),
'active' => $this->isBoolean('active'),
'settings' => $this->isArray('settings'),
'tag_ids' => $this->isArrayOf('integer', 'tag_ids'),
'email' => $this->isEmail('email'),
'website' => $this->isNullOrUrl('website'),
'quantity' => $this->isPositiveInteger('quantity'),
O método collection() detecta automaticamente dados paginados e inclui metadata.
$users = [
['codempr' => 1, 'nome' => 'João'],
['codempr' => 2, 'nome' => 'Maria'],
];
UserOutputDTO::collection($users);
// [
// ['id' => 1, 'name' => 'João'],
// ['id' => 2, 'name' => 'Maria'],
// ]
$paginated = [
'items' => [...],
'per_page' => 10,
'total' => 50,
'current_page' => 1,
];
// Resultado inclui 'meta':
// {
// "items": [...],
// "meta": {
// "current_page": 1,
// "per_page": 10,
// "total": 50,
// ...
// }
// }
// Usar whenHas() para segurança
'name' => $this->whenHas('name'),
// DTO aninhado com lazy loading
'customer' => $this->whenHas('customer',
fn () => CustomerDTO::make($this->customer)
),
// Acesso directo pode falhar
'name' => $this->resource['name'],
// DTO sem lazy loading pode lançar erro
'customer' => CustomerDTO::make($this->customer),
{Domain}{Entity}OutputDTO (ex: CrmAppointmentOutputDTO)Og\Modules\{Module}\{Domain}\DTOssnake_case (ex: employee_id, created_at)