diff --git a/Civi/Api4/Service/Schema/SchemaMapBuilder.php b/Civi/Api4/Service/Schema/SchemaMapBuilder.php index bcaad8ee350c..77666cb4a78d 100644 --- a/Civi/Api4/Service/Schema/SchemaMapBuilder.php +++ b/Civi/Api4/Service/Schema/SchemaMapBuilder.php @@ -91,6 +91,7 @@ private function addJoins(Table $table, $field, array $data) { if ($fkClass) { $tableName = AllCoreTables::getTableForClass($fkClass); $fkKey = $data['FKKeyColumn'] ?? 'id'; + // Fixme: Clumsy string manipulation to transform e.g. "contact_id" to "contact" - we never should have done this $alias = str_replace('_id', '', $field); $joinable = new Joinable($tableName, $fkKey, $alias); $joinable->setJoinType($joinable::JOIN_TYPE_MANY_TO_ONE); @@ -154,7 +155,7 @@ private function addCustomFields(SchemaMap $map, Table $baseTable, string $entit } $fieldData = \CRM_Utils_SQL_Select::from('civicrm_custom_field f') ->join('custom_group', 'INNER JOIN civicrm_custom_group g ON g.id = f.custom_group_id') - ->select(['g.name as custom_group_name', 'g.table_name', 'g.is_multiple', 'f.name', 'label', 'column_name', 'option_group_id']) + ->select(['g.name as custom_group_name', 'g.table_name', 'g.is_multiple', 'f.name', 'f.data_type', 'label', 'column_name', 'option_group_id']) ->where('g.extends IN (@entity)', ['@entity' => $customInfo['extends']]) ->where('g.is_active') ->where('f.is_active') @@ -182,6 +183,11 @@ private function addCustomFields(SchemaMap $map, Table $baseTable, string $entit $joinable = new Joinable($baseTable->getName(), $customInfo['column'], AllCoreTables::convertEntityNameToLower($entityName)); $customTable->addTableLink('entity_id', $joinable); } + + if ($fieldData->data_type === 'ContactReference') { + $joinable = new Joinable('civicrm_contact', 'id', $fieldData->name); + $customTable->addTableLink($fieldData->column_name, $joinable); + } } foreach ($links as $alias => $link) { diff --git a/ext/search/Civi/Search/Admin.php b/ext/search/Civi/Search/Admin.php index 92b2f605656f..5a28f6d6f054 100644 --- a/ext/search/Civi/Search/Admin.php +++ b/ext/search/Civi/Search/Admin.php @@ -136,11 +136,20 @@ public static function getSchema() { if (in_array('DAOEntity', $entity['type'], TRUE) && !in_array('EntityBridge', $entity['type'], TRUE)) { foreach (array_reverse($entity['fields'], TRUE) as $index => $field) { if (!empty($field['fk_entity']) && !$field['options'] && !empty($schema[$field['fk_entity']]['label_field'])) { - // The original field will get title instead of label since it represents the id (title usually ends in ID but label does not) - $entity['fields'][$index]['label'] = $field['title']; + $isCustom = strpos($field['name'], '.'); + // Custom fields: append "ID" to original field label + if ($isCustom) { + $entity['fields'][$index]['label'] .= ' ' . E::ts('Contact ID'); + } + // DAO fields: use title instead of label since it represents the id (title usually ends in ID but label does not) + else { + $entity['fields'][$index]['label'] = $field['title']; + } // Add the label field from the other entity to this entity's list of fields $newField = \CRM_Utils_Array::findAll($schema[$field['fk_entity']]['fields'], ['name' => $schema[$field['fk_entity']]['label_field']])[0]; - $newField['name'] = str_replace('_id', '', $field['name']) . '.' . $schema[$field['fk_entity']]['label_field']; + // Due to string manipulation in \Civi\Api4\Service\Schema\SchemaMapBuilder::addJoins() + $alias = $isCustom ? $field['name'] : str_replace('_id', '', $field['name']); + $newField['name'] = $alias . '.' . $schema[$field['fk_entity']]['label_field']; $newField['label'] = $field['label'] . ' ' . $newField['label']; array_splice($entity['fields'], $index, 0, [$newField]); } diff --git a/ext/search/ang/crmSearchAdmin.module.js b/ext/search/ang/crmSearchAdmin.module.js index 88cdddbe6994..dfa3910d792c 100644 --- a/ext/search/ang/crmSearchAdmin.module.js +++ b/ext/search/ang/crmSearchAdmin.module.js @@ -105,7 +105,6 @@ return new RegExp('^' + join.alias + '_\\d\\d').test(path); }); if (!join) { - console.warn( 'Join ' + fullNameOrAlias + ' not found.'); return; } path = path.replace(join.alias + '_', ''); @@ -138,28 +137,20 @@ return result; } function getFieldAndJoin(fieldName, entityName) { - var dotSplit = fieldName.split('.'), - joinEntity = dotSplit.length > 1 ? dotSplit[0] : null, - name = _.last(dotSplit).split(':')[0], + var fieldPath = fieldName.split(':')[0], + dotSplit = fieldPath.split('.'), + name, join, field; - // Custom fields contain a dot in their fieldname - // If 3 segments, the first is the joinEntity and the last 2 are the custom field - if (dotSplit.length === 3) { - name = dotSplit[1] + '.' + name; - } - // If 2 segments, it's ambiguous whether this is a custom field or joined field. Search the main entity first. - if (dotSplit.length === 2) { - field = _.find(getEntity(entityName).fields, {name: dotSplit[0] + '.' + name}); - if (field) { - field.baseEntity = entityName; - return {field: field}; + // If 2 or more segments, the first might be the name of a join + if (dotSplit.length > 1) { + join = getJoin(dotSplit[0]); + if (join) { + dotSplit.shift(); + entityName = join.entity; } } - if (joinEntity) { - join = getJoin(joinEntity); - entityName = getJoin(joinEntity).entity; - } + name = dotSplit.join('.'); field = _.find(getEntity(entityName).fields, {name: name}); if (!field && join && join.bridge) { field = _.find(getEntity(join.bridge).fields, {name: name}); diff --git a/tests/phpunit/api/v4/Action/CustomJoinTest.php b/tests/phpunit/api/v4/Action/CustomJoinTest.php new file mode 100644 index 000000000000..9605577ee3da --- /dev/null +++ b/tests/phpunit/api/v4/Action/CustomJoinTest.php @@ -0,0 +1,73 @@ +addValue('name', 'MyContactRef') + ->addValue('extends', 'Individual') + ->execute() + ->first(); + + CustomField::create(FALSE) + ->addValue('label', 'FavPerson') + ->addValue('custom_group_id', $customGroup['id']) + ->addValue('html_type', 'Autocomplete-Select') + ->addValue('data_type', 'ContactReference') + ->execute(); + + $favPersonId = Contact::create(FALSE) + ->addValue('first_name', 'Favorite') + ->addValue('last_name', 'Person') + ->addValue('contact_type', 'Individual') + ->execute() + ->first()['id']; + + $contactId = Contact::create(FALSE) + ->addValue('first_name', 'Mya') + ->addValue('last_name', 'Tester') + ->addValue('contact_type', 'Individual') + ->addValue('MyContactRef.FavPerson', $favPersonId) + ->execute() + ->first()['id']; + + $contact = Contact::get(FALSE) + ->addSelect('display_name') + ->addSelect('MyContactRef.FavPerson.first_name') + ->addSelect('MyContactRef.FavPerson.last_name') + ->addWhere('id', '=', $contactId) + ->execute() + ->first(); + + $this->assertEquals('Favorite', $contact['MyContactRef.FavPerson.first_name']); + $this->assertEquals('Person', $contact['MyContactRef.FavPerson.last_name']); + } + +} diff --git a/tests/phpunit/api/v4/AllTests.php b/tests/phpunit/api/v4/AllTests.php index 67304b923a66..0a4965ecc559 100644 --- a/tests/phpunit/api/v4/AllTests.php +++ b/tests/phpunit/api/v4/AllTests.php @@ -16,37 +16,6 @@ * @copyright CiviCRM LLC https://civicrm.org/licensing */ -// vim: set si ai expandtab tabstop=4 shiftwidth=4 softtabstop=4: - -/** - * File for the api_v4_AllTests class - * - * (PHP 5) - * - * @author Walt Haas (801) 534-1262 - * @copyright Copyright CiviCRM LLC (C) 2009 - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html - * GNU Affero General Public License version 3 - * @version $Id: AllTests.php 40328 2012-05-11 23:06:13Z allen $ - * @package CiviCRM - * - * This file is part of CiviCRM - * - * CiviCRM is free software; you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation; either version 3 of - * the License, or (at your option) any later version. - * - * CiviCRM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this program. If not, see - * . - */ - /** * Class containing the APIv4 test suite * @@ -73,13 +42,3 @@ public static function suite() { } } -// class AllTests - -// -- set Emacs parameters -- -// Local variables: -// mode: php; -// tab-width: 4 -// c-basic-offset: 4 -// c-hanging-comment-ender-p: nil -// indent-tabs-mode: nil -// End: