Here's how I've opted to do it. Please do submit alternative answers if there's a better way.
Use the civicrm_post hook like this:
Note that you need to replace N
with your custom field's ID number
function myext_civicrm_post($op, $objectName, $objectId, &$objectRef) {
if ($objectId && $op == 'create' && $objectName == 'Contribution') {
// See if we have a field that belongs to the fieldset.
$result = civicrm_api3('CustomValue', 'get', [
'sequential' => 1,
'entity_id' => $objectId,
'entity_type' => "Contribution",
'return.custom_N' => 1,
]);
if ($result['count'] == 0) {
// Create the default entry.
$result = civicrm_api3('CustomValue', 'create', [
'sequential' => 1,
'entity_id' => $contribution_id,
'entity_type' => "Contribution",
'custom_N' => 'unknown',
// custom_M ...
]);
}
}
}
Then when you create a Contribution, the defaults are properly set.
Interesting observations
My interest was in creating the default record when the contribution was created by the API. But I thought I'd look into how this works with using the UI too.
If you use the web UI to create a contribution then the code above gets in before the main process creates/sets the custom values. i.e. This is the order of things:
- User creates a contribution record.
- Contribution record created.
- Our hook runs, no custom value exists so it creates the default record.
- The user-entered values (inc. the defaults that were set up in the form) overwrite the default created by our hook.
The same process happens when you use the API to create a contribution but include values for the custom data also.