From 96ff473c1e76ee4961435c799de20393c3b7dc8e Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sat, 3 Jul 2021 20:41:57 -0400 Subject: [PATCH] Afform - Support implicit joins in SearchDisplay filter fields --- .../Civi/AfformAdmin/AfformAdminMeta.php | 22 ++++++- .../Civi/Afform/AfformMetadataInjector.php | 61 +++++++++++++------ 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php b/ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php index 06619a649c00..fa8554171b91 100644 --- a/ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php +++ b/ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php @@ -2,6 +2,7 @@ namespace Civi\AfformAdmin; +use Civi\Api4\Utils\CoreUtil; use CRM_AfformAdmin_ExtensionUtil as E; class AfformAdminMeta { @@ -104,7 +105,26 @@ public static function getFields($entityName, $params = []) { } $params['values']['state_province_id'] = \Civi::settings()->get('defaultContactStateProvince'); } - return (array) civicrm_api4($entityName, 'getFields', $params, 'name'); + $fields = (array) civicrm_api4($entityName, 'getFields', $params); + + // Add implicit joins to search fields + if ($params['action'] === 'search') { + foreach (array_reverse($fields, TRUE) as $index => $field) { + if (!empty($field['fk_entity']) && !$field['options']) { + $fkLabelField = CoreUtil::getInfoItem($field['fk_entity'], 'label_field'); + if ($fkLabelField) { + // Add the label field from the other entity to this entity's list of fields + $newField = civicrm_api4($field['fk_entity'], 'getFields', [ + 'where' => [['name', '=', $fkLabelField]], + ])->first(); + $newField['name'] = $field['name'] . '.' . $newField['name']; + $newField['label'] = $field['label'] . ' ' . $newField['label']; + array_splice($fields, $index, 0, [$newField]); + } + } + } + } + return array_column($fields, NULL, 'name'); } /** diff --git a/ext/afform/core/Civi/Afform/AfformMetadataInjector.php b/ext/afform/core/Civi/Afform/AfformMetadataInjector.php index 261aafa92130..418e6cb8abbf 100644 --- a/ext/afform/core/Civi/Afform/AfformMetadataInjector.php +++ b/ext/afform/core/Civi/Afform/AfformMetadataInjector.php @@ -83,24 +83,7 @@ public static function preprocess($e) { */ private static function fillFieldMetadata($entityName, $action, \DOMElement $afField) { $fieldName = $afField->getAttribute('name'); - // For explicit joins, strip the alias off the field name - if (strpos($entityName, ' AS ')) { - [$entityName, $alias] = explode(' AS ', $entityName); - $fieldName = preg_replace('/^' . preg_quote($alias . '.', '/') . '/', '', $fieldName); - } - $params = [ - 'action' => $action, - 'where' => [['name', '=', $fieldName]], - 'select' => ['label', 'input_type', 'input_attrs', 'help_pre', 'help_post', 'options', 'fk_entity'], - 'loadOptions' => ['id', 'label'], - // If the admin included this field on the form, then it's OK to get metadata about the field regardless of user permissions. - 'checkPermissions' => FALSE, - ]; - if (in_array($entityName, \CRM_Contact_BAO_ContactType::basicTypes(TRUE))) { - $params['values'] = ['contact_type' => $entityName]; - $entityName = 'Contact'; - } - $fieldInfo = civicrm_api4($entityName, 'getFields', $params)->first(); + $fieldInfo = self::getField($entityName, $fieldName, $action); // Merge field definition data with whatever's already in the markup. $deep = ['input_attrs']; if ($fieldInfo) { @@ -160,6 +143,48 @@ private static function fillFieldMetadata($entityName, $action, \DOMElement $afF } } + /** + * @param string $entityName + * @param string $fieldName + * @param string $action + * @return array|NULL + */ + private static function getField(string $entityName, string $fieldName, string $action):? array { + // For explicit joins, strip the alias off the field name + if (strpos($entityName, ' AS ')) { + [$entityName, $alias] = explode(' AS ', $entityName); + $fieldName = preg_replace('/^' . preg_quote($alias . '.', '/') . '/', '', $fieldName); + } + $namesToMatch = [$fieldName]; + // Also match base field if this is an implicit join + if ($action === 'get' && strpos($fieldName, '.')) { + $namesToMatch[] = substr($fieldName, 0, strrpos($fieldName, '.')); + } + $params = [ + 'action' => $action, + 'where' => [['name', 'IN', $namesToMatch]], + 'select' => ['name', 'label', 'input_type', 'input_attrs', 'help_pre', 'help_post', 'options', 'fk_entity'], + 'loadOptions' => ['id', 'label'], + // If the admin included this field on the form, then it's OK to get metadata about the field regardless of user permissions. + 'checkPermissions' => FALSE, + ]; + if (in_array($entityName, \CRM_Contact_BAO_ContactType::basicTypes(TRUE))) { + $params['values'] = ['contact_type' => $entityName]; + $entityName = 'Contact'; + } + $fields = civicrm_api4($entityName, 'getFields', $params); + $field = $originalField = $fields->first(); + // If this is an implicit join, get new field from fk entity + if ($field['name'] !== $fieldName && $field['fk_entity']) { + $params['where'] = [['name', '=', substr($fieldName, 1 + strrpos($fieldName, '.'))]]; + $field = civicrm_api4($field['fk_entity'], 'getFields', $params)->first(); + if ($field) { + $field['label'] = $originalField['label'] . ' ' . $field['label']; + } + } + return $field; + } + /** * Determines name of the api entity based on the field name prefix *