跳到主要内容

生成模式

介绍

为便于创建能够程序化生成 ent.Schema 的工具,ent 支持通过 entgo.io/contrib/schemast 包操作 schema/ 目录。

API

加载

我们首先需要加载已有的模式目录到 schemast.Context 对象中以便操作它:

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!")
}
}

打印

使用 schemast.Print 将我们的上下文打印到目标目录中:

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

突变器

使用 schemast.Mutate 修改 ent/schema 目录,它可接收应用于上下文的一系列 schemast.Mutator

package schemast

// Mutator changes a Context.
type Mutator interface {
Mutate(ctx *Context) error
}

目前只有一个类型的 schemast.Mutator 被实现了,即 UpsertSchema

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
}

让我们使用它:

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

运行此程序后,可以看到两个新的文件在模式目录中生成了:user.goteam.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
}
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
}

与边协作

ent 中边这样定义:

edge.To("edge_name", OtherSchema.Type)

当我们定义边时实际上语法基于已经存在的 OtherSchema 结构体,所以我们可以引用它的 Type 方法。 当我们程序化生成模式时,明显我们需要在类型定义存在之前向代码生成器描述边。 可以像如下方式做到这一点:

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

示例

protoc-gen-ent (文档) 是程序化从 .proto 文件生成 ent.Schema 的 protoc 插件, 它使用 schemast 操作目标 schema 目录。 参见 阅读源代码 了解如何实现。

注意事项

schemast 仍处于实验阶段,其 API 在未来可能会有变化。此外,使用 ent.Field 定义 API 目前还有一小部分功能不支持, 参见 源代码 了解不支持的功能列表。