Save life cycle
As editors create and modify items, Brightspot makes corresponding changes in the database. You can override callbacks to provide additional functionality during the save life cycle. For example, when Brightspot successfully saves an item, you can display a notification in the content edit form.
Understanding the save life cycle
The save life cycle starts with either of the following methods:
Record#save
—Calling this method performs a standard save.Record#saveImmediately
—Calling this method saves the object outside the context of a transaction.
The following diagram shows the various callbacks you can use during this life cycle.
Referring to the previous diagram—
The available callbacks beforeSave, onValidate, beforeCommit, onDuplicate, and afterSave have empty implementations in
Record
, so you can override them in your own classes.save
andsaveImmediately
are not available callbacks, and cannot be overridden.
Note
The save life cycle incurs overhead for callbacks and validation. If you want to save data without validation, such as during a large ingestion, save objects with the Record#saveUnsafely
method.
Save life cycle callbacks
This section describes the various callbacks during the extended save life cycle. These callbacks correspond to the diagram Save life cycle.
Note
If you need to save or delete an object in the following callbacks, always use saveImmediately
or deleteImmediately
. If you need to save or delete multiple objects, consider using a database transaction (see Transactions) with Database#beginIsolatedWrites.
beforeSave
Use beforeSave
for simple state changes and low-overhead database or API calls. In particular, you can use this method to modify data before the validation step in the save life cycle. One common use for beforeSave
is to populate hidden fields. Because editors cannot directly populated these fields in the UI, you must populate them in code.
import com.psddev.cms.db.Content; import com.psddev.cms.db.ToolUi; public class Article extends Content { private String name; private String code; /* Declare a hidden field. */ @ToolUi.Hidden private String internalName; public String getName() { return name; } public String getCode() { return code; } /* Populate a hidden field. */ @Override public void beforeSave() { this.internalName = getName() + "-" + getCode(); } }
onValidate
Use onValidate
for custom validation on an object beyond what the database or annotations supply, such as sending customized messages to the UI.
In the following snippet, the name
field is annotated with @Recordable.Required. If an editor saves an item with a blank name
, Brightspot displays a standard error message. The snippet overrides the onValidate
callback to display a custom error message.
import com.psddev.cms.db.Content; public class Article extends Content { @Required private String name; /* Display custom error message. */ protected void onValidate() { if (name == null) { getState().addError(getState().getField("name"), "Enter a name."); } } }
![]() Figure 110. Standard error message | ![]() Figure 111. Custom error message |
beforeCommit
Use beforeCommit
to make additional changes to the object that do not require validation. Examples include ensuring certain fields are null, prepending or appending strings, or populating fields with valid non-null values.
In the following example, the beforeSave
method sets the lastUpdated
field to the current date and time.
import com.psddev.cms.db.Content; import com.psddev.cms.db.ToolUi; public class Article extends Content { @ToolUi.Hidden private Date lastUpdated; protected void beforeCommit() { lastUpdated = new Date(); } }
onDuplicate
Use onDuplicate
or provide custom logic when the database save operation fails because a uniqueness violation is detected on an indexed field or method. The most common uses of this method are the following:
Resolve the violation by setting a unique value and return
true
. In this case the save life cycle returns to the onValidate method.Display a custom message in the content edit form and return
false
. In this case the save life cycle returns to the beforeSave method.
In the following example, the headline
field is annotated with @Indexed(unique = true)
. If an editor saves an item with an existing headline
, Brightspot displays a standard error message. The following snippet overrides the onDuplicate
callback to display a custom error message.
import com.psddev.cms.db.Content; import com.psddev.dari.db.ObjectIndex; public class Article extends Content { @Indexed(unique = true) private String headline; /* Display custom error message. */ protected boolean onDuplicate(ObjectIndex index) { getState().addError(getState().getField("headline"), "Another article has this headline. Use a different headline."); return false; } }
![]() Figure 112. Standard error message | ![]() Figure 113. Custom error message |
afterSave
Use afterSave
to provide processing after the database save, typically for sending messages to the UI or to log files. The following snippet overrides the afterSave
callback to write a message to stdout.
import com.psddev.cms.db.Content; public class Article extends Content { private String headline; public String getHeadline() { return headline; } /* Write message to stdout. */ protected void afterSave() { System.out.println("Saved an article with the following headline: " + getHeadline()); } }
27-Feb-2020 14:32:06 INFO Saved an article with the following headline: After 300 years, Astronaut Higgins escapes from black hole. Doesn't look a day older.