gorm/callback.go

243 lines
8.4 KiB
Go
Raw Normal View History

2014-01-23 12:43:39 +04:00
package gorm
import "log"
2014-01-25 16:04:01 +04:00
2016-03-07 09:54:20 +03:00
// DefaultCallback default callbacks defined by gorm
var DefaultCallback = &Callback{}
2016-01-16 14:20:52 +03:00
2016-12-21 11:11:23 +03:00
// Callback is a struct that contains all CRUD callbacks
2016-03-07 16:09:05 +03:00
// Field `creates` contains callbacks will be call when creating object
// Field `updates` contains callbacks will be call when updating object
// Field `deletes` contains callbacks will be call when deleting object
// Field `queries` contains callbacks will be call when querying object with query methods like Find, First, Related, Association...
// Field `rowQueries` contains callbacks will be call when querying object with Row, Rows...
// Field `processors` contains all callback processors, will be used to generate above callbacks in order
2016-01-16 16:55:00 +03:00
type Callback struct {
2014-01-26 08:41:37 +04:00
creates []*func(scope *Scope)
updates []*func(scope *Scope)
deletes []*func(scope *Scope)
queries []*func(scope *Scope)
2015-04-17 13:27:20 +03:00
rowQueries []*func(scope *Scope)
2016-01-16 14:20:52 +03:00
processors []*CallbackProcessor
2014-01-24 17:52:26 +04:00
}
2016-03-07 16:09:05 +03:00
// CallbackProcessor contains callback informations
2016-01-16 14:20:52 +03:00
type CallbackProcessor struct {
name string // current callback's name
before string // register current callback before a callback
after string // register current callback after a callback
replace bool // replace callbacks with same name
remove bool // delete callbacks with same name
kind string // callback type: create, update, delete, query, row_query
processor *func(scope *Scope) // callback handler
2016-01-16 16:55:00 +03:00
parent *Callback
2014-01-23 12:43:39 +04:00
}
2016-01-16 16:55:00 +03:00
func (c *Callback) clone() *Callback {
return &Callback{
2015-02-26 09:47:56 +03:00
creates: c.creates,
updates: c.updates,
deletes: c.deletes,
queries: c.queries,
2016-03-09 15:44:24 +03:00
rowQueries: c.rowQueries,
2015-02-26 09:47:56 +03:00
processors: c.processors,
}
2014-01-29 06:25:58 +04:00
}
2016-01-16 14:20:52 +03:00
// Create could be used to register callbacks for creating object
// db.Callback().Create().After("gorm:create").Register("plugin:run_after_create", func(*Scope) {
// // business logic
// ...
//
// // set error if some thing wrong happened, will rollback the creating
// scope.Err(errors.New("error"))
// })
2016-01-16 16:55:00 +03:00
func (c *Callback) Create() *CallbackProcessor {
2016-02-15 11:55:47 +03:00
return &CallbackProcessor{kind: "create", parent: c}
2014-01-23 12:43:39 +04:00
}
2016-01-16 14:20:52 +03:00
// Update could be used to register callbacks for updating object, refer `Create` for usage
2016-01-16 16:55:00 +03:00
func (c *Callback) Update() *CallbackProcessor {
2016-02-15 11:55:47 +03:00
return &CallbackProcessor{kind: "update", parent: c}
2014-01-23 12:43:39 +04:00
}
2016-01-16 14:20:52 +03:00
// Delete could be used to register callbacks for deleting object, refer `Create` for usage
2016-01-16 16:55:00 +03:00
func (c *Callback) Delete() *CallbackProcessor {
2016-02-15 11:55:47 +03:00
return &CallbackProcessor{kind: "delete", parent: c}
2014-01-23 12:43:39 +04:00
}
2016-01-16 14:20:52 +03:00
// Query could be used to register callbacks for querying objects with query methods like `Find`, `First`, `Related`, `Association`...
2016-03-07 16:09:05 +03:00
// Refer `Create` for usage
2016-01-16 16:55:00 +03:00
func (c *Callback) Query() *CallbackProcessor {
2016-02-15 11:55:47 +03:00
return &CallbackProcessor{kind: "query", parent: c}
2014-01-23 12:43:39 +04:00
}
2016-01-17 10:30:42 +03:00
// RowQuery could be used to register callbacks for querying objects with `Row`, `Rows`, refer `Create` for usage
2016-01-16 16:55:00 +03:00
func (c *Callback) RowQuery() *CallbackProcessor {
2016-02-15 11:55:47 +03:00
return &CallbackProcessor{kind: "row_query", parent: c}
2015-04-17 13:27:20 +03:00
}
2016-01-16 14:20:52 +03:00
// After insert a new callback after callback `callbackName`, refer `Callbacks.Create`
func (cp *CallbackProcessor) After(callbackName string) *CallbackProcessor {
cp.after = callbackName
2014-01-24 17:52:26 +04:00
return cp
}
2016-01-16 14:20:52 +03:00
// Before insert a new callback before callback `callbackName`, refer `Callbacks.Create`
func (cp *CallbackProcessor) Before(callbackName string) *CallbackProcessor {
cp.before = callbackName
2014-01-24 17:52:26 +04:00
return cp
}
2016-01-16 14:20:52 +03:00
// Register a new callback, refer `Callbacks.Create`
func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *Scope)) {
if cp.kind == "row_query" {
if cp.before == "" && cp.after == "" && callbackName != "gorm:row_query" {
log.Printf("Registing RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility...\n", callbackName)
cp.before = "gorm:row_query"
}
}
2016-01-16 14:20:52 +03:00
cp.name = callbackName
cp.processor = &callback
2016-02-15 11:55:47 +03:00
cp.parent.processors = append(cp.parent.processors, cp)
2016-02-15 12:22:29 +03:00
cp.parent.reorder()
2014-01-24 17:52:26 +04:00
}
2016-01-16 14:20:52 +03:00
// Remove a registered callback
// db.Callback().Create().Remove("gorm:update_time_stamp_when_create")
func (cp *CallbackProcessor) Remove(callbackName string) {
log.Printf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum())
2016-01-16 14:20:52 +03:00
cp.name = callbackName
2014-01-25 16:04:01 +04:00
cp.remove = true
2016-02-15 11:55:47 +03:00
cp.parent.processors = append(cp.parent.processors, cp)
2016-02-15 12:22:29 +03:00
cp.parent.reorder()
2016-01-16 14:20:52 +03:00
}
// Replace a registered callback with new callback
// db.Callback().Create().Replace("gorm:update_time_stamp_when_create", func(*Scope) {
// scope.SetColumn("Created", now)
// scope.SetColumn("Updated", now)
// })
func (cp *CallbackProcessor) Replace(callbackName string, callback func(scope *Scope)) {
log.Printf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum())
2016-01-16 14:20:52 +03:00
cp.name = callbackName
cp.processor = &callback
2014-01-24 17:52:26 +04:00
cp.replace = true
2016-02-15 11:55:47 +03:00
cp.parent.processors = append(cp.parent.processors, cp)
2016-02-15 12:22:29 +03:00
cp.parent.reorder()
2014-01-25 15:35:21 +04:00
}
2016-01-17 10:30:42 +03:00
// Get registered callback
// db.Callback().Create().Get("gorm:create")
func (cp *CallbackProcessor) Get(callbackName string) (callback func(scope *Scope)) {
for _, p := range cp.parent.processors {
if p.name == callbackName && p.kind == cp.kind && !cp.remove {
return *p.processor
2016-01-17 10:30:42 +03:00
}
}
return nil
}
2016-01-16 14:20:52 +03:00
// getRIndex get right index from string slice
2014-01-25 16:04:01 +04:00
func getRIndex(strs []string, str string) int {
for i := len(strs) - 1; i >= 0; i-- {
if strs[i] == str {
return i
2014-01-25 15:35:21 +04:00
}
}
return -1
}
2016-01-16 14:20:52 +03:00
// sortProcessors sort callback processors based on its before, after, remove, replace
func sortProcessors(cps []*CallbackProcessor) []*func(scope *Scope) {
2016-01-17 15:51:11 +03:00
var (
allNames, sortedNames []string
sortCallbackProcessor func(c *CallbackProcessor)
)
2014-01-25 15:35:21 +04:00
for _, cp := range cps {
2016-01-17 15:51:11 +03:00
// show warning message the callback name already exists
if index := getRIndex(allNames, cp.name); index > -1 && !cp.replace && !cp.remove {
log.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
2014-01-25 16:04:01 +04:00
}
2016-01-17 15:51:11 +03:00
allNames = append(allNames, cp.name)
2014-01-25 15:35:21 +04:00
}
2016-01-16 14:20:52 +03:00
sortCallbackProcessor = func(c *CallbackProcessor) {
2016-01-17 15:51:11 +03:00
if getRIndex(sortedNames, c.name) == -1 { // if not sorted
if c.before != "" { // if defined before callback
if index := getRIndex(sortedNames, c.before); index != -1 {
// if before callback already sorted, append current callback just after it
sortedNames = append(sortedNames[:index], append([]string{c.name}, sortedNames[index:]...)...)
} else if index := getRIndex(allNames, c.before); index != -1 {
// if before callback exists but haven't sorted, append current callback to last
sortedNames = append(sortedNames, c.name)
sortCallbackProcessor(cps[index])
}
2014-01-25 15:35:21 +04:00
}
2016-01-17 15:51:11 +03:00
if c.after != "" { // if defined after callback
if index := getRIndex(sortedNames, c.after); index != -1 {
// if after callback already sorted, append current callback just before it
sortedNames = append(sortedNames[:index+1], append([]string{c.name}, sortedNames[index+1:]...)...)
} else if index := getRIndex(allNames, c.after); index != -1 {
// if after callback exists but haven't sorted
cp := cps[index]
// set after callback's before callback to current callback
if cp.before == "" {
cp.before = c.name
}
sortCallbackProcessor(cp)
2014-01-25 15:35:21 +04:00
}
}
2016-01-17 15:51:11 +03:00
// if current callback haven't been sorted, append it to last
if getRIndex(sortedNames, c.name) == -1 {
sortedNames = append(sortedNames, c.name)
}
2014-01-25 15:35:21 +04:00
}
}
for _, cp := range cps {
2014-08-27 12:10:33 +04:00
sortCallbackProcessor(cp)
2014-01-25 15:35:21 +04:00
}
2016-01-17 15:51:11 +03:00
var sortedFuncs []*func(scope *Scope)
2014-01-25 15:35:21 +04:00
for _, name := range sortedNames {
2016-01-17 15:51:11 +03:00
if index := getRIndex(allNames, name); !cps[index].remove {
2014-01-25 16:04:01 +04:00
sortedFuncs = append(sortedFuncs, cps[index].processor)
}
2014-01-25 15:35:21 +04:00
}
2016-01-17 15:51:11 +03:00
return sortedFuncs
2014-01-25 15:35:21 +04:00
}
2017-01-04 10:53:49 +03:00
// reorder all registered processors, and reset CRUD callbacks
2016-01-16 16:55:00 +03:00
func (c *Callback) reorder() {
2016-01-16 14:20:52 +03:00
var creates, updates, deletes, queries, rowQueries []*CallbackProcessor
2014-01-25 15:35:21 +04:00
for _, processor := range c.processors {
if processor.name != "" {
switch processor.kind {
case "create":
creates = append(creates, processor)
case "update":
updates = append(updates, processor)
case "delete":
deletes = append(deletes, processor)
case "query":
queries = append(queries, processor)
case "row_query":
rowQueries = append(rowQueries, processor)
}
2014-01-25 15:35:21 +04:00
}
}
c.creates = sortProcessors(creates)
c.updates = sortProcessors(updates)
c.deletes = sortProcessors(deletes)
c.queries = sortProcessors(queries)
2015-04-17 13:27:20 +03:00
c.rowQueries = sortProcessors(rowQueries)
2014-01-23 12:43:39 +04:00
}