# Object Lifecycle Customization

The system supports customizing the logic at various points in an object's lifecycle through customization, including object creation, update, deletion, access, API data return, and search.

# Creation

The system supports customizing additional logic when an object is created through customization, which can be inserted at the following points:

  1. Before saving on creation: The object has been created in memory, all object properties have been constructed, but it has not yet been saved to the database.
  2. After saving on creation: The object's save method has been called, but the transaction has not yet been committed.

For the two types of customization described above, you need to create the following Dynamic Object Hook objects respectively

Object Type: Select the object type this custom logic applies to
Hook Type: Select "Before creating"
Core Logic: Custom code, see the documentation below for available injected variables and return value conventions
1
2
3
Object Type: Select the object type this custom logic applies to
Hook Type: Select "After creating"
Core Logic: Custom code, see the documentation below for available injected variables and return value conventions
1
2
3

The injected variables that can be used in this custom code are shown in the following table:

Variable Name Variable Type Description
object <? extends GormEntity> Object instance
dynamicFieldValues List<tech.muyan.dynamic.field.DynamicFieldValue> Values of all dynamic fields
userContext grails.plugin.springsecurity.userdetails.GrailsUser Current operating user information
application grails.core.GrailsApplication Current grails application context
log Closure<?> Log closure for printing execution logs
hookType tech.muyan.enums.ObjectHookType Type of the current executing object hook

For return results and exception handling, please refer to the Return Results and Exception Handling section below.

WARNING

The dynamicFieldDefinition parameter is only available in the customization point after saving on creation

# Update

The system supports customizing additional logic when an object is updated through customization, which can be inserted at the following points:

  1. Before updating: The object has been updated in memory, including all object properties including dynamic properties, but has not yet been saved to the database.
  2. After updating: The update methods for the object and all dynamic fields have been called, but the transaction has not yet been committed.

For the two types of customization described above, you need to create the following Dynamic Object Hook objects respectively

Object Type: Select the object type this custom logic applies to
Hook Type: Select "Before updating"
Core logic: Custom code definition, see the documentation below for available injected variables and return value conventions
1
2
3
Object Type: Select the object type this custom logic applies to
Hook Type: Select "After updating"
Core logic: Custom code definition, see the documentation below for available injected variables and return value conventions
1
2
3

The injected variables that can be used in this custom code are shown in the following table:

Variable Name Variable Type Description
oldObject <? extends GormEntity> Copy of the object instance before update
newObject <? extends GormEntity> Updated object instance including dynamic fields
userContext grails.plugin.springsecurity.userdetails.GrailsUser Current operating user information
application grails.core.GrailsApplication Current grails application context
log Closure<?> Log closure for printing execution logs
hookType tech.muyan.enums.ObjectHookType Type of the current executing object hook

For return results and exception handling, please refer to the Return Results and Exception Handling section below.

# Deletion

The system supports customizing additional logic when an object is deleted through customization, which can be inserted at the following points:

  1. Before deletion: The object has been queried from the database, before the object's delete method is called
  2. After deletion: After the object's delete method has been called, before the transaction is committed

For the two types of customization described above, you need to create the following Dynamic Object Hook objects respectively

Object Type: Select the object type this custom logic applies to
Hook Type: Select "Before deleting"
Core logic: Custom code definition, see the documentation below for available injected variables and return value conventions
1
2
3
Object Type: Select the object type this custom logic applies to
Hook Type: Select "After deleting"
Core logic: Custom code definition, see the documentation below for available injected variables and return value conventions
1
2
3

The injected variables that can be used in this custom code are shown in the following table:

Variable Name Variable Type Description
object <? extends GormEntity> Object to be deleted
userContext grails.plugin.springsecurity.userdetails.GrailsUser Current operating user information
application grails.core.GrailsApplication Current grails application context
log Closure<?> Log closure for printing execution logs
hookType tech.muyan.enums.ObjectHookType Type of the current executing object hook

For return results and exception handling, please refer to the Return Results and Exception Handling section below.

# Return Results and Exception Handling

In the customization logic before an operation, you can directly modify the object instance parameter to implement customization, and modifications to the object instance will be saved to the database.

During the execution of the customization code logic before and after the operation, exceptions can be thrown to interrupt the system's operation process on the object, as detailed below:

  • If an exception of type tech.muyan.exception.CustomLogicInterruptException or its subtype is caught, the object's operation process will be interrupted, the operation will not be saved to the database, and the Message of the exception will be displayed to the user as an error in the frontend.

  • If an exception of type tech.muyan.exception.CustomLogicWarningException or its subtype is caught, the object's operation process will not be interrupted, but the Message of the exception will be displayed to the user as a warning in the frontend.

TIP

At the customization point before saving on creation, the id field of the object is empty. At the customization point after saving on creation, the id field of the object has already obtained a value.

# API Return Data Customization

The system supports customizing the structure and values of object data returned by the API interface to the frontend for a certain object through customization. Create the following Dynamic Object Hook object to implement this customization.

Object Type: Select the object type this custom logic applies to
Logic Type: Select "Object render"
Core logic: Custom code, see the documentation below for available injected variables and return value conventions
1
2
3
  • Injected variables

The injected variables that can be used in this custom code are shown in the following table:

Variable Name Variable Type Description
object <? extends GormEntity> Object instance to be returned by the interface
userContext grails.plugin.springsecurity.userdetails.GrailsUser Current operating user information
application grails.core.GrailsApplication Current grails application context
page tech.muyan.enums.CustomRenderPageType Page calling this render method
log Closure<?> Log closure for printing execution logs
ownerClass java.lang.Class<? extends GormEntity> For search pages, the object owning the search field
fieldName java.lang.String For search pages, the name of the search field
fetchType tech.muyan.enums.FetchType Type of data retrieval

The fetchType parameter selects the range of data to be returned based on different business scenarios on the interface. The current optional values are explained as follows:

Value Description
ONLY_LABEL_FIELD Only retrieve the Label field and id
EXCLUDE_ARRAY_COLUMNS Return values of all fields except one-to-many and many-to-many associated objects
ALL_COLUMNS Return values of all fields

The optional values for the page parameter are as follows:

Value Description
RELATED_SEARCH Related object list page with search conditions
RELATED Related object list page
LIST_SEARCH Main list page with search conditions
LIST Main list page
FINDER Search page
FINDER_SEARCH Search page with search conditions
DETAIL Detail or edit page
SHOW_MULTIPLE Return value of Show multiple API

WARNING

In non-search result rendering scenarios, the injected ownerClass field is empty. Please pay attention to compatibility when implementing the render API.

  • Return result

The result that needs to be returned by this custom code is a Map data type or an instance of an org.grails.datastore.gorm.GormEntity object.

When the return value is of Map type, the Key of the Map is "result", and the Value of the Map is another Map. The key of this inner Map is the name of each field, and the value is the value of the field. The system will return the JSON representation of this Map data to the client calling the API.

// ่ฏฅๅฎขๅˆถๅŒ–ไปฃ็ ้œ€่ฆ่ฟ”ๅ›ž็š„็ป“ๆžœไธบไธ€ไธช Map, key ไธบ result, value ไธญไธบ [่ฟ”ๅ›žๅญ—ๆฎตๅ: ๅญ—ๆฎตๅ€ผ] ็š„ Map ็š„ๆ ผๅผ่ฟ”ๅ›ž็ป“ๆžœ
// The result returned by the custom code needs to be a Map, with a key of result and a value of [return field name: field value] in the format of a Map
[
  result: [
    columnName: columnValue,
    // ๅฏน่ฑก็ฑปๅž‹็š„ๅญๅญ—ๆฎต่ฟ”ๅ›žไธ€ไธชๅชๅŒ…ๅซ id ็š„ๅญ map
    objectField: [
      id: objectId
    ],
    // ็”จไบŽ card list view ่ฟ›่กŒ HTML ๆธฒๆŸ“็š„ๅฑžๆ€ง
    "@HTML_CONTENT@": ""
  ]
]
// ๆˆ–่€…็›ดๆŽฅไธบไธ€ไธช org.grails.datastore.gorm.GormEntity ๅฏน่ฑก็š„ๅฎžไพ‹
// Or directly return an instance of an org.grails.datastore.gorm.GormEntity object
return object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

System Limitations

  • Currently, this customization only applies to entity classes that do not include dynamic fields.
  • Card list rendering

In the table rendering interface, users can switch between table and card display modes.

Specifically, if the data returned by the render API includes a property named @HTML_CONTENT@, when rendering the card list view, the system will directly render the value of this property in the card, instead of displaying the details of the object in table form.

# Object Detail Access

The system supports calling custom logic when object details are viewed through customization. A more intuitive application scenario is an object access counter.

Currently, the calling point for this customization is: When an object is accessed through the /show/<domainId> interface

For this customization, you need to create the following Dynamic Object Hook object

Object Type: Select the object type this custom logic applies to
Hook Type: Select "Object access"
Code: Custom code, see the documentation below for available injected variables and return value conventions
1
2
3

The injected variables that can be used in this custom code are shown in the following table:

Variable Name Variable Type Description
object <? extends GormEntity> Object instance
renderedObject <? extends GormEntity> or Map<String, Object> Object instance or Map object containing object data returned by Render API
userContext grails.plugin.springsecurity.userdetails.GrailsUser Current operating user information
application grails.core.GrailsApplication Current grails application context
fetchType tech.muyan.enums.FetchType Type of data retrieval
log Closure<?> Log closure for printing execution logs
hookType tech.muyan.enums.ObjectHookType.ACCESS Type of the current executing object hook

Tip

This customization has no return value and should not modify the object to be accessed. The ACCESS customization should not modify the value of the object to be rendered, but should only implement logic such as object access counting.

Tip

The difference between this customization point and the render API is that this customization point is only called when the details of an object are requested, corresponding to the system code, when the DomainDataController.show method is called. While the render API is called in various scenarios where object data is requested, such as object lists, creating, returning data after updating objects, returning object lists after searching, etc.

# Attachment Storage and Return Customization

To support requirements such as virus scanning, encryption, and decryption, the system supports calling custom code to transform attachment content when writing and reading attachment content. This transformation is implemented through the RENDER API of the StorageFieldValue object.

When creating an attachment, in the render customization code, you can obtain the original attachment file data through the object.getData() method, and by setting the data attribute of the same object, you can inject the transformed file data into the save node and save it in the file storage engine.

When reading an attachment, in the render code, you can obtain the file data stored in the storage engine through the object.getData() method, and by setting the data attribute of the same object, you can inject the transformed file data into the return data node and return it to the caller.

For specific injected parameters of the customization API, please refer to the API Return Data Customization section.

Last Updated: 12/4/2024, 1:00:56 PM