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);
}
}
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.