Skip to content

Commit

Permalink
Merge pull request #19516 from colemanw/apiGetFkFields
Browse files Browse the repository at this point in the history
APIv4 - Enable getFields to find fields across implicit FK joins
  • Loading branch information
eileenmcnaughton authored Feb 3, 2021
2 parents c28b32d + 37d2f98 commit fdc8f59
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 11 deletions.
43 changes: 38 additions & 5 deletions Civi/Api4/Generic/DAOGetFieldsAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,48 @@ class DAOGetFieldsAction extends BasicGetFieldsAction {
* @return array
*/
protected function getRecords() {
$fields = $this->_itemsToGet('name');
$fieldsToGet = $this->_itemsToGet('name');
/** @var \Civi\Api4\Service\Spec\SpecGatherer $gatherer */
$gatherer = \Civi::container()->get('spec_gatherer');
// Any fields name with a dot in it is custom
if ($fields) {
$this->includeCustom = strpos(implode('', $fields), '.') !== FALSE;
// Any fields name with a dot in it is either custom or an implicit join
if ($fieldsToGet) {
$this->includeCustom = strpos(implode('', $fieldsToGet), '.') !== FALSE;
}
$spec = $gatherer->getSpec($this->getEntityName(), $this->getAction(), $this->includeCustom, $this->values);
return SpecFormatter::specToArray($spec->getFields($fields), $this->loadOptions, $this->values);
$fields = SpecFormatter::specToArray($spec->getFields($fieldsToGet), $this->loadOptions, $this->values);
foreach ($fieldsToGet ?? [] as $fieldName) {
if (empty($fields[$fieldName]) && strpos($fieldName, '.') !== FALSE) {
$fkField = $this->getFkFieldSpec($fieldName, $fields);
if ($fkField) {
$fkField['name'] = $fieldName;
$fields[] = $fkField;
}
}
}
return $fields;
}

/**
* @param string $fieldName
* @param array $fields
* @return array|null
* @throws \API_Exception
*/
private function getFkFieldSpec($fieldName, $fields) {
$fieldPath = explode('.', $fieldName);
// Search for the first segment alone plus the first and second
// No field in the schema contains more than one dot in its name.
$searchPaths = [$fieldPath[0], $fieldPath[0] . '.' . $fieldPath[1]];
$fkFieldName = array_intersect($searchPaths, array_keys($fields))[0] ?? NULL;
if ($fkFieldName && !empty($fields[$fkFieldName]['fk_entity'])) {
$newFieldName = substr($fieldName, 1 + strlen($fkFieldName));
return civicrm_api4($fields[$fkFieldName]['fk_entity'], 'getFields', [
'checkPermissions' => $this->checkPermissions,
'where' => [['name', '=', $newFieldName]],
'loadOptions' => $this->loadOptions,
'action' => $this->action,
])->first();
}
}

public function fields() {
Expand Down
14 changes: 8 additions & 6 deletions Civi/Api4/Service/Spec/RequestSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,15 @@ public function getFields($fieldNames = NULL) {
if (!$fieldNames) {
return $this->fields;
}
$fields = [];
foreach ($this->fields as $field) {
if (in_array($field->getName(), $fieldNames)) {
$fields[] = $field;
// Return all exact matches plus partial matches (to support retrieving fk fields)
return array_filter($this->fields, function($field) use($fieldNames) {
foreach ($fieldNames as $fieldName) {
if (strpos($fieldName, $field->getName()) === 0) {
return TRUE;
}
}
}
return $fields;
return FALSE;
});
}

/**
Expand Down
14 changes: 14 additions & 0 deletions tests/phpunit/api/v4/Action/GetExtraFieldsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,18 @@ public function testGetOptionsAddress() {
$this->assertContains('Alberta', $caOptions['options']);
}

public function testGetFkFields() {
$fields = \Civi\Api4\Participant::getFields()
->setLoadOptions(TRUE)
->addWhere('name', 'IN', ['event_id', 'event_id.created_id', 'contact_id.gender_id', 'event_id.created_id.sort_name'])
->execute()
->indexBy('name');

$this->assertCount(4, $fields);
$this->assertEquals('Participant', $fields['event_id']['entity']);
$this->assertEquals('Event', $fields['event_id.created_id']['entity']);
$this->assertEquals('Contact', $fields['event_id.created_id.sort_name']['entity']);
$this->assertGreaterThan(1, count($fields['contact_id.gender_id']['options']));
}

}

0 comments on commit fdc8f59

Please sign in to comment.