2014-01-23 12:43:39 +04:00
package gorm
2019-05-08 09:49:33 +03:00
import "fmt"
2014-01-25 16:04:01 +04:00
2016-03-07 09:54:20 +03:00
// DefaultCallback default callbacks defined by gorm
2019-12-05 17:57:15 +03:00
var DefaultCallback = & Callback { logger : nopLogger { } }
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 {
2019-05-02 00:54:39 +03:00
logger logger
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 {
2019-05-02 00:54:39 +03:00
logger logger
2016-01-16 14:20:52 +03:00
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
}
2019-05-02 00:54:39 +03:00
func ( c * Callback ) clone ( logger logger ) * Callback {
2016-01-16 16:55:00 +03:00
return & Callback {
2019-05-02 00:54:39 +03:00
logger : logger ,
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 {
2019-05-02 00:54:39 +03:00
return & CallbackProcessor { logger : c . logger , 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 {
2019-05-02 00:54:39 +03:00
return & CallbackProcessor { logger : c . logger , 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 {
2019-05-02 00:54:39 +03:00
return & CallbackProcessor { logger : c . logger , 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 {
2019-05-02 00:54:39 +03:00
return & CallbackProcessor { logger : c . logger , 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 {
2019-05-02 00:54:39 +03:00
return & CallbackProcessor { logger : c . logger , 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 ) ) {
2017-01-16 04:48:06 +03:00
if cp . kind == "row_query" {
if cp . before == "" && cp . after == "" && callbackName != "gorm:row_query" {
2019-10-17 18:12:01 +03:00
cp . logger . Print ( "info" , fmt . Sprintf ( "Registering RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility..." , callbackName ) )
2017-01-16 04:48:06 +03:00
cp . before = "gorm:row_query"
}
}
2019-12-05 17:57:15 +03:00
cp . logger . Print ( "info" , fmt . Sprintf ( "[info] registering callback `%v` from %v" , callbackName , fileWithLineNum ( ) ) )
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 ) {
2019-10-17 18:12:01 +03:00
cp . logger . Print ( "info" , fmt . Sprintf ( "[info] removing callback `%v` from %v" , 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) {
2019-10-05 07:12:47 +03:00
// scope.SetColumn("CreatedAt", now)
// scope.SetColumn("UpdatedAt", now)
2016-01-16 14:20:52 +03:00
// })
func ( cp * CallbackProcessor ) Replace ( callbackName string , callback func ( scope * Scope ) ) {
2019-10-17 18:12:01 +03:00
cp . logger . Print ( "info" , fmt . Sprintf ( "[info] replacing callback `%v` from %v" , 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 ) ) {
2016-02-15 11:23:26 +03:00
for _ , p := range cp . parent . processors {
2019-09-12 17:16:05 +03:00
if p . name == callbackName && p . kind == cp . kind {
if p . remove {
callback = nil
} else {
callback = * p . processor
}
2016-01-17 10:30:42 +03:00
}
}
2019-09-12 17:16:05 +03:00
return
2016-01-17 10:30:42 +03:00
}
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 {
2019-10-17 18:12:01 +03:00
cp . logger . Print ( "warning" , fmt . Sprintf ( "[warning] duplicated callback `%v` from %v" , 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 {
2016-02-15 11:23:26 +03:00
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
}