Update an entity in the Upgrader class
The challenge¶
You have developed an Extension with a new entity for CiviCRM version 6+ using the .entityType.php file (check the Entities documentation on how this is done). At a later point a new field needs to be added to the extension in a new version. If the extension already exists on a CiviCRM system and is being updated to this version, then there will already be data in the entity and the extension cannot be simply uninstalled and reinstalled.
The entity that was created¶
In the extension Ehexample below we have added the entity using the civix command generate:entity RwAction to generate the boilerplate elements for the entity and then adapted the resulting RwAction.entityType.ph:
use CRM_Ehexample_ExtensionUtil as E;
return [
'name' => 'RwAction',
'table' => 'civicrm_rw_action',
'class' => 'CRM_Ehexample_DAO_RwAction',
'getInfo' => fn() => [
'title' => E::ts('Rw Action'),
'title_plural' => E::ts('RW Actions'),
'description' => E::ts('Table for RW Actions'),
'log' => TRUE,
'label_field' => 'title',
],
'getIndices' => fn() => [
'index_rw_action_id' => [
'fields' => [
'rw_action_id' => TRUE,
],
],
],
'getFields' => fn() => [
'id' => [
'title' => E::ts('ID'),
'sql_type' => 'int unsigned',
'input_type' => 'Number',
'required' => TRUE,
'description' => E::ts('Unique RwAction ID'),
'primary_key' => TRUE,
'auto_increment' => TRUE,
],
'rw_action_id' => [
'title' => E::ts('Refugee Walk Action ID'),
'sql_type' => 'int unsigned',
'input_type' => 'Number',
'description' => E::ts('Refugee Walk Action ID'),
],
'project_id' => [
'title' => E::ts('Project ID'),
'sql_type' => 'int unsigned',
'input_type' => 'Number',
'description' => E::ts('Project ID'),
'entity_reference' => [
'entity' => 'RwProject',
'key' => 'id',
'on_delete' => 'CASCADE',
],
],
'first_name' => [
'title' => E::ts('First Name'),
'sql_type' => 'varchar(128)',
'input_type' => 'Text',
'description' => E::ts('First Name'),
],
'last_name' => [
'title' => E::ts('Last Name'),
'sql_type' => 'varchar(128)',
'input_type' => 'Text',
'description' => E::ts('Last Name'),
],
'is_active' => [
'title' => E::ts('Refugee Walk Action Is Active?'),
'sql_type' => 'boolean',
'input_type' => 'Radio',
'description' => E::ts('Is this Refugee Walk Action active?'),
],
],
];
Upgrader
When you add an entity using the civix it should have automatically added an Upgrader.php file to the CRM/Ehexample folder. If this did not happen (you might use an older version of civix) you can use the civix command civix generate:upgrader. More information on the Upgrader class in the Upgrader documentation.
Update the entity in two places¶
Now we want to add the column contact_id to the entity. To accomplish that we will do 2 things:
- Update the
RwAction.entityType.phpfile to include the new contact_id column so that if someone installs the extension from scratch the column is included. - Add an upgrade_xxxx function to the
Upgrader.phpfile so that when the extension is already installed the user is prompted to execute the upgrades which will add the column to the entity.
Update of the entityType.php¶
use CRM_Ehexample_ExtensionUtil as E;
return [
'name' => 'RwAction',
'table' => 'civicrm_rw_action',
'class' => 'CRM_Ehexample_DAO_RwAction',
'getInfo' => fn() => [
'title' => E::ts('Rw Action'),
'title_plural' => E::ts('RW Actions'),
'description' => E::ts('Table for RW Actions'),
'log' => TRUE,
'label_field' => 'title',
],
'getIndices' => fn() => [
'index_rw_action_id' => [
'fields' => [
'rw_action_id' => TRUE,
],
],
],
'getFields' => fn() => [
'id' => [
'title' => E::ts('ID'),
'sql_type' => 'int unsigned',
'input_type' => 'Number',
'required' => TRUE,
'description' => E::ts('Unique RwAction ID'),
'primary_key' => TRUE,
'auto_increment' => TRUE,
],
'rw_action_id' => [
'title' => E::ts('Refugee Walk Action ID'),
'sql_type' => 'int unsigned',
'input_type' => 'Number',
'description' => E::ts('Refugee Walk Action ID'),
],
'project_id' => [
'title' => E::ts('Project ID'),
'sql_type' => 'int unsigned',
'input_type' => 'Number',
'description' => E::ts('Project ID'),
'entity_reference' => [
'entity' => 'RwProject',
'key' => 'id',
'on_delete' => 'CASCADE',
],
],
'contact_id' => [
'title' => E::ts('CiviCRM contact ID'),
'sql_type' => 'int unsigned',
'input_type' => 'Number',
'description' => E::ts('FK to Contact'),
'entity_reference' => [
'entity' => 'Contact',
'key' => 'id',
'on_delete' => 'SET NULL',
],
],
'first_name' => [
'title' => E::ts('First Name'),
'sql_type' => 'varchar(128)',
'input_type' => 'Text',
'description' => E::ts('First Name'),
],
'last_name' => [
'title' => E::ts('Last Name'),
'sql_type' => 'varchar(128)',
'input_type' => 'Text',
'description' => E::ts('Last Name'),
],
'is_active' => [
'title' => E::ts('Refugee Walk Action Is Active?'),
'sql_type' => 'boolean',
'input_type' => 'Radio',
'description' => E::ts('Is this Refugee Walk Action active?'),
],
],
];
Add the upgrade_xxxx function¶
We will add the upgrade_1001 (or whatever the sequence should be) to my Upgrader.php, and if the field does not exist yet use E::schema()->alterSchemaField method to add the field.
public function upgrade_1001(): bool {
$this->ctx->log->info('Applying update 1001 - add contact id to rwaction');
if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_rw_action', 'contact_id')) {
E::schema()->alterSchemaField('RwAction', 'contact_id', [
'title' => E::ts('Contact ID'),
'sql_type' => 'int unsigned',
'input_type' => 'Number',
'description' => E::ts('FK to Contact'),
'entity_reference' => [
'entity' => 'Contact',
'key' => 'id',
'on_delete' => 'SET NULL',
],
]);
}
return TRUE;
}