alysontrizotto / laravel-ddl-crud
Gerador de CRUD a partir de DDL (SQL) para Laravel 12.
Requires
- php: >=8.2
- illuminate/console: ^12.0
- illuminate/filesystem: ^12.0
- illuminate/support: ^12.0
This package is auto-updated.
Last update: 2025-08-15 19:20:52 UTC
README
Gere um CRUD completo (Migration, Model, Service, Requests, Resource, Controller API, Factory e Testes) a partir de um arquivo DDL (.sql) no Laravel 12.
- Framework alvo: Laravel 12
- Stubs personalizáveis:
stubs/cascade/
- Código organizado por geradores dedicados (SRP) e utilitários de suporte
Recursos
- Entrada via DDL: lê múltiplas
CREATE TABLE
no mesmo arquivo - Geração completa: migrations, models, services, requests, resources, controllers, factories, testes unit/feature
- Stubs sobrescrevíveis: personalize a estrutura gerada publicando os stubs
- Heurísticas úteis: mapeamento de tipos, inferência de
fillable
,casts
, validações básicas - Separação de responsabilidades: parsing, geração e escrita extraídos para classes dedicadas
- Rotas automáticas: gera rotas REST em
routes/api.php
com marcadores por domínio e idempotência (não duplica nem apaga rotas já existentes)
Sumário
- Pré-requisitos
- Instalação
- Publicar stubs (opcional)
- Uso
- Exemplo de DDL suportada
- O que é gerado (por tabela)
- Rotas
- Customização
- Testes
- Solução de problemas
- Licença
Pré-requisitos
- PHP e extensões do Laravel 12
- Projeto Laravel instalado e funcional
- Arquivo DDL (.sql) com instruções
CREATE TABLE ... (...);
Instalação
Instale via Composer:
composer require alysontrizotto/laravel-ddl-crud
Este pacote suporta auto-discovery no Laravel. O service provider exposto é
AlysonTrizotto\DdlCrud\DdlCrudServiceProvider
(wrapper que aponta para
AlysonTrizotto\DdlCrud\Providers\DdlCrudServiceProvider
).
Publicar stubs (opcional)
Publique os stubs para customização no seu projeto (tag stubs
):
php artisan vendor:publish --tag=stubs
Os stubs serão publicados em stubs/cascade/
na raiz do projeto.
Stubs utilizados pelo gerador (se não existirem no projeto, um fallback interno será usado):
stubs/cascade/model.stub
stubs/cascade/service.stub
stubs/cascade/request.stub
stubs/cascade/resource.stub
stubs/cascade/controller.api.stub
stubs/cascade/factory.stub
stubs/cascade/unit.model.test.stub
stubs/cascade/unit.service.test.stub
stubs/cascade/feature.controller.test.stub
Uso
Você pode executar de duas formas:
- Interativo (responder aos prompts):
php artisan make:crud-from-ddl
Prompts:
- Domínio (CamelCase) — exemplo:
Checklist
ouTrip
. - Caminho do arquivo DDL — exemplo:
D:/Alyson/ddl/checklist.sql
.
- Direto por argumentos (sem prompts):
php artisan make:crud-from-ddl Checklist D:/Alyson/ddl/checklist.sql
Opções úteis:
--no-routes
— não gerar/adicionar rotas noroutes/api.php
.--route-prefix=...
— adicionaRoute::prefix('...')->group(...)
envolvendo as rotas geradas (ex.:--route-prefix=v1
).--route-name=...
— define/override o slug da rota gerada (ex.:--route-name=annotations
).--middleware=a|b|c
— aplica middlewares na rota/agrupamento (ex.:--middleware=auth:sanctum|throttle:60,1
).--name-prefix=...
— prefixo de nomes de rotas (ex.:--name-prefix=checklist.
→checklist.photo-annotations.index
).--only=a,b
— limita métodos do resource (ex.:--only=index,show
).--except=a,b
— exclui métodos do resource (ex.:--except=destroy
).--nested=...
— slug aninhado (ex.:--nested=orders/{order}/items
).
Exemplo de DDL suportada (Postgres-like ou MySQL simples)
CREATE TABLE checklist.photo_annotations ( id uuid primary key, checklist_id uuid not null, label varchar(150) not null, metadata jsonb, created_at timestamptz, updated_at timestamptz );
O parser identifica o schema opcional (checklist
), o nome da tabela (photo_annotations
), colunas, tipos, nulos e chave primária.
O que é gerado (por tabela)
- Migration em
database/migrations/*_create_{schema_}{tabela}_table.php
- Cria schema (se informado) e a tabela com colunas mapeadas a partir da DDL
- Model em
app/Models/{Domínio}/{Model}.php
use HasFactory;
use SoftDeletes;
(apenas se a DDL contiverdeleted_at
)$table
,$primaryKey
,$incrementing
,$keyType
$fillable
(excluicreated_at
,updated_at
,deleted_at
)$casts
(json/jsonb/arrays mapeados paraarray
)- Métodos:
store(array $data)
eapplyUpdate(array $data)
scopeFilter(array $filters)
- Service em
app/Services/{Domínio}/{Model}Service.php
- Focado em regra de negócio
- Usa
Model::store
,$model->applyUpdate
,$model->delete()
- Métodos:
paginate
,find
,create
,update
,delete
- Requests em
app/Http/Requests/{Domínio}/{Model}/
Store{Model}Request.php
eUpdate{Model}Request.php
- Regras inferidas da DDL (required, tipos básicos, unique quando aplicável)
- Resource em
app/Http/Resources/{Domínio}/{Model}Resource.php
- Constrói a resposta padronizada com os campos da tabela
- Controller API em
app/Http/Controllers/API/{Domínio}/{Model}Controller.php
- Endpoints:
index
,store
,show
,update
,destroy
- Retorna
Resource
nas respostas
- Endpoints:
- Factory em
database/factories/{Domínio}/{Model}Factory.php
- Namespace:
Database\\Factories\\{Domínio}
$model
usando classe importada- PHPDoc
@extends Factory<{Model}>
usando nome curto
- Namespace:
- Testes
- Unit (Model): valida configuração do model
- Unit (Service): cobre paginação e fluxo CRUD com asserts no banco
- Feature (Controller): cobre CRUD completo via HTTP, status codes corretos, usa
apiResource
Exemplo de execução
php artisan make:crud-from-ddl # Informe o domínio: Checklist # Informe o caminho da DDL: D:/Alyson/sql/checklist_tables.sql
Arquivos esperados (exemplo Checklist
+ tabela photo_annotations
):
database/migrations/2025_08_13_000000_create_checklist_photo_annotations_table.php
app/Models/Checklist/PhotoAnnotation.php
app/Services/Checklist/PhotoAnnotationService.php
app/Http/Requests/Checklist/PhotoAnnotation/StorePhotoAnnotationRequest.php
app/Http/Requests/Checklist/PhotoAnnotation/UpdatePhotoAnnotationRequest.php
app/Http/Resources/Checklist/PhotoAnnotationResource.php
app/Http/Controllers/API/Checklist/PhotoAnnotationController.php
Rotas
As rotas REST são adicionadas automaticamente ao arquivo routes/api.php
com marcadores por domínio e lógica idempotente (não duplica rotas existentes e não apaga nada fora dos blocos do pacote).
Exemplo do que será inserido:
use Illuminate\Support\Facades\Route; // BEGIN: DDL-CRUD routes [Checklist] Route::apiResource('photo-annotations', \App\Http\Controllers\API\Checklist\PhotoAnnotationController::class); // END: DDL-CRUD routes [Checklist]
Com --route-prefix=v1
:
// BEGIN: DDL-CRUD routes [Checklist] Route::prefix('v1')->group(function () { Route::apiResource('photo-annotations', \App\Http\Controllers\API\Checklist\PhotoAnnotationController::class); }); // END: DDL-CRUD routes [Checklist]
Notas:
- O slug da rota é derivado do nome da tabela (
photo_annotations
→photo-annotations
). - Se o arquivo
routes/api.php
não existir, ele será criado. - Use
--no-routes
para pular a etapa de rotas.
Exemplos de flags de rota
- Somente leitura com prefixo e middlewares:
php artisan make:crud-from-ddl Checklist ddl.sql \
--route-prefix=v1 \
--middleware=auth:sanctum|throttle:60,1 \
--only=index,show
- Nome de rota prefixado e slug customizado:
php artisan make:crud-from-ddl Checklist ddl.sql \ --name-prefix=checklist. \ --route-name=annotations
- Rota aninhada:
php artisan make:crud-from-ddl Orders ddl.sql --nested=orders/{order}/items
Observações:
- Para
--middleware
, separe múltiplos middlewares com|
ou;
(não use vírgulas, pois podem fazer parte de parâmetros como emthrottle:60,1
). - Para
--nested
, informe o slug final desejado (com placeholders, se necessário). O gerador usa esse slug diretamente noapiResource
.
Remoção de rotas (segura por domínio)
Para remover o bloco de rotas gerado para um domínio (entre BEGIN/END
), use:
php artisan ddl-crud:routes:remove Checklist
Esse comando remove apenas o bloco do domínio informado, preservando o restante de routes/api.php
.
Customização
Edite os stubs em stubs/cascade/
para moldar o padrão do seu projeto:
- Adicionar/remover campos na resposta do
Resource
- Ajustar validações nos
Requests
- Adaptar
Service
para suas regras de negócio - Evoluir o
Model
(relations, mutators, etc.)
Observações
- O comando valida domínio (CamelCase) e lê múltiplas tabelas no mesmo arquivo DDL.
- Para tipos não suportados no Schema do Laravel, o gerador faz fallback para
string(36)
(ex.: uuid) e outros mapeamentos simples. - Caso você utilize MySQL, ajuste os tipos/timezones nas DDLs conforme o seu banco.
- Fábricas são geradas por domínio:
database/factories/{Domínio}/{Model}Factory.php
. Isso permite ao Laravel resolver automaticamente a factory de models namespaced. - O Controller usa vinculação de rota por nome de parâmetro; exemplo: para
PhotoAnnotationController
, as assinaturas sãoshow(PhotoAnnotation $photoAnnotation)
,update(PhotoAnnotation $photoAnnotation, ...)
,destroy(PhotoAnnotation $photoAnnotation)
. - Em SQLite, colunas JSON podem ser persistidas como texto. Os testes gerados evitam comparar diretamente campos JSON com
assertDatabaseHas
para prevenir falsos negativos. - Para
SoftDeletes
, incluadeleted_at
na DDL para o gerador adicionar o trait ao Model e os asserts de soft delete aos testes. Caso seu schema inicial não tenhadeleted_at
, crie uma migration complementar adicionandosoftDeletes()
.
Testes
- Configure
.env.testing
e rode as migrations de teste. - Execute a suíte:
php artisan test
.
Se precisar, abra uma issue ou peça para ajustar os stubs para novas regras do seu domínio.
Problemas comuns
- "Command cannot have an empty name": confira a assinatura do comando
make:crud-from-ddl
emapp/Console/Commands/MakeCrudFromDdl.php
. - Arquivo DDL inválido: certifique-se de terminar cada
CREATE TABLE ... (...);
com;
e usar sintaxe consistente.