Referência v3
Voltar à plataforma

Estrutura Completa do Módulo

Entenda cada arquivo gerado pelo scaffold e como usá-los para construir módulos robustos.

Visão geral

Ao criar um módulo com scaffold, a IDE gera 16 arquivos organizados em camadas. Você tem total liberdade para adicionar, remover ou modificar qualquer arquivo.

MeuModulo/
├── Controllers/MeuModuloController.php   ← Endpoints HTTP (CRUD)
├── Services/MeuModuloService.php         ← Lógica de negócio
├── Repositories/MeuModuloRepository.php  ← Acesso ao banco (PDO)
├── Entities/MeuModulo.php                ← Entidade de domínio
├── DTOs/
│   ├── CreateMeuModuloDTO.php            ← Dados de entrada (criação)
│   └── UpdateMeuModuloDTO.php            ← Dados de entrada (atualização)
├── Middlewares/MeuModuloMiddleware.php   ← Middleware próprio
├── Validators/MeuModuloValidator.php     ← Validação e sanitização
├── Exceptions/MeuModuloException.php     ← Exceções de domínio
├── Helpers/MeuModuloHelper.php           ← Funções utilitárias
├── Config/config.php                     ← Configurações do módulo
├── Database/
│   ├── connection.php                    ← Seleção de banco
│   ├── Migrations/...                    ← Criação de tabelas
│   └── Seeders/MeuModuloSeeder.php       ← Dados iniciais
├── Routes/web.php                        ← Definição de rotas
└── README.md                             ← Documentação

Controller — Endpoints HTTP

O controller recebe a requisição, valida os dados e retorna a resposta. Dependências são injetadas automaticamente pelo container.

final class ProdutoController
{
    public function __construct(
        private readonly ProdutoService $service
    ) {}

    public function listar(Request $request): Response
    {
        $page = max(1, (int) ($request->query['page'] ?? 1));
        return Response::json($this->service->listar($page, 20));
    }

    public function criar(Request $request): Response
    {
        $erro = ProdutoValidator::validarCriacao($request->body);
        if ($erro !== null) {
            return Response::json(['error' => $erro], 422);
        }
        $item = $this->service->criar(ProdutoValidator::sanitizar($request->body));
        return Response::json(['produto' => $item], 201);
    }
}

Service — Lógica de negócio

O service contém as regras de negócio e orquestra o repository. Nunca acessa o banco diretamente.

final class ProdutoService
{
    public function __construct(
        private readonly ProdutoRepository $repository
    ) {}

    public function listar(int $page = 1, int $perPage = 20): array
    {
        $total = $this->repository->count();
        $items = $this->repository->findPaginated($page, $perPage);
        return ['data' => $items, 'total' => $total, 'page' => $page];
    }

    public function criar(array $data): array
    {
        return $this->repository->create($data);
    }
}

Repository — Acesso ao banco

O repository encapsula todas as queries SQL. Recebe PDO via injeção de dependência.

final class ProdutoRepository
{
    private string $table = 'produtos';

    public function __construct(private readonly PDO $pdo) {}

    public function findPaginated(int $page, int $perPage): array
    {
        $stmt = $this->pdo->prepare(
            "SELECT * FROM {$this->table} ORDER BY criado_em DESC LIMIT ? OFFSET ?"
        );
        $stmt->execute([$perPage, ($page - 1) * $perPage]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function create(array $data): array
    {
        $id = bin2hex(random_bytes(16));
        $this->pdo->prepare(
            "INSERT INTO {$this->table} (id, nome, preco) VALUES (?, ?, ?)"
        )->execute([$id, $data['nome'], $data['preco']]);
        return $this->findById($id) ?? ['id' => $id];
    }
}

Validator — Validação e sanitização

final class ProdutoValidator
{
    public static function validarCriacao(array $data): ?string
    {
        if (empty(trim($data['nome'] ?? ''))) return 'Nome é obrigatório.';
        if (!isset($data['preco']) || $data['preco'] <= 0) return 'Preço deve ser positivo.';
        return null;
    }

    public static function sanitizar(array $data): array
    {
        return [
            'nome'  => trim(strip_tags($data['nome'] ?? '')),
            'preco' => (float) ($data['preco'] ?? 0),
        ];
    }
}

Exception — Erros de domínio

final class ProdutoException extends \DomainException
{
    private int $statusCode;

    public function __construct(string $message, int $statusCode = 400)
    {
        parent::__construct($message);
        $this->statusCode = $statusCode;
    }

    public function getStatusCode(): int { return $this->statusCode; }

    public static function naoEncontrado(): self
    {
        return new self('Produto não encontrado.', 404);
    }
}
Liberdade total

Você pode criar quantas classes, pastas e arquivos quiser dentro do módulo. O scaffold é apenas um ponto de partida. Adicione Events, Observers, Jobs, Caches, ou qualquer padrão que seu projeto precisar.