1. Instalando a ferramenta ent
Para instalar a ferramenta de geração de código ent
, você precisa seguir estes passos:
Primeiro, certifique-se de que o ambiente Go language esteja instalado no seu sistema. Em seguida, execute o seguinte comando para obter a ferramenta ent
:
go get -d entgo.io/ent/cmd/ent
Este comando irá baixar o código da ferramenta ent
, mas não o compilará e instalará imediatamente. Se deseja instalar o ent
no diretório $GOPATH/bin
para que possa usá-lo em qualquer lugar, também precisará executar o seguinte comando:
go install entgo.io/ent/cmd/ent
Após a conclusão da instalação, você pode verificar se a ferramenta ent
está corretamente instalada e ver os comandos disponíveis e instruções executando ent -h
.
2. Inicializando o Esquema
2.1 Inicializando o Modelo Usando ent init
Criar um novo arquivo de esquema é o primeiro passo para começar a usar o ent
para a geração de código. Você pode inicializar o modelo de esquema executando o seguinte comando:
go run -mod=mod entgo.io/ent/cmd/ent new User Pet
Este comando criará dois novos arquivos de esquema: user.go
e pet.go
, e os colocará no diretório ent/schema
. Se o diretório ent
não existir, este comando também o criará automaticamente.
Executar o comando ent init
no diretório raiz do projeto é uma boa prática, pois ajuda a manter a estrutura e clareza do diretório do projeto.
2.2 Estrutura do Arquivo de Esquema
No diretório ent/schema
, cada esquema corresponde a um arquivo de origem da linguagem Go. Os arquivos de esquema são onde você define o modelo do banco de dados, incluindo os campos e arestas (relacionamentos).
Por exemplo, no arquivo user.go
, você pode definir um modelo de usuário, incluindo campos como nome de usuário e idade, e definir o relacionamento entre usuários e pets. Da mesma forma, no arquivo pet.go
, você definiria o modelo de pet e seus campos relacionados, como nome do pet, tipo e o relacionamento entre pets e usuários.
Esses arquivos serão usados pelo ent
para gerar código Go correspondente, incluindo código cliente para operações de banco de dados e operações CRUD (Create, Read, Update, Delete).
// ent/schema/user.go
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User define o esquema para a entidade de usuário.
type User struct {
ent.Schema
}
// O método Fields é usado para definir os campos da entidade.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").Unique(),
field.Int("age").Positive(),
}
}
// O método Edges é usado para definir as associações da entidade.
func (User) Edges() []ent.Edge {
// As associações serão explicadas em mais detalhes na próxima seção.
}
Os arquivos de esquema ent
utilizam tipos e funções da linguagem Go para declarar a estrutura do modelo do banco de dados, incluindo os campos e relacionamentos entre modelos, e utilizam a API fornecida pelo framework ent
para definir essas estruturas. Essa abordagem torna o ent
muito intuitivo e fácil de estender, enquanto aproveita a digitação forte da linguagem Go.
3.1 Executando a Geração de Código
Executar ent generate
para gerar código é um passo crucial no framework ent
. Com este comando, o ent
irá gerar arquivos de código Go correspondentes com base nos esquemas definidos, facilitando o trabalho de desenvolvimento subsequente. O comando para a execução da geração de código é direto:
go generate ./ent
O comando acima precisa ser executado no diretório raiz do projeto. Quando o go generate
é chamado, ele buscará todos os arquivos Go contendo anotações específicas e executará os comandos especificados nas anotações. Em nosso exemplo, este comando especifica o gerador de código para o ent
.
Certifique-se de que a inicialização do esquema e as adições de campo necessárias tenham sido concluídas antes da execução. Somente então o código gerado incluirá todas as partes necessárias.
3.2 Compreensão dos Ativos de Código Gerado
Os ativos de código gerado contêm vários componentes, cada um com funções diferentes:
-
Objetos Client e Tx: Usados para interagir com o gráfico de dados. O Cliente fornece métodos para criar transações (Tx) ou executar operações diretamente no banco de dados.
-
Builders CRUD: Para cada tipo de esquema, gera builders para criar, ler, atualizar e excluir, simplificando a lógica de operação da entidade correspondente.
-
Objeto de entidade (struct em Go): Gera as structs em Go correspondentes para cada tipo no esquema, mapeando essas structs para as tabelas no banco de dados.
-
Pacote de constantes e predicados: Contém constantes e predicados para interagir com os builders.
-
Pacote de migração: Um pacote para migração de banco de dados, adequado para dialetos SQL.
-
Pacote de hook: Fornece a funcionalidade para adicionar middleware de alteração, permitindo que lógica personalizada seja executada antes ou depois de criar, atualizar ou excluir entidades.
Ao examinar o código gerado, você pode obter uma compreensão mais profunda de como o framework ent
automatiza o código de acesso aos dados para seus esquemas.
4. Opções de Geração de Código
O comando ent generate
suporta várias opções para personalizar o processo de geração de código. Você pode consultar todas as opções de geração suportadas através do seguinte comando:
ent generate -h
Aqui estão algumas opções de linha de comando comumente utilizadas:
-
--feature strings
: Estende a geração de código, adicionando funcionalidades extras. -
--header string
: Substitui o arquivo de cabeçalho de geração de código. -
--storage string
: Especifica o driver de armazenamento suportado na geração de código, com padrão para "sql". -
--target string
: Especifica o diretório de destino para a geração de código. -
--template strings
: Executa modelos adicionais em Go. Suporta caminho de arquivo, diretório e curinga, por exemplo:--template file="caminho/para/arquivo.tmpl"
.
Essas opções permitem que os desenvolvedores personalizem seu processo de geração de código de acordo com diferentes necessidades e preferências.
5. Configuração da Opção de Armazenamento
O ent
suporta a geração de ativos de código tanto para dialetos SQL quanto para Gremlin, sendo o padrão o dialeto SQL. Se o projeto precisar se conectar a um banco de dados Gremlin, o dialeto correspondente precisa ser configurado. O seguinte demonstra como especificar as opções de armazenamento:
ent generate --storage gremlin ./ent/schema
O comando acima instrui o ent
a usar o dialeto Gremlin ao gerar o código. Isso garante que os ativos gerados sejam adaptados aos requisitos do banco de dados Gremlin, garantindo compatibilidade com um banco de dados de grafo específico.
6. Uso Avançado: Pacote entc
6.1 Uso do entc
como Pacote no Projeto
O entc
é o pacote principal usado para a geração de código no framework ent
. Além da ferramenta de linha de comando, o entc
também pode ser integrado ao projeto como um pacote, permitindo que os desenvolvedores controlem e personalizem o processo de geração de código dentro do próprio código.
Para usar o entc
como um pacote no projeto, você precisa criar um arquivo chamado entc.go
e adicionar o seguinte conteúdo ao arquivo:
// +build ignore
package main
import (
"log"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
)
func main() {
if err := entc.Generate("./schema", &gen.Config{}); err != nil {
log.Fatal("executando a geração de código do ent:", err)
}
}
Ao utilizar essa abordagem, você pode modificar a estrutura gen.Config
dentro da função main
para aplicar diferentes opções de configuração. Chamando a função entc.Generate
conforme necessário, você pode controlar de forma flexível o processo de geração de código.
6.2 Configuração Detalhada do entc
entc
fornece extensas opções de configuração, permitindo que os desenvolvedores personalizem o código gerado. Por exemplo, ganchos personalizados podem ser configurados para inspecionar ou modificar o código gerado, e dependências externas podem ser injetadas usando injeção de dependência.
O exemplo a seguir demonstra como fornecer ganchos personalizados para a função entc.Generate
:
func main() {
err := entc.Generate("./esquema", &gen.Config{
Hooks: []gen.Hook{
HookFunction,
},
})
if err != nil {
log.Fatalf("executando a geração de código ent: %v", err)
}
}
// HookFunction é uma função de gancho personalizada
func HookFunction(next gen.Generator) gen.Generator {
return gen.GenerateFunc(func(g *gen.Graph) error {
// Pode manipular o modo de gráfico representado por g aqui
// Por exemplo, validar a existência de campos ou modificar a estrutura
return next.Generate(g)
})
}
Além disso, as dependências externas podem ser adicionadas usando entc.Dependency
:
func main() {
opts := []entc.Option{
entc.Dependency(
entc.DependencyType(&http.Client{}),
),
entc.Dependency(
entc.DependencyName("Writer"),
entc.DependencyTypeInfo(&field.TypeInfo{
Ident: "io.Writer",
PkgPath: "io",
}),
),
}
if err := entc.Generate("./esquema", &gen.Config{}, opts...); err != nil {
log.Fatalf("executando a geração de código ent: %v", err)
}
}
Neste exemplo, injectamos http.Client
e io.Writer
como dependências nos objetos de cliente gerados.
7. Saída da Descrição do Esquema
No framework ent
, o comando ent describe
pode ser usado para gerar a descrição do esquema em um formato gráfico. Isso pode ajudar os desenvolvedores a entender rapidamente as entidades e relacionamentos existentes.
Execute o seguinte comando para obter a descrição do esquema:
go run -mod=mod entgo.io/ent/cmd/ent describe ./ent/esquema
O comando acima irá gerar uma tabela semelhante à seguinte, exibindo informações como campos, tipos, relacionamentos, etc. para cada entidade:
Usuário:
+-------+---------+--------+-----------+ ...
| Campo | Tipo | Único | Opcional | ...
+-------+---------+--------+-----------+ ...
| id | int | false | false | ...
| nome | string | true | false | ...
+-------+---------+--------+-----------+ ...
+-------+--------+---------+-----------+ ...
| Aresta| Tipo | Inverso | Relação | ...
+-------+--------+---------+-----------+ ...
| pets | Pet | false | O2M | ...
+-------+--------+---------+-----------+ ...
8. Ganchos de Geração de Código
8.1 Conceito de Ganchos
Ganchos são funções middleware que podem ser inseridas no processo de geração de código ent
, permitindo a inserção de lógica personalizada antes e depois da geração de código. Os ganchos podem ser usados para manipular a árvore de sintaxe abstrata (AST) do código gerado, realizar validação ou adicionar trechos de código adicionais.
8.2 Exemplo de Uso de Ganchos
Aqui está um exemplo de uso de um gancho para garantir que todos os campos contenham uma determinada tag de estrutura (por exemplo, json
):
func main() {
err := entc.Generate("./esquema", &gen.Config{
Hooks: []gen.Hook{
EnsureStructTag("json"),
},
})
if err != nil {
log.Fatalf("executando a geração de código ent: %v", err)
}
}
// EnsureStructTag garante que todos os campos no gráfico contenham uma tag de estrutura específica
func EnsureStructTag(name string) gen.Hook {
return func(next gen.Generator) gen.Generator {
return gen.GenerateFunc(func(g *gen.Graph) error {
for _, node := range g.Nodes {
for _, field := range node.Fields {
tag := reflect.StructTag(field.StructTag)
if _, ok := tag.Lookup(name); !ok {
return fmt.Errorf("a tag de estrutura %q está ausente para o campo %s.%s", name, node.Name, field.Name)
}
}
}
return next.Generate(g)
})
}
}
Neste exemplo, antes de gerar o código, a função EnsureStructTag
verifica cada campo para a tag json
. Se um campo estiver faltando essa tag, a geração de código será interrompida e retornará um erro. Esta é uma maneira eficaz de manter a limpeza e consistência do código.