ent 软删除

前言

由于 golang 逆天的 import 机制(虽然设计理念很好,但是实际使用下来太烦人),导致使用框架的时候,非常容易造成循环引用。

因此在使用 ent 软删除的时候,还要折腾很多步骤

软删除

  1. 在 ent/scheme/schemetype 目录下新建 TimeMixin Mixin,用于统一管理 scheme 的创建、更新、删除时间。
1
2
3
4
5
6
7
8
9
10
11
type TimeMixin struct {  
mixin.Schema
}

func (TimeMixin) Fields() []ent.Field {
return []ent.Field{
field.Time("created_at").Immutable().Default(time.Now),
field.Time("updated_at").Default(time.Now).UpdateDefault(time.Now),
field.Time("deleted_at").Optional(),
}
}
  1. 运行 go generate ./ent 命令,生成代码

  2. 更改 ent/generate.go文件,在 generate 命令后面添加 --feature intercept,schema/snapshot 字段

1
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature intercept,schema/snapshot ./schema
  1. 在main.go 的 import 语句添加依赖 _ "<Project>/ent/runtime"

  2. 运行 go generate ./ent 生成 intercept代码

  3. 添加 interceptor 和 hooks 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
type TimeMixin struct {  
mixin.Schema
}

func (TimeMixin) Fields() []ent.Field {
return []ent.Field{
field.Time("created_at").Immutable().Default(time.Now),
field.Time("updated_at").Default(time.Now).UpdateDefault(time.Now),
field.Time("deleted_at").Optional(),
}}

type softDeleteKey struct{}

func SkipSoftDelete(parent context.Context) context.Context {
return context.WithValue(parent, softDeleteKey{}, true)
}

func (t TimeMixin) Interceptors() []ent.Interceptor {
return []ent.Interceptor{
intercept.TraverseFunc(func(ctx context.Context, q intercept.Query) error {
if skip, _ := ctx.Value(softDeleteKey{}).(bool); skip {
return nil
}
t.P(q)
return nil
}),
}
}

func (t TimeMixin) Hooks() []ent.Hook {
return []ent.Hook{
hook.On(
func(next ent.Mutator) ent.Mutator {
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
// Skip soft-delete, means delete the entity permanently.
if skip, _ := ctx.Value(softDeleteKey{}).(bool); skip {
return next.Mutate(ctx, m)
}
mx, ok := m.(interface {
SetOp(ent.Op)
Client() *gen.Client
SetDeletedAt(time.Time)
WhereP(...func(*sql.Selector))
})
if !ok {
return nil, fmt.Errorf("unexpected mutation types %T", m)
}
t.P(mx

mx.SetOp(ent.OpUpdate)
mx.SetDeletedAt(time.Now())
return mx.Client().Mutate(ctx, m)
})
}, ent.OpDeleteOne|ent.OpDelete,
)}
}

func (t TimeMixin) P(w interface{ WhereP(...func(*sql.Selector)) }) {
w.WhereP(
sql.FieldIsNull(t.Fields()[2].Descriptor().Name),
)
}

在 import 语句中添加如下内容

1
gen "<yourProject>/ent"
  1. 在 scheme中,添加 Mixin
1
2
3
4
5
6
7
8
9
type User struct {  
ent.Schema
}

func (User) Mixin() []ent.Mixin {
return []ent.Mixin{
schematype.TimeMixin{},
}
}