跳到主要内容

生成模式

概述

为便于创建以编程方式生成 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)
}
// 由于未对 Context 进行任何操作,此处为无操作
if err := schemast.Print("./ent/schema"); err != nil {
log.Fatalf("failed: %v", err)
}
}

变更器

要变更 ent/schema 目录,我们可以使用 schemast.Mutate,它接受一组要应用于上下文的 schemast.Mutator

package schemast

// Mutator 用于变更 Context。
type Mutator interface {
Mutate(ctx *Context) error
}

目前仅实现了一种 schemast.Mutator 类型,即 UpsertSchema

package schemast

// UpsertSchema 实现了 Mutator 接口。如果命名类型不存在,UpsertSchema 会将其添加到 Context 中,
// 并重写该类型的 Fields、Edges、Indexes 和 Annotations 方法。
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 {
// 向边构造器传递占位符类型:
e := edge.To(edgeName, placeholder.Type)
// 然后直接在边描述符中覆盖其他类型的名称:
return withType(e, otherType)
}

示例

protoc-gen-ent (文档) 是一个 protoc 插件,它能从 .proto 文件以编程方式生成 ent.Schema,它使用 schemast 来操作目标 schema 目录。具体实现请参阅源代码

注意事项

schemast 仍处于实验阶段,API 未来可能发生变化。此外,目前暂不支持一小部分 ent.Field 定义 API,完整的不支持功能列表请参阅源代码