Files
ent/doc/md/generating-ent-schemas.md
2021-05-05 14:01:39 +03:00

226 lines
4.7 KiB
Markdown

---
id: generating-ent-schemas
title: Generating Schemas
---
## Introduction
To facilitate the creation of tooling that generates `ent.Schema`s programmatically, `ent` supports the manipulation of
the `schema/` directory using the `entgo.io/contrib/schemast` package.
## API
### Loading
In order to manipulate an existing schema directory we must first load it into a `schemast.Context` object:
```go
package main
import (
"fmt"
"log"
"entgo.io/contrib/schemast"
)
func main() {
ctx, err := schemast.Load("./ent/schema")
if err != nil {
log.Fatalf("failed: %v", err)
}
if ctx.HasType("user") {
fmt.Println("schema directory contains a schema named User!")
}
}
```
### Printing
To print back out our context to a target directory, use `schemast.Print`:
```go
package main
import (
"log"
"entgo.io/contrib/schemast"
)
func main() {
ctx, err := schemast.Load("./ent/schema")
if err != nil {
log.Fatalf("failed: %v", err)
}
// A no-op since we did not manipulate the Context at all.
if err := schemast.Print("./ent/schema"); err != nil {
log.Fatalf("failed: %v", err)
}
}
```
### Mutators
To mutate the `ent/schema` directory, we can use `schemast.Mutate`, which takes a list of
`schemast.Mutator`s to apply to the context:
```go
package schemast
// Mutator changes a Context.
type Mutator interface {
Mutate(ctx *Context) error
}
```
Currently, only a single type of `schemast.Mutator` is implemented, `UpsertSchema`:
```go
package schemast
// UpsertSchema implements Mutator. UpsertSchema will add to the Context the type named
// Name if not present and rewrite the type's Fields, Edges, Indexes and Annotations methods.
type UpsertSchema struct {
Name string
Fields []ent.Field
Edges []ent.Edge
Indexes []ent.Index
Annotations []schema.Annotation
}
```
To use it:
```go
package main
import (
"log"
"entgo.io/contrib/schemast"
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
func main() {
ctx, err := schemast.Load("./ent/schema")
if err != nil {
log.Fatalf("failed: %v", err)
}
mutations := []schemast.Mutator{
&schemast.UpsertSchema{
Name: "User",
Fields: []ent.Field{
field.String("name"),
},
},
&schemast.UpsertSchema{
Name: "Team",
Fields: []ent.Field{
field.String("name"),
},
},
}
err = schemast.Mutate(ctx, mutations...)
if err := ctx.Print("./ent/schema"); err != nil {
log.Fatalf("failed: %v", err)
}
}
```
After running this program, observe two new files exist in the schema directory: `user.go` and `team.go`:
```go
// user.go
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/field"
)
type User struct {
ent.Schema
}
func (User) Fields() []ent.Field {
return []ent.Field{field.String("name")}
}
func (User) Edges() []ent.Edge {
return nil
}
func (User) Annotations() []schema.Annotation {
return nil
}
```
```go
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/field"
)
type Team struct {
ent.Schema
}
func (Team) Fields() []ent.Field {
return []ent.Field{field.String("name")}
}
func (Team) Edges() []ent.Edge {
return nil
}
func (Team) Annotations() []schema.Annotation {
return nil
}
```
### Working with Edges
Edges are defined in `ent` this way:
```go
edge.To("edge_name", OtherSchema.Type)
```
This syntax relies on the fact that the `OtherSchema` struct already exists when we define the edge so we can refer to
its `Type` method. When we are generating schemas programmatically, obviously we need somehow to describe the edge to
the code-generator before the type definitions exist. To do this you can do something like:
```go
type placeholder struct {
ent.Schema
}
func withType(e ent.Edge, typeName string) ent.Edge {
e.Descriptor().Type = typeName
return e
}
func newEdgeTo(edgeName, otherType string) ent.Edge {
// we pass a placeholder type to the edge constructor:
e := edge.To(edgeName, placeholder.Type)
// then we override the other type's name directly on the edge descriptor:
return withType(e, otherType)
}
```
## Examples
The `protoc-gen-ent` ([doc](https://github.com/ent/contrib/tree/master/entproto/cmd/protoc-gen-ent)) is a protoc plugin
that programmatically generates `ent.Schema`s from .proto files, it uses the `schemast` to manipulate the
target `schema` directory. To see
how, [read the source code](https://github.com/ent/contrib/blob/master/entproto/cmd/protoc-gen-ent/main.go#L34).
## Caveats
`schemast` is still experimental, APIs are subject to change in the future. In addition, a small portion of
the `ent.Field` definition API is unsupported at this point in time, to see a full list of unsupported features see
the [source code](https://github.com/ent/contrib/blob/aed7a43a3e54550c1dd9a1a066ce1236b4bae56c/schemast/field.go#L158).