callback.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package gorm
  2. import "fmt"
  3. // DefaultCallback default callbacks defined by gorm
  4. var DefaultCallback = &Callback{}
  5. // Callback is a struct that contains all CRUD callbacks
  6. // Field `creates` contains callbacks will be call when creating object
  7. // Field `updates` contains callbacks will be call when updating object
  8. // Field `deletes` contains callbacks will be call when deleting object
  9. // Field `queries` contains callbacks will be call when querying object with query methods like Find, First, Related, Association...
  10. // Field `rowQueries` contains callbacks will be call when querying object with Row, Rows...
  11. // Field `processors` contains all callback processors, will be used to generate above callbacks in order
  12. type Callback struct {
  13. logger logger
  14. creates []*func(scope *Scope)
  15. updates []*func(scope *Scope)
  16. deletes []*func(scope *Scope)
  17. queries []*func(scope *Scope)
  18. rowQueries []*func(scope *Scope)
  19. processors []*CallbackProcessor
  20. }
  21. // CallbackProcessor contains callback informations
  22. type CallbackProcessor struct {
  23. logger logger
  24. name string // current callback's name
  25. before string // register current callback before a callback
  26. after string // register current callback after a callback
  27. replace bool // replace callbacks with same name
  28. remove bool // delete callbacks with same name
  29. kind string // callback type: create, update, delete, query, row_query
  30. processor *func(scope *Scope) // callback handler
  31. parent *Callback
  32. }
  33. func (c *Callback) clone(logger logger) *Callback {
  34. return &Callback{
  35. logger: logger,
  36. creates: c.creates,
  37. updates: c.updates,
  38. deletes: c.deletes,
  39. queries: c.queries,
  40. rowQueries: c.rowQueries,
  41. processors: c.processors,
  42. }
  43. }
  44. // Create could be used to register callbacks for creating object
  45. // db.Callback().Create().After("gorm:create").Register("plugin:run_after_create", func(*Scope) {
  46. // // business logic
  47. // ...
  48. //
  49. // // set error if some thing wrong happened, will rollback the creating
  50. // scope.Err(errors.New("error"))
  51. // })
  52. func (c *Callback) Create() *CallbackProcessor {
  53. return &CallbackProcessor{logger: c.logger, kind: "create", parent: c}
  54. }
  55. // Update could be used to register callbacks for updating object, refer `Create` for usage
  56. func (c *Callback) Update() *CallbackProcessor {
  57. return &CallbackProcessor{logger: c.logger, kind: "update", parent: c}
  58. }
  59. // Delete could be used to register callbacks for deleting object, refer `Create` for usage
  60. func (c *Callback) Delete() *CallbackProcessor {
  61. return &CallbackProcessor{logger: c.logger, kind: "delete", parent: c}
  62. }
  63. // Query could be used to register callbacks for querying objects with query methods like `Find`, `First`, `Related`, `Association`...
  64. // Refer `Create` for usage
  65. func (c *Callback) Query() *CallbackProcessor {
  66. return &CallbackProcessor{logger: c.logger, kind: "query", parent: c}
  67. }
  68. // RowQuery could be used to register callbacks for querying objects with `Row`, `Rows`, refer `Create` for usage
  69. func (c *Callback) RowQuery() *CallbackProcessor {
  70. return &CallbackProcessor{logger: c.logger, kind: "row_query", parent: c}
  71. }
  72. // After insert a new callback after callback `callbackName`, refer `Callbacks.Create`
  73. func (cp *CallbackProcessor) After(callbackName string) *CallbackProcessor {
  74. cp.after = callbackName
  75. return cp
  76. }
  77. // Before insert a new callback before callback `callbackName`, refer `Callbacks.Create`
  78. func (cp *CallbackProcessor) Before(callbackName string) *CallbackProcessor {
  79. cp.before = callbackName
  80. return cp
  81. }
  82. // Register a new callback, refer `Callbacks.Create`
  83. func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *Scope)) {
  84. if cp.kind == "row_query" {
  85. if cp.before == "" && cp.after == "" && callbackName != "gorm:row_query" {
  86. cp.logger.Print(fmt.Sprintf("Registering RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility...\n", callbackName))
  87. cp.before = "gorm:row_query"
  88. }
  89. }
  90. cp.name = callbackName
  91. cp.processor = &callback
  92. cp.parent.processors = append(cp.parent.processors, cp)
  93. cp.parent.reorder()
  94. }
  95. // Remove a registered callback
  96. // db.Callback().Create().Remove("gorm:update_time_stamp_when_create")
  97. func (cp *CallbackProcessor) Remove(callbackName string) {
  98. cp.logger.Print(fmt.Sprintf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum()))
  99. cp.name = callbackName
  100. cp.remove = true
  101. cp.parent.processors = append(cp.parent.processors, cp)
  102. cp.parent.reorder()
  103. }
  104. // Replace a registered callback with new callback
  105. // db.Callback().Create().Replace("gorm:update_time_stamp_when_create", func(*Scope) {
  106. // scope.SetColumn("Created", now)
  107. // scope.SetColumn("Updated", now)
  108. // })
  109. func (cp *CallbackProcessor) Replace(callbackName string, callback func(scope *Scope)) {
  110. cp.logger.Print(fmt.Sprintf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum()))
  111. cp.name = callbackName
  112. cp.processor = &callback
  113. cp.replace = true
  114. cp.parent.processors = append(cp.parent.processors, cp)
  115. cp.parent.reorder()
  116. }
  117. // Get registered callback
  118. // db.Callback().Create().Get("gorm:create")
  119. func (cp *CallbackProcessor) Get(callbackName string) (callback func(scope *Scope)) {
  120. for _, p := range cp.parent.processors {
  121. if p.name == callbackName && p.kind == cp.kind {
  122. if p.remove {
  123. callback = nil
  124. } else {
  125. callback = *p.processor
  126. }
  127. }
  128. }
  129. return
  130. }
  131. // getRIndex get right index from string slice
  132. func getRIndex(strs []string, str string) int {
  133. for i := len(strs) - 1; i >= 0; i-- {
  134. if strs[i] == str {
  135. return i
  136. }
  137. }
  138. return -1
  139. }
  140. // sortProcessors sort callback processors based on its before, after, remove, replace
  141. func sortProcessors(cps []*CallbackProcessor) []*func(scope *Scope) {
  142. var (
  143. allNames, sortedNames []string
  144. sortCallbackProcessor func(c *CallbackProcessor)
  145. )
  146. for _, cp := range cps {
  147. // show warning message the callback name already exists
  148. if index := getRIndex(allNames, cp.name); index > -1 && !cp.replace && !cp.remove {
  149. cp.logger.Print(fmt.Sprintf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum()))
  150. }
  151. allNames = append(allNames, cp.name)
  152. }
  153. sortCallbackProcessor = func(c *CallbackProcessor) {
  154. if getRIndex(sortedNames, c.name) == -1 { // if not sorted
  155. if c.before != "" { // if defined before callback
  156. if index := getRIndex(sortedNames, c.before); index != -1 {
  157. // if before callback already sorted, append current callback just after it
  158. sortedNames = append(sortedNames[:index], append([]string{c.name}, sortedNames[index:]...)...)
  159. } else if index := getRIndex(allNames, c.before); index != -1 {
  160. // if before callback exists but haven't sorted, append current callback to last
  161. sortedNames = append(sortedNames, c.name)
  162. sortCallbackProcessor(cps[index])
  163. }
  164. }
  165. if c.after != "" { // if defined after callback
  166. if index := getRIndex(sortedNames, c.after); index != -1 {
  167. // if after callback already sorted, append current callback just before it
  168. sortedNames = append(sortedNames[:index+1], append([]string{c.name}, sortedNames[index+1:]...)...)
  169. } else if index := getRIndex(allNames, c.after); index != -1 {
  170. // if after callback exists but haven't sorted
  171. cp := cps[index]
  172. // set after callback's before callback to current callback
  173. if cp.before == "" {
  174. cp.before = c.name
  175. }
  176. sortCallbackProcessor(cp)
  177. }
  178. }
  179. // if current callback haven't been sorted, append it to last
  180. if getRIndex(sortedNames, c.name) == -1 {
  181. sortedNames = append(sortedNames, c.name)
  182. }
  183. }
  184. }
  185. for _, cp := range cps {
  186. sortCallbackProcessor(cp)
  187. }
  188. var sortedFuncs []*func(scope *Scope)
  189. for _, name := range sortedNames {
  190. if index := getRIndex(allNames, name); !cps[index].remove {
  191. sortedFuncs = append(sortedFuncs, cps[index].processor)
  192. }
  193. }
  194. return sortedFuncs
  195. }
  196. // reorder all registered processors, and reset CRUD callbacks
  197. func (c *Callback) reorder() {
  198. var creates, updates, deletes, queries, rowQueries []*CallbackProcessor
  199. for _, processor := range c.processors {
  200. if processor.name != "" {
  201. switch processor.kind {
  202. case "create":
  203. creates = append(creates, processor)
  204. case "update":
  205. updates = append(updates, processor)
  206. case "delete":
  207. deletes = append(deletes, processor)
  208. case "query":
  209. queries = append(queries, processor)
  210. case "row_query":
  211. rowQueries = append(rowQueries, processor)
  212. }
  213. }
  214. }
  215. c.creates = sortProcessors(creates)
  216. c.updates = sortProcessors(updates)
  217. c.deletes = sortProcessors(deletes)
  218. c.queries = sortProcessors(queries)
  219. c.rowQueries = sortProcessors(rowQueries)
  220. }