Skip to content

Commit

Permalink
dev/core#2823 Restructure determination of required actions.
Browse files Browse the repository at this point in the history
The expected actions are now calculated in the constructor
and retrievable by function. The code that calls the action-actions
is still pretty cludgey - but the intent is that
we would stop passing the dao object into those
action-action functions in the near future.

This PR punts any decisions about how outputs might look
(eg. making the array retrievable from outside of the function.

Note that there is pretty comprehensive
test cover in CRM_Core_ManagedEntitiesTest
  • Loading branch information
eileenmcnaughton committed Sep 8, 2021
1 parent b314ea2 commit abb4304
Showing 1 changed file with 112 additions and 26 deletions.
138 changes: 112 additions & 26 deletions CRM/Core/ManagedEntities.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use Civi\Api4\Managed;

/**
* The ManagedEntities system allows modules to add records to the database
* declaratively. Those records will be automatically inserted, updated,
Expand All @@ -26,6 +28,13 @@ public static function getCleanupOptions() {
*/
protected $moduleIndex;

/**
* Actions arising from the managed entities.
*
* @var array
*/
protected $managedActions = [];

/**
* @var array
* List of all entity declarations.
Expand Down Expand Up @@ -76,6 +85,29 @@ public function __construct($modules, $declarations) {
else {
$this->loadDeclarations();
}
$managedEntities = Managed::get(FALSE)->addSelect('*')->execute();
foreach ($managedEntities as $managedEntity) {
$key = "{$managedEntity['module']}_{$managedEntity['name']}_{$managedEntity['entity_type']}";
// Set to 'delete' - it will be overwritten below if it is to be updated.
$action = 'delete';
$this->managedActions[$key] = array_merge($managedEntity, ['managed_action' => $action]);
}
foreach ($this->declarations as $declaration) {
$key = "{$declaration['module']}_{$declaration['name']}_{$declaration['entity']}";
if (isset($this->managedActions[$key])) {
$this->managedActions[$key]['params'] = $declaration['params'];
$this->managedActions[$key]['managed_action'] = 'update';
}
else {
$this->managedActions[$key] = [
'module' => $declaration['module'],
'name' => $declaration['name'],
'entity_type' => $declaration['entity'],
'managed_action' => 'create',
'params' => $declaration['params'],
];
}
}
}

/**
Expand Down Expand Up @@ -144,7 +176,7 @@ public function reconcileEnabledModules() {
$decls = $this->createDeclarationIndex($this->moduleIndex, $this->getDeclarations());
foreach ($decls as $moduleName => $todos) {
if ($this->isModuleEnabled($moduleName)) {
$this->reconcileEnabledModule($this->moduleIndex[TRUE][$moduleName], $todos);
$this->reconcileEnabledModule($moduleName);
}
}
}
Expand All @@ -153,33 +185,87 @@ public function reconcileEnabledModules() {
* For one enabled module, add new entities, update existing entities,
* and remove orphaned (stale) entities.
*
* @param \CRM_Core_Module $module
* @param array $todos
* List of entities currently declared by this module.
* array(string $name => array $entityDef).
* @param string $module
*/
public function reconcileEnabledModule(CRM_Core_Module $module, $todos) {
$dao = new CRM_Core_DAO_Managed();
$dao->module = $module->name;
$dao->find();
while ($dao->fetch()) {
if (isset($todos[$dao->name]) && $todos[$dao->name]) {
// update existing entity; remove from $todos
$this->updateExistingEntity($dao, $todos[$dao->name]);
unset($todos[$dao->name]);
}
else {
// remove stale entity; not in $todos
$this->removeStaleEntity($dao);
}
public function reconcileEnabledModule(string $module): void {
foreach ($this->getManagedEntitiesToUpdate(['module' => $module]) as $todo) {
$dao = new CRM_Core_DAO_Managed();
$dao->module = $todo['module'];
$dao->name = $todo['name'];
$dao->entity_type = $todo['entity_type'];
$dao->entity_id = $todo['entity_id'];
$dao->id = $todo['id'];
$this->updateExistingEntity($dao, $todo);
}

// create new entities from leftover $todos
foreach ($todos as $name => $todo) {
foreach ($this->getManagedEntitiesToDelete(['module' => $module]) as $todo) {
$dao = new CRM_Core_DAO_Managed();
$dao->module = $todo['module'];
$dao->name = $todo['name'];
$dao->entity_type = $todo['entity_type'];
$dao->id = $todo['id'];
$dao->cleanup = $todo['cleanup'];
$dao->entity_id = $todo['entity_id'];
$this->removeStaleEntity($dao);
}
foreach ($this->getManagedEntitiesToCreate(['module' => $module]) as $todo) {
$this->insertNewEntity($todo);
}
}

/**
* Get the managed entities to be created.
*
* @param array $filters
*
* @return array
*/
protected function getManagedEntitiesToCreate(array $filters = []): array {
return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'create']));
}

/**
* Get the managed entities to be created.
*
* @param array $filters
*
* @return array
*/
protected function getManagedEntitiesToUpdate(array $filters = []): array {
return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'update']));
}

/**
* Get the managed entities to be deleted.
*
* @param array $filters
*
* @return array
*/
protected function getManagedEntitiesToDelete(array $filters = []): array {
return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'delete']));
}

/**
* Get the managed entities that fit the criteria.
*
* @param array $filters
*
* @return array
*/
protected function getManagedEntities(array $filters = []): array {
$return = [];
foreach ($this->managedActions as $actionKey => $action) {
foreach ($filters as $filterKey => $filterValue) {
if ($action[$filterKey] !== $filterValue) {
continue 2;
}
}
$return[$actionKey] = $action;
}
return $return;
}

/**
* For all disabled modules, disable any managed entities.
*/
Expand Down Expand Up @@ -231,15 +317,15 @@ public function reconcileUnknownModules() {
* Entity specification (per hook_civicrm_managedEntities).
*/
public function insertNewEntity($todo) {
$result = civicrm_api($todo['entity'], 'create', $todo['params']);
$result = civicrm_api($todo['entity_type'], 'create', $todo['params']);
if (!empty($result['is_error'])) {
$this->onApiError($todo['entity'], 'create', $todo['params'], $result);
$this->onApiError($todo['entity_type'], 'create', $todo['params'], $result);
}

$dao = new CRM_Core_DAO_Managed();
$dao->module = $todo['module'];
$dao->name = $todo['name'];
$dao->entity_type = $todo['entity'];
$dao->entity_type = $todo['entity_type'];
// A fatal error will result if there is no valid id but if
// this is v4 api we might need to access it via ->first().
$dao->entity_id = $result['id'] ?? $result->first()['id'];
Expand Down Expand Up @@ -322,7 +408,7 @@ public function disableEntity($dao): void {
* Remove a stale entity (if policy allows).
*
* @param CRM_Core_DAO_Managed $dao
* @throws Exception
* @throws CRM_Core_Exception
*/
public function removeStaleEntity($dao) {
$policy = empty($dao->cleanup) ? 'always' : $dao->cleanup;
Expand Down Expand Up @@ -350,7 +436,7 @@ public function removeStaleEntity($dao) {
break;

default:
throw new \Exception('Unrecognized cleanup policy: ' . $policy);
throw new CRM_Core_Exception('Unrecognized cleanup policy: ' . $policy);
}

if ($doDelete) {
Expand Down

0 comments on commit abb4304

Please sign in to comment.