diff --git a/CRM/Contact/DAO/RelationshipCache.php b/CRM/Contact/DAO/RelationshipCache.php index 33fdc4ebec94..c1ddd69b816a 100644 --- a/CRM/Contact/DAO/RelationshipCache.php +++ b/CRM/Contact/DAO/RelationshipCache.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/RelationshipCache.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:79cb98a7730bede29ea2cf1aeb5a780e) + * (GenCodeChecksum:3f113a0869fa2a649d0e6044a8e59572) */ /** @@ -305,6 +305,7 @@ public static function &fields() { 'bao' => 'CRM_Contact_BAO_RelationshipCache', 'localizable' => 0, 'html' => [ + 'type' => 'Select', 'label' => ts("Relationship to contact"), ], 'pseudoconstant' => [ @@ -345,6 +346,7 @@ public static function &fields() { 'bao' => 'CRM_Contact_BAO_RelationshipCache', 'localizable' => 0, 'html' => [ + 'type' => 'Select', 'label' => ts("Relationship from contact"), ], 'pseudoconstant' => [ @@ -419,6 +421,7 @@ public static function &fields() { 'FKClassName' => 'CRM_Case_DAO_Case', 'component' => 'CiviCase', 'html' => [ + 'type' => 'EntityRef', 'label' => ts("Case"), ], 'readonly' => TRUE, diff --git a/ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php b/ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php index 1bcba8155227..c8255525a1da 100644 --- a/ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php +++ b/ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php @@ -195,11 +195,18 @@ public function _run(\Civi\Api4\Generic\Result $result) { $entities[] = $display['saved_search_id.api_entity']; foreach ($display['saved_search_id.api_params']['join'] ?? [] as $join) { $entities[] = explode(' AS ', $join[0])[0]; + // Add bridge entities (but only if they are tagged searchable e.g. RelationshipCache) + if (is_string($join[2] ?? NULL) && + in_array(CoreUtil::getInfoItem($join[2], 'searchable'), ['primary', 'secondary']) + ) { + $entities[] = $join[2]; + } } } if (!$newForm) { $scanBlocks($info['definition']['layout']); } + $entities = array_unique($entities); $this->loadAvailableBlocks($entities, $info, [['join_entity', 'IS NULL']]); } diff --git a/ext/afform/admin/ang/afGuiEditor/afGuiSearch.component.js b/ext/afform/admin/ang/afGuiEditor/afGuiSearch.component.js index d6f93f6c073a..04c006921a78 100644 --- a/ext/afform/admin/ang/afGuiEditor/afGuiSearch.component.js +++ b/ext/afform/admin/ang/afGuiEditor/afGuiSearch.component.js @@ -101,18 +101,32 @@ label: mainEntity.label, fields: mainEntity.fields }]; - entityCount[mainEntity.entity] = 1; + + // Increment count of entityName and return a suffix string if > 1 + function countEntity(entityName) { + entityCount[entityName] = (entityCount[entityName] || 0) + 1; + return entityCount[entityName] > 1 ? ' ' + entityCount[entityName] : ''; + } + countEntity(mainEntity.entity); _.each(ctrl.display.settings['saved_search_id.api_params'].join, function(join) { var joinInfo = join[0].split(' AS '), - entity = afGui.getEntity(joinInfo[0]); - entityCount[entity.entity] = (entityCount[entity.entity] || 0) + 1; + entity = afGui.getEntity(joinInfo[0]), + joinEntity = afGui.getEntity(join[2]); entities.push({ name: entity.entity, prefix: joinInfo[1] + '.', - label: entity.label + (entityCount[entity.entity] > 1 ? ' ' + entityCount[entity.entity] : ''), + label: entity.label + countEntity(entity.entity), fields: entity.fields, }); + if (joinEntity) { + entities.push({ + name: joinEntity.entity, + prefix: joinInfo[1] + '.', + label: joinEntity.label + countEntity(joinEntity.entity), + fields: _.omit(joinEntity.fields, _.keys(entity.fields)), + }); + } }); return entities; diff --git a/ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js b/ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js index 8a0d0a0e1139..ba9d313f9577 100644 --- a/ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js +++ b/ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js @@ -400,7 +400,7 @@ }; // Returns the entity type for fields within this conainer (join entity type if this is a join, else the primary entity type) - this.getFieldEntityType = function(fieldName) { + this.getFieldEntityType = function(fieldKey) { var entityType; // If entityName is declared for this fieldset, return entity-type or join-type if (ctrl.entityName) { @@ -409,17 +409,22 @@ } else { var searchKey = ctrl.getDataEntity(), searchDisplay = afGui.getSearchDisplay.apply(null, searchKey.split('.')), - prefix = _.includes(fieldName, '.') ? fieldName.split('.')[0] : null; + fieldName = fieldKey.substr(fieldKey.indexOf('.') + 1), + prefix = _.includes(fieldKey, '.') ? fieldKey.split('.')[0] : null; if (prefix) { _.each(searchDisplay['saved_search_id.api_params'].join, function(join) { var joinInfo = join[0].split(' AS '); if (prefix === joinInfo[1]) { entityType = joinInfo[0]; + // If entity doesn't contain field, it belongs to the bridge join + if (!(fieldName in afGui.getEntity(entityType).fields)) { + entityType = join[2]; + } return false; } }); } - if (!entityType && fieldName && afGui.getField(searchDisplay['saved_search_id.api_entity'], fieldName)) { + if (!entityType && fieldKey && afGui.getField(searchDisplay['saved_search_id.api_entity'], fieldKey)) { entityType = searchDisplay['saved_search_id.api_entity']; } } diff --git a/ext/afform/core/Civi/Afform/AfformMetadataInjector.php b/ext/afform/core/Civi/Afform/AfformMetadataInjector.php index b84bdfb324c7..c30c009c014b 100644 --- a/ext/afform/core/Civi/Afform/AfformMetadataInjector.php +++ b/ext/afform/core/Civi/Afform/AfformMetadataInjector.php @@ -77,14 +77,19 @@ public static function preprocess($e) { /** * Merge field definition metadata into an afform field's definition * - * @param string $entityName + * @param string|array $entityNames * @param string $action * @param \DOMElement $afField * @throws \API_Exception */ - private static function fillFieldMetadata($entityName, $action, \DOMElement $afField) { + private static function fillFieldMetadata($entityNames, $action, \DOMElement $afField) { $fieldName = $afField->getAttribute('name'); - $fieldInfo = self::getField($entityName, $fieldName, $action); + foreach ((array) $entityNames as $entityName) { + $fieldInfo = self::getField($entityName, $fieldName, $action); + if ($fieldInfo) { + break; + } + } // Merge field definition data with whatever's already in the markup. $deep = ['input_attrs']; if ($fieldInfo) { @@ -179,6 +184,9 @@ private static function getField(string $entityName, string $fieldName, string $ break; } } + if (!isset($field)) { + return NULL; + } // Id field for selecting existing entity if ($action === 'update' && $field['name'] === CoreUtil::getIdFieldName($entityName)) { $entityTitle = CoreUtil::getInfoItem($entityName, 'title'); @@ -200,24 +208,28 @@ private static function getField(string $entityName, string $fieldName, string $ } /** - * Determines name of the api entity based on the field name prefix + * Determines name of the api entit(ies) based on the field name prefix + * + * Note: Normally will return a single entity name, but + * Will return 2 entity names in the case of Bridge joins e.g. RelationshipCache * * @param string $fieldName * @param string[] $entityList - * @return string + * @return string|array */ private static function getFieldEntityType($fieldName, $entityList) { $prefix = strpos($fieldName, '.') ? explode('.', $fieldName)[0] : NULL; + $joinEntities = []; $baseEntity = array_shift($entityList); if ($prefix) { foreach ($entityList as $entityAndAlias) { [$entity, $alias] = explode(' AS ', $entityAndAlias); if ($alias === $prefix) { - return $entityAndAlias; + $joinEntities[] = $entityAndAlias; } } } - return $baseEntity; + return $joinEntities ?: $baseEntity; } private static function getFormEntities(\phpQueryObject $doc) { diff --git a/ext/search_kit/Civi/Search/AfformSearchMetadataInjector.php b/ext/search_kit/Civi/Search/AfformSearchMetadataInjector.php index 98244fe9d74e..478c491d2e67 100644 --- a/ext/search_kit/Civi/Search/AfformSearchMetadataInjector.php +++ b/ext/search_kit/Civi/Search/AfformSearchMetadataInjector.php @@ -57,7 +57,13 @@ public static function preprocess($e) { // Add entity names to the fieldset so that afform can populate field metadata $fieldset = pq($component)->parents('[af-fieldset]'); if ($fieldset->length) { - $entityList = array_merge([$display['saved_search_id.api_entity']], array_column($display['saved_search_id.api_params']['join'] ?? [], 0)); + $entityList = [$display['saved_search_id.api_entity']]; + foreach ($display['saved_search_id.api_params']['join'] ?? [] as $join) { + $entityList[] = $join[0]; + if (is_string($join[2] ?? NULL)) { + $entityList[] = $join[2] . ' AS ' . (explode(' AS ', $join[0])[1]); + } + } $fieldset->attr('api-entities', htmlspecialchars(\CRM_Utils_JS::encode($entityList))); } } diff --git a/xml/schema/Contact/RelationshipCache.xml b/xml/schema/Contact/RelationshipCache.xml index 62d9ea1d2fbb..7421611dbcff 100644 --- a/xml/schema/Contact/RelationshipCache.xml +++ b/xml/schema/Contact/RelationshipCache.xml @@ -115,6 +115,7 @@ 5.29 + Select CRM_Core_PseudoConstant::relationshipTypeOptions @@ -152,6 +153,7 @@ 5.29 + Select CRM_Core_PseudoConstant::relationshipTypeOptions @@ -237,6 +239,7 @@ FK to civicrm_case + EntityRef 5.44 true