@extends('layouts.docs') @section('title', 'Web Layer System - OG Framework') @section('body')
HTTP & API
Arquitectura moderna e extensível para processamento de requests HTTP. Integra Http System, Middleware Pipeline e Routing System com mais de 75 controllers implementados.
O sistema oferece duas classes base com diferentes níveis de integração:
Acesso directo aos objectos globais do sistema legacy:
class BaseController
{
public Aplication $application; // Instância da aplicação
public $user; // Utilizador autenticado
public $modules; // Sistema de módulos legacy
public $database; // Instância de database
public function __construct()
{
global $a, $u, $m, $db;
$this->application = $a;
$this->user = $u;
$this->modules = $m;
$this->database = $db;
}
}
// Uso típico
class CustomerController extends BaseController
{
public function index(): Response
{
$customers = $this->modules['entidades']->listaClientesAPI(limit: 250);
return response()->json($customers);
}
}
Estende BaseClass e adiciona suporte a authorization policies:
class Controller extends BaseClass
{
protected array $policies = [];
protected string $policy;
public function authorize(string $method, string $modelClassName, ?array $params = null): void
{
// 1. Usa $this->policy se definida
// 2. Procura no mapa $this->policies
// 3. Inferência automática: Controller → Policy
// UserController → UserPolicy
// Og\Crm\Controllers\AppointmentController → Og\Crm\Policies\AppointmentPolicy
}
}
class AppointmentController extends Controller
{
public function index(AppointmentsIndexRequest $request): Response
{
// 1. Autorização via Policy
$this->authorize('viewAny', static::class);
// 2. Dados validados
$validData = $request->validated();
// 3. Delegar para Action
$appointments = (new GetAppointments())($validData);
return response()->json($appointments);
}
public function create(AppointmentsCreateRequest $request, CreateAppointment $action): Response
{
$this->authorize('create', static::class);
[$appointment, $statusCode] = $action->execute($request->validated());
return response()->json($appointment, $statusCode);
}
}
Padrões: Request Classes • Action Classes • Policy Authorization • Dependency Injection
Estende Symfony\Component\HttpFoundation\Request com API rica para HTTP:
// Cria Request a partir de superglobals PHP
$request = Request::capture();
// Processa JSON automaticamente
if (str_contains($headers->get('CONTENT_TYPE'), 'application/json')) {
$data = json_decode($request->getContent(), true);
$request->request->replace($data);
}
// Suporta method override (_method)
// POST com _method=PUT → $request->method() retorna 'PUT'
// Obter apenas campos específicos
$data = $request->only(['name', 'email', 'user.profile.bio']);
// Excluir campos
$data = $request->except(['password', 'password_confirmation']);
// Verificações
$request->filled('name'); // Campo preenchido?
$request->missing('token'); // Campo ausente?
$request->has(['email']); // Campos existem?
$request->ajax(); // É AJAX?
$request->expectsJson(); // Espera JSON?
// Merge de dados
$request->merge(['active' => true]);
$request->mergeIfMissing(['role' => 'user']);
$request->header('X-API-KEY'); // Header específico
$request->bearerToken(); // Extract Bearer token
$request->cookie('session_id'); // Cookies
$request->user(); // Utilizador autenticado
$request->all(); // Inputs + ficheiros
$request->remove('temp_field'); // Remover campo
// JSON Response
return response()->json(['users' => $users], 200);
// XML Response
return response()->xml($xmlContent);
// Text Response
return response()->text('Plain text content');
// Redirect com flash data
return response()->redirect('/dashboard')->with('message', 'Welcome!');
// No Content (204)
return response()->noContent();
// Definir headers
return response()->header('X-Custom', 'value')->json($data);
// Integração Legacy
response()->sendAjax($content); // Via AjaxServer
response()->ogJson($data, 1000); // Formato OfficeGest
O MiddlewareManager implementa o Pipeline Pattern para processamento de middlewares:
interface MiddlewareInterface
{
public function handle(Request $request, Closure $next, mixed $params = null): mixed;
}
class ValidateApiKeyMiddleware implements MiddlewareInterface
{
public function handle(Request $request, Closure $next, mixed $params = null): mixed
{
$apiKey = $request->header('X-API-KEY');
if (!$this->isValidApiKey($apiKey)) {
return response()->json(['error' => 'Invalid API key'], 401);
}
// Adicionar info ao request
$request->merge(['api_client' => $this->getClientByKey($apiKey)]);
return $next($request);
}
}
class ThrottleMiddleware implements MiddlewareInterface
{
public function handle(Request $request, Closure $next, mixed $params = null): mixed
{
// Params: ['60', '1'] de 'throttle:60,1'
$maxAttempts = (int) ($params[0] ?? 60);
$decayMinutes = (int) ($params[1] ?? 1);
if ($this->tooManyAttempts($key, $maxAttempts)) {
return response()->json(['error' => 'Too many requests'], 429);
}
return $next($request);
}
}
// Uso
Route::middleware(['throttle:100,1'])->group(function() {
Route::get('/api/heavy', 'ApiController@heavy');
});
Padrão para isolar lógica de negócio em classes reutilizáveis e testáveis:
class CreateAppointment
{
public function execute(array $data): array
{
// Validação de regras de negócio
$this->validateBusinessRules($data);
// Criar agendamento
$appointment = Appointment::create([
'customer_id' => $data['customer_id'],
'scheduled_at' => Carbon::parse($data['scheduled_at']),
'duration' => $data['duration'] ?? 60,
'notes' => $data['notes'] ?? null,
]);
// Enviar notificação
$appointment->customer->notify(new AppointmentCreated($appointment));
return [$appointment->toArray(), Response::HTTP_CREATED];
}
}
// Uso no Controller (injectado automaticamente via DI)
public function create(AppointmentsCreateRequest $request, CreateAppointment $action): Response
{
[$appointment, $statusCode] = $action->execute($request->validated());
return response()->json($appointment, $statusCode);
}
Benefícios:
Controllers podem incluir anotações OpenAPI para documentação automática da API:
/**
* @OA\Tag(name="Customers")
*/
class CustomerController extends Controller
{
/**
* @OA\Get(
* path="/entities/customers",
* summary="List all customers",
* tags={"Customers"},
* @OA\Parameter(name="name", in="query", @OA\Schema(type="string")),
* @OA\Parameter(name="taxId", in="query", @OA\Schema(type="string")),
* @OA\Response(response=200, description="OK",
* @OA\JsonContent(type="array", @OA\Items(ref="#/components/schemas/CustomerOutputDTO")))
* )
*/
public function index(CustomerIndexRequest $request): Response
{
// ...
}
/**
* @OA\Post(
* path="/entities/customers",
* summary="Create a new customer",
* tags={"Customers"},
* @OA\RequestBody(ref="#/components/requestBodies/CreateCustomerRequest"),
* @OA\Response(response=201, description="Created"),
* @OA\Response(response=422, description="Unprocessable Entity")
* )
*/
public function create(CustomerCreateRequest $request): Response
{
// ...
}
}
Utilitário para carregar relações dinamicamente em arrays de dados:
use Og\Modules\Common\Database\Relations\RelationshipLoader;
// Carregar relações para array de clientes
$customers = (new RelationshipLoader())->load(
$customers, // Array de dados
['country', 'contacts'], // Relações a carregar
EntityRelation::class // Classe que define as relações
);
// Uso típico em controller
public function show(int $id): Response
{
$customer = $this->modules['entidades']->loadCliente($id);
// Carrega relação 'country' automaticamente
$customer = (new RelationshipLoader())->load(
$customer,
['country'],
EntityRelation::class
);
return response()->json(EntitiesCustomerOutputDTO::make($customer));
}
Classe para gestão de ficheiros enviados via upload:
$file = $request->file('avatar');
// Informações do ficheiro
$file->getClientOriginalName(); // 'photo.jpg'
$file->getClientMimeType(); // 'image/jpeg'
$file->getSize(); // 245632 bytes
$file->getExtension(); // 'jpg'
// Mover para destino
$file->move('/uploads/avatars', 'user_123.jpg');
// Store com nome automático
$path = $file->store('avatars'); // 'avatars/abc123.jpg'
// Validação (ver página Validação)
$rules = [
'avatar' => ['uploaded', 'is_image', 'max_size[2048]'],
];
O sistema de rotas é documentado em detalhe na sua própria página:
Executa handlers com Dependency Injection automática:
readonly class RouteDispatcher
{
public function dispatch(callable|string $handler, array $routeParams = []): mixed
{
return match (true) {
$handler instanceof Closure => $this->dispatchClosure($handler, $routeParams),
$this->isValidHandlerString($handler) => $this->dispatchController($handler, $routeParams),
default => throw new RuntimeException('Invalid route handler.')
};
}
// Resolução automática de parâmetros:
// - Route params: {id}, {slug} → conversão automática de tipos (int, bool, float)
// - Request injection: Request $request
// - BaseRequest validation: AppointmentsCreateRequest $request
// - Dependency injection: CreateAppointment $action
}
use Og\Modules\Common\Facades\Request;
use Og\Modules\Common\Facades\Response;
use Og\Modules\Common\Facades\Route;
// Request Facade
$data = Request::all();
$name = Request::get('name', 'default');
$token = Request::bearerToken();
$user = Request::user();
// Response Facade
return Response::json(['success' => true]);
return Response::redirect('/dashboard')->with('message', 'OK');
// Route Facade
Route::get('/users', 'UserController@index')->name('users.index');
Route::middleware(['auth'])->group(function() { /* ... */ });
| Domínio | Qty | Exemplos |
|---|---|---|
| Ams | 18 | BoardingGateController, TransportServicesController |
| Entidade | 9 | CustomerController, EmployeeController, SupplierController |
| Crm | 5 | AppointmentController, ParameterController, AbcController |
| ChatFlow | 3 | ChatFlowController, WhatsappController |
| Tickets | 3 | TicketController, TicketMessageController |
| Outros | 37+ | AI, Auth, Developer, SAFT, Portal... |