Skip to content

Commit

Permalink
SearchKit - Allow creation of new records via in-place edit
Browse files Browse the repository at this point in the history
Allows e.g. an email record to be created if one does not already exist.
Fixes dev/core#2853
  • Loading branch information
colemanw committed Mar 22, 2022
1 parent f8cc892 commit 35ceb7e
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 16 deletions.
3 changes: 2 additions & 1 deletion Civi/Api4/Query/Api4SelectQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -751,14 +751,15 @@ private function addExplicitJoins() {
'alias' => $alias,
'table' => $tableName,
'bridge' => NULL,
'on' => array_filter(array_filter($join, 'is_array')),
];
// If the first condition is a string, it's the name of a bridge entity
if (!empty($join[0]) && is_string($join[0]) && \CRM_Utils_Rule::alphanumeric($join[0])) {
$this->addBridgeJoin($join, $entity, $alias, $side);
}
else {
$conditions = $this->getJoinConditions($join, $entity, $alias, $joinEntityFields);
foreach (array_filter($join) as $clause) {
foreach ($join['on'] as $clause) {
$conditions[] = $this->treeWalkClauses($clause, 'ON');
}
$this->join($side, $tableName, $alias, $conditions);
Expand Down
61 changes: 47 additions & 14 deletions ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -488,35 +488,67 @@ private function getUrl(string $path, $query = NULL) {
/**
* @param $column
* @param $data
* @return array{entity: string, input_type: string, data_type: string, options: bool, serialize: bool, fk_entity: string, value_key: string, record: array, value: mixed}|null
* @return array{entity: string, action: string, input_type: string, data_type: string, options: bool, serialize: bool, nullable: bool, fk_entity: string, value_key: string, record: array, value: mixed}|null
*/
private function formatEditableColumn($column, $data) {
$editable = $this->getEditableInfo($column['key']);
$editable['record'] = [];
// Generate params to edit existing record
if (!empty($data[$editable['id_path']])) {
$editable['action'] = 'update';
$editable['record'][$editable['id_key']] = $data[$editable['id_path']];
$editable['value'] = $data[$editable['value_path']];
}
// Generate params to create new record, if applicable
elseif ($editable['explicit_join']) {
$editable['action'] = 'create';
$editable['value'] = NULL;
$editable['nullable'] = FALSE;
// Get values for creation from the join clause
$join = $this->getQuery()->getExplicitJoin($editable['explicit_join']);
foreach ($join['on'] ?? [] as $clause) {
if (is_array($clause) && count($clause) === 3 && $clause[1] === '=') {
// Because clauses are reversible, check both directions to see which side has a fieldName belonging to this join
foreach ([0 => 2, 2 => 0] as $field => $value) {
if (strpos($clause[$field], $editable['explicit_join'] . '.') === 0) {
$fieldName = substr($clause[$field], strlen($editable['explicit_join']) + 1);
// If the value is a field, get it from the data
if (isset($data[$clause[$value]])) {
$editable['record'][$fieldName] = $data[$clause[$value]];
}
// If it's a literal bool or number
elseif (is_bool($clause[$value]) || is_numeric($clause[$value])) {
$editable['record'][$fieldName] = $clause[$value];
}
// If it's a literal string it will be quoted
elseif (is_string($clause[$value]) && in_array($clause[$value][0], ['"', "'"], TRUE) && substr($clause[$value], -1) === $clause[$value][0]) {
$editable['record'][$fieldName] = substr($clause[$value], 1, -1);
}
}
}
}
}
}
// Ensure current user has access
if ($editable['record']) {
$access = civicrm_api4($editable['entity'], 'checkAccess', [
'action' => 'update',
'values' => [
$editable['id_key'] => $data[$editable['id_path']],
],
'action' => $editable['action'],
'values' => $editable['record'],
], 0)['access'];
if (!$access) {
return NULL;
if ($access) {
\CRM_Utils_Array::remove($editable, 'id_key', 'id_path', 'value_path', 'explicit_join');
return $editable;
}
$editable['record'] = [
$editable['id_key'] => $data[$editable['id_path']],
];
$editable['value'] = $data[$editable['value_path']];
\CRM_Utils_Array::remove($editable, 'id_key', 'id_path', 'value_path');
return $editable;
}
return NULL;
}

/**
* @param $key
* @return array{entity: string, input_type: string, data_type: string, options: bool, serialize: bool, nullable: bool, fk_entity: string, value_key: string, value_path: string, id_key: string, id_path: string}|null
* @return array{entity: string, input_type: string, data_type: string, options: bool, serialize: bool, nullable: bool, fk_entity: string, value_key: string, value_path: string, id_key: string, id_path: string, explicit_join: string}|null
*/
private function getEditableInfo($key) {
// Strip pseudoconstant suffix
[$key] = explode(':', $key);
$field = $this->getField($key);
// If field is an implicit join to another entity (not a custom group), use the original fk field
Expand All @@ -543,6 +575,7 @@ private function getEditableInfo($key) {
'value_path' => $key,
'id_key' => $idKey,
'id_path' => $idPath,
'explicit_join' => $field['explicit_join'],
];
}
return NULL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
var record = _.cloneDeep(col.edit.record);
record[col.edit.value_key] = ctrl.value;
$('input', $element).attr('disabled', true);
ctrl.doSave({apiCall: [col.edit.entity, 'update', {values: record}]});
ctrl.doSave({apiCall: [col.edit.entity, col.edit.action, {values: record}]});
};

function loadOptions() {
Expand Down

0 comments on commit 35ceb7e

Please sign in to comment.