Callbacks

GORM leverages Callbacks to power its core functionalities. These callbacks provide hooks for various database operations like Create, Query, Update, Delete, Row, and Raw, allowing for extensive customization of GORM’s behavior.

Callbacks are registered at the global *gorm.DB level, not on a session basis. This means if you need different callback behaviors, you should initialize a separate *gorm.DB instance.

Registering a Callback

You can register a callback for specific operations. For example, to add a custom image cropping functionality:

  1. func cropImage(db *gorm.DB) {
  2. if db.Statement.Schema != nil {
  3. // crop image fields and upload them to CDN, dummy code
  4. for _, field := range db.Statement.Schema.Fields {
  5. switch db.Statement.ReflectValue.Kind() {
  6. case reflect.Slice, reflect.Array:
  7. for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
  8. // Get value from field
  9. if fieldValue, isZero := field.ValueOf(db.Statement.Context, db.Statement.ReflectValue.Index(i)); !isZero {
  10. if crop, ok := fieldValue.(CropInterface); ok {
  11. crop.Crop()
  12. }
  13. }
  14. }
  15. case reflect.Struct:
  16. // Get value from field
  17. if fieldValue, isZero := field.ValueOf(db.Statement.Context, db.Statement.ReflectValue); !isZero {
  18. if crop, ok := fieldValue.(CropInterface); ok {
  19. crop.Crop()
  20. }
  21. }
  22. // Set value to field
  23. err := field.Set(db.Statement.Context, db.Statement.ReflectValue, "newValue")
  24. }
  25. }
  26. // All fields for current model
  27. db.Statement.Schema.Fields
  28. // All primary key fields for current model
  29. db.Statement.Schema.PrimaryFields
  30. // Prioritized primary key field: field with DB name `id` or the first defined primary key
  31. db.Statement.Schema.PrioritizedPrimaryField
  32. // All relationships for current model
  33. db.Statement.Schema.Relationships
  34. // Find field with field name or db name
  35. field := db.Statement.Schema.LookUpField("Name")
  36. // processing
  37. }
  38. }
  39. // Register the callback for the Create operation
  40. db.Callback().Create().Register("crop_image", cropImage)

Deleting a Callback

If a callback is no longer needed, it can be removed:

  1. // Remove the 'gorm:create' callback from Create operations
  2. db.Callback().Create().Remove("gorm:create")

Replacing a Callback

Callbacks with the same name can be replaced with a new function:

  1. // Replace the 'gorm:create' callback with a new function
  2. db.Callback().Create().Replace("gorm:create", newCreateFunction)

Ordering Callbacks

Callbacks can be registered with specific orders to ensure they execute at the right time in the operation lifecycle.

  1. // Register to execute before the 'gorm:create' callback
  2. db.Callback().Create().Before("gorm:create").Register("update_created_at", updateCreated)
  3. // Register to execute after the 'gorm:create' callback
  4. db.Callback().Create().After("gorm:create").Register("update_created_at", updateCreated)
  5. // Register to execute after the 'gorm:query' callback
  6. db.Callback().Query().After("gorm:query").Register("my_plugin:after_query", afterQuery)
  7. // Register to execute after the 'gorm:delete' callback
  8. db.Callback().Delete().After("gorm:delete").Register("my_plugin:after_delete", afterDelete)
  9. // Register to execute before the 'gorm:update' callback
  10. db.Callback().Update().Before("gorm:update").Register("my_plugin:before_update", beforeUpdate)
  11. // Register to execute before 'gorm:create' and after 'gorm:before_create'
  12. db.Callback().Create().Before("gorm:create").After("gorm:before_create").Register("my_plugin:before_create", beforeCreate)
  13. // Register to execute before any other callbacks
  14. db.Callback().Create().Before("*").Register("update_created_at", updateCreated)
  15. // Register to execute after any other callbacks
  16. db.Callback().Create().After("*").Register("update_created_at", updateCreated)

Predefined Callbacks

GORM comes with a set of predefined callbacks that drive its standard features. It’s recommended to review these defined callbacks before creating custom plugins or additional callback functions.

Plugins

GORM’s plugin system allows for easy extensibility and customization of its core functionalities, enhancing your application’s capabilities while maintaining a modular architecture.

The Plugin Interface

To create a plugin for GORM, you need to define a struct that implements the Plugin interface:

  1. type Plugin interface {
  2. Name() string
  3. Initialize(*gorm.DB) error
  4. }
  • Name Method: Returns a unique string identifier for the plugin.
  • Initialize Method: Contains the logic to set up the plugin. This method is called when the plugin is registered with GORM for the first time.

Registering a Plugin

Once your plugin conforms to the Plugin interface, you can register it with a GORM instance:

  1. // Example of registering a plugin
  2. db.Use(MyCustomPlugin{})

Accessing Registered Plugins

After a plugin is registered, it is stored in GORM’s configuration. You can access registered plugins via the Plugins map:

  1. // Access a registered plugin by its name
  2. plugin := db.Config.Plugins[pluginName]

Practical Example

An example of a GORM plugin is the Prometheus plugin, which integrates Prometheus monitoring with GORM:

  1. // Registering the Prometheus plugin
  2. db.Use(prometheus.New(prometheus.Config{
  3. // Configuration options here
  4. }))

Prometheus plugin documentation provides detailed information on its implementation and usage.