hook_civicrm_pre¶
Summary¶
This hook is called before a db write on some core objects.
Notes¶
This hook does not allow the abort of the operation, use a form hook instead.
We suspect the pre hook will be useful for developers building more complex applications and need to perform operations before CiviCRM takes op. This is very applicable when you need to maintain foreign key constraints etc (when deleting an object, the child objects have to be deleted first). Another good use for the pre hook is to see what is changing between the old and new data.
Tip
Some of the more esoteric entities may not fire this hook when they're saved. If you happen to find such an objectName, please make a PR to core which adds this hook. As an example, you can refer to CRM_Core_BAO_Dashboard::create() to find succinct syntax that appropriately calls both CRM_Utils_Hook::pre() and CRM_Utils_Hook::post().
When this event is subscribed to using Symfony it passes in a Civi\Core\Event\PreEvent Object.
Since 6.15, if you use the Symfony listener approach, PreEvent gives you a consistent way to read and write both core fields and custom fields, regardless of the raw format the caller happened to submit them in (e.g. custom_123, custom_123_-1, or the custom array). Retrieve a single value with getValue(string $name), or all values at once with getValues(). Set a value with setValue(string $name, mixed $value), or several at once with mergeValues(array $values). Core fields are named as-is (e.g. contact_id); custom fields use the APIv4 long-name format, e.g. CustomGroupName.CustomFieldName.
Definition¶
hook_civicrm_pre($op, $objectName, $id, &$params = [])
Parameters¶
-
string
$op(previously$op) - operation being performed with CiviCRM object. Can have the following values:- 'view' : The CiviCRM object is going to be displayed
- 'create' : The CiviCRM object is created (or contacts are being added to a group)
- 'edit' : The CiviCRM object is edited
- 'delete' : The CiviCRM object is being deleted (or contacts are being removed from a group)
- 'update': The contact is being moved to trash or restored(Contact objects only)
-
string
$objectName(previously$objectName) - can have the following values:- 'Activity'
- 'Address'
- 'Batch'
- 'Campaign' (from 4.6)
- 'Case'
- 'CaseContact' (from 5.14.0)
- 'CaseType'
- 'Contribution'
- 'ContributionPage'
- 'ContributionRecur'
- 'ContributionSoft' (from 5.23.0)
- 'CustomGroup'
- 'CustomField'
- 'Dashboard'
- 'Domain' (from 5.18.0)
- 'Email'
- 'EntityBatch'
- 'EntityTag' (from 4.7.16)
- 'Event'
- 'FinancialAccount'
- 'FinancialItem'
- 'Event'
- 'Individual'
- 'Household'
- 'Grant'
- 'Group'
- 'GroupContact'
- 'LineItem'
- 'Mailing'
- 'MailingAB'
- 'MailingEventBounce'
- 'Membership'
- 'MembershipBlock'
- 'MembershipPayment'
- 'MessageTemplate'
- 'Organization'
- 'OpenID'
- 'Participant'
- 'ParticipantPayment'
- 'Pledge'
- 'PledgePayment'
- 'Profile' (while this is not really an object, people have expressed an interest to perform an op when a profile is created/edited)
- 'RecurringEntity'
- 'ReportInstance'
- 'Relationship'
- 'SmsProvider'
- 'StatusPreference'
- 'Survey' (from 5.1.x)
- 'UFGroup' (from 5.34)
- 'UFMatch' (when an object is linked to a CMS user record, at the request of GordonH. A UFMatch object is passed for both the pre and post hooks)
- 'Website'
- 'PriceField'
- 'PriceFieldValue'
- 'PriceSet'
-
?int
$id- the unique identifier for the object if availableNote
$idisNULLon create - the object doesn't have an id yet at this point in the save. It's only populated foredit/delete. If you need the id unconditionally (e.g. to look up a related record), fall back to reading it from$params/getValue()where the caller supplied it directly (such ascontact_idon a newContribution), rather than assuming$idis set. -
array
$params- the parameters passed
Returns¶
- None
Example¶
Traditional Format¶
myExtension_civicrm_pre(string $op, string $objectName, ?id $id, array $params): void {
if ($objectName === 'Contribution' && $op == 'create') {
$params['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'testing');
}
}
Symfony format¶
Civi\Core\Event\PreEvent's properties are $action (not $op) and $entity (not $objectName) - the constructor is PreEvent($action, $entity, $id, &$params), see PreEvent.php.
You can register for a specific entity, which avoids running your listener for every entity's saves. Internally, hook_civicrm_pre is also re-dispatched as hook_civicrm_pre::{$event->entity} (see Civi\Core\Container::createEventDispatcher()), so this is just as reliable as the generic form and is the pattern most core listeners use:
myextension_civicrm_config(&$config): void {
Civi::dispatcher()->addListener('hook_civicrm_pre::Contribution', 'myPreHook');
}
function myPreHook(\Civi\Core\Event\PreEvent $event): void {
if ($event->action === 'create') {
$event->params['contribution_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'testing');
$event->setValue('MyCustomGroup.MyCustomField', ['id1', 'id2']);
$event->mergeValues([
'MyCustomGroup.MyCustomField2' => 500,
'MyCustomGroup2.MyTestingCustomField' => 'Testing Value',
]);
}
}
Or register generically for every entity, and check $event->entity yourself:
myextension_civicrm_config(&$config): void {
Civi::dispatcher()->addListener('hook_civicrm_pre', 'myPreHook');
}
function myPreHook(\Civi\Core\Event\PreEvent $event): void {
if ($event->entity === 'Contribution' && $event->action === 'create') {
// ...as above
}
}