Hook特性允许我们对特性的Model绑定CURD钩子处理。

相关定义

相关Hook函数:

type (
	HookFuncSelect func(ctx context.Context, in *HookSelectInput) (result Result, err error)
	HookFuncInsert func(ctx context.Context, in *HookInsertInput) (result sql.Result, err error)
	HookFuncUpdate func(ctx context.Context, in *HookUpdateInput) (result sql.Result, err error)
	HookFuncDelete func(ctx context.Context, in *HookDeleteInput) (result sql.Result, err error)
)

// HookHandler manages all supported hook functions for Model.
type HookHandler struct {
	Select HookFuncSelect
	Insert HookFuncInsert
	Update HookFuncUpdate
	Delete HookFuncDelete
}

Hook注册方法:

// Hook sets the hook functions for current model.
func (m *Model) Hook(hook HookHandler) *Model

使用示例

查询birth_day字段时,同时计算出当前用户的年龄:

// Hook function definition.
var hook = gdb.HookHandler{
	Select: func(ctx context.Context, in *gdb.HookSelectInput) (result gdb.Result, err error) {
		result, err = in.Next(ctx)
		if err != nil {
			return
		}
		for i, record := range result {
			if !record["birth_day"].IsEmpty() {
				age := gtime.Now().Sub(record["birth_day"].GTime()).Hours() / 24 / 365
				record["age"] = gvar.New(age)
			}
			result[i] = record
		}
		return
	},
}
// It registers hook function, creates and returns a new `model`.
model := g.Model("user").Hook(hook)

// The hook function takes effect on each ORM operation when using the `model`.
all, err := model.Where("status", "online").OrderAsc(`id`).All()




Content Menu

  • No labels

17 Comments

  1. 没看明白,Hook方法怎么注册?

  2. package hook
    
    import (
       "context"
       "database/sql"
       "github.com/gogf/gf/v2/database/gdb"
    )
    
    var Curd = gdb.HookHandler{
       Select: func(ctx context.Context, in *gdb.HookSelectInput) (result gdb.Result, err error) {
          result, err = in.Next(ctx)
          return
       },
       Insert: func(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result, err error) {
          result, err = in.Next(ctx)
          return
       },
    
       Update: func(ctx context.Context, in *gdb.HookUpdateInput) (result sql.Result, err error) {
          result, err = in.Next(ctx)
          return
       },
    
       Delete: func(ctx context.Context, in *gdb.HookDeleteInput) (result sql.Result, err error) {
          result, err = in.Next(ctx)
          return
       },
    }
    
    
    
    
    dao.xxx.Ctx(ctx).Data(in).Hook(hook.Curd).InsertAndGetId()
    1. 请问只能在业务代码中这样注册吗?是否可以在不修改业务代码的情况下,在其他地方统一注册呢?

      dao.xxx.Ctx(ctx).Data(in).Hook(hook.Curd).InsertAndGetId()


      1. 重写dao的Ctx方法 或者自己实现一个xxxDao的成员方法例如CtxByHook

  3. hook切换分表操作的应用实现建议.updateHookHandler,insertHookHandler,deleteHookHandler三者直接对in *gdb.HookXXXInput中的in.Table进行覆写即可完成分表切换(理论).但selectHookHandler中替换in.Table是无效的,在h.Model.db.DoSelect(ctx, h.link, h.Sql, h.Args...)引用的是in.Sql的内容,因此需要对in.Sql中的table信息进行替换,例如执行in.Sql = gstr.Replace(in.Sql, in.Table, gstr.Replace(in.Table, "\"", "")+"_hash1")完成切换分表操作(已验证)

    1. 兄弟能不能贴一下关键代码参考一下

      1. master已经修复了selectHookHandler替换无效的问题 直接替换in.Table就可以了.

        1. 是的,从v2.5.0版本开始的HOOK中可以修改TableSchema

    2. zxl

      大佬,如何把自定义的表名传入到selectHookHandler中呢?新手小白,还请大佬告知

      1. 通过ctx注入的方式去实现替换.例如在业务中计算得到了子表名, 即context.WithValue(ctx, subTableKey, tableName)设置到上下文中,然后在handler中使用ctx.Value(subTableKey)取回即可.注意未取到情况为nil的情况的防护判断.

        PS:不建议直接注入表名,而是将分表的计算条件注入,在handler中完成取模操作.例如SaaS场景下,在前置middleware中对ctx注入商户唯一值即可在后续上下文中使用.

  4. 我能全局定义 hook 吗? 比如 user 表中的用户名 `first_name` 与 `last_name` 组合出一个 `full_name` ,按文档的意思是每次查询都要自己注册一次吗?是否可以支持全局注册?? 郭强 

  5. 经过我的测试,

    type HookHandler struct {
    	Select HookFuncSelect
    	Insert HookFuncInsert
    	Update HookFuncUpdate
    	Delete HookFuncDelete
    }

    分别关联的是SQL层面的SELECT、INSERT、UPDATE、DELETE等SQL语句,而不是关联的orm层面model.All()  model.Insert()  model.Update()  model.Delete() 等方法。作者在此处的设计上,是前者的意义更大吗?
    我如果想实现业务逻辑中调用model.Delete()时,底层不是DELETE语句操作,而是其他的如UPDATE和INSERT语句逻辑,那用hook方法实现是不是就不合适了?

    1. 那为什么不在业务层调用对等的方法呢.除了hook还有更底层的driver,也可以重写driver的Exec.

      1. 启用了软删除功能后,调用model.Delete(),底层并没有执行DELETE语句,而是update语句,这样hook里调用的就是HookFuncUpdate

        1. 软删除的本质就是对删除标记字段写入值.

  6. 在钩子里面如何清空查询条件

  7. 能否支持多个hook, 比如 dao.xxx.Ctx(ctx).Hook(hook.Do1).Hook(hook.Do1) 或者 dao.xxx.Ctx(ctx).Hook(hook.Do1,hook.Do2)