Skip to content

Commit

Permalink
Copy over core functions that are internal & subject to change
Browse files Browse the repository at this point in the history
  • Loading branch information
colemanw committed Jul 14, 2024
1 parent a985357 commit 737ca0f
Showing 1 changed file with 187 additions and 2 deletions.
189 changes: 187 additions & 2 deletions Civi/Api4/Service/Spec/Provider/EckEntitySpecProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Civi\Api4\Service\Spec\Provider;

use Civi\Api4\Service\Spec\FieldSpec;
use Civi\Api4\Service\Spec\RequestSpec;
use Civi\Api4\Service\Spec\SpecFormatter;
use Civi\Api4\Utils\CoreUtil;
use Civi\Api4\Utils\FormattingUtil;

/**
* Class EckEntitySpecProvider
Expand All @@ -22,10 +24,11 @@ public function modifySpec(RequestSpec $spec) {
$entityName = $spec->getEntity();
// Instantiate the BAO for initialization with the given ECK entity type.
$bao = new \CRM_Eck_BAO_Entity($entityTypes[$entityName]['name']);
// TODO: Use `Civi::entity()->getFields()` instead
foreach ($bao::getSupportedFields() as $DAOField) {
if (NULL === $spec->getFieldByName($DAOField['name'])) {
$this->setDynamicFk($DAOField, $spec);
$field = SpecFormatter::arrayToField($DAOField, $entityName);
$field = self::arrayToField($DAOField, $entityName);
$spec->addFieldSpec($field);
}
}
Expand All @@ -37,10 +40,192 @@ public function modifySpec(RequestSpec $spec) {
}
}

/**
* Copied from \Civi\Api4\Service\Spec\SpecFormatter
*
* TODO: Use `Civi::entity()->getFields()` instead of DAO::fields()
* so we don't need this copied function
*/
private static function arrayToField(array $data, string $entityName): FieldSpec {

Check failure on line 49 in Civi/Api4/Service/Spec/Provider/EckEntitySpecProvider.php

View workflow job for this annotation

GitHub Actions / PHP_CodeSniffer

Function's cyclomatic complexity (26) exceeds allowed maximum of 20
$dataTypeName = self::getDataType($data);

$hasDefault = isset($data['default']) && $data['default'] !== '';

$name = $data['name'] ?? NULL;
$field = new FieldSpec($name, $entityName, $dataTypeName);
$field->setType('Field');
$field->setColumnName($name);
$field->setNullable(empty($data['required']));
$field->setRequired(!empty($data['required']) && !$hasDefault && $name !== 'id');
$field->setTitle($data['title'] ?? NULL);
$field->setLabel($data['html']['label'] ?? NULL);
$field->setLocalizable($data['localizable'] ?? FALSE);
if (!empty($data['DFKEntities'])) {
$field->setDfkEntities($data['DFKEntities']);
}
if (!empty($data['pseudoconstant'])) {
// Do not load options if 'prefetch' is disabled
if (($data['pseudoconstant']['prefetch'] ?? NULL) !== 'disabled') {
$field->setOptionsCallback([__CLASS__, 'getOptions']);
}
// Explicitly declared suffixes
if (!empty($data['pseudoconstant']['suffixes'])) {
$suffixes = $data['pseudoconstant']['suffixes'];
}
else {
// These suffixes are always supported if a field has options
$suffixes = ['name', 'label'];
// Add other columns specified in schema (e.g. 'abbrColumn')
foreach (array_diff(FormattingUtil::$pseudoConstantSuffixes, $suffixes) as $suffix) {
if (!empty($data['pseudoconstant'][$suffix . 'Column'])) {
$suffixes[] = $suffix;
}
}
if (!empty($data['pseudoconstant']['optionGroupName'])) {
$suffixes = CoreUtil::getOptionValueFields($data['pseudoconstant']['optionGroupName'], 'name');
}
}
$field->setSuffixes($suffixes);
}
$field->setReadonly(!empty($data['readonly']));
if (isset($data['usage'])) {
$field->setUsage(array_keys(array_filter($data['usage'])));
}
if ($hasDefault) {
$field->setDefaultValue(FormattingUtil::convertDataType($data['default'], $dataTypeName));
}
$field->setSerialize($data['serialize'] ?? NULL);
$field->setDescription($data['description'] ?? NULL);
$field->setDeprecated($data['deprecated'] ?? FALSE);
self::setInputTypeAndAttrs($field, $data, $dataTypeName);

$field->setPermission($data['permission'] ?? NULL);
$fkAPIName = $data['FKApiName'] ?? NULL;
$fkClassName = $data['FKClassName'] ?? NULL;
if ($fkAPIName || $fkClassName) {
$field->setFkEntity($fkAPIName ?: CoreUtil::getApiNameFromBAO($fkClassName));
}
// For pseudo-fk fields like `civicrm_group.parents`
elseif (($data['html']['type'] ?? NULL) === 'EntityRef' && !empty($data['pseudoconstant']['table'])) {
$field->setFkEntity(CoreUtil::getApiNameFromTableName($data['pseudoconstant']['table']));
}
if (!empty($data['FKColumnName'])) {
$field->setFkColumn($data['FKColumnName']);
}

return $field;
}

/**
* Copied from \Civi\Api4\Service\Spec\SpecFormatter
*
* @param \Civi\Api4\Service\Spec\FieldSpec $fieldSpec
* @param array $data
* @param string $dataTypeName
*/
private static function setInputTypeAndAttrs(FieldSpec $fieldSpec, $data, $dataTypeName) {

Check failure on line 126 in Civi/Api4/Service/Spec/Provider/EckEntitySpecProvider.php

View workflow job for this annotation

GitHub Actions / PHP_CodeSniffer

Function's cyclomatic complexity (24) exceeds allowed maximum of 20
$inputType = $data['html']['type'] ?? $data['html_type'] ?? NULL;
$inputAttrs = $data['html'] ?? [];
unset($inputAttrs['type']);
// Custom field EntityRef or ContactRef filters
if (is_string($data['filter'] ?? NULL) && strpos($data['filter'], '=')) {
$filters = explode('&', $data['filter']);
$inputAttrs['filter'] = $filters;
}

$map = [
'Select Date' => 'Date',
'Link' => 'Url',
'Autocomplete-Select' => 'EntityRef',
];
$inputType = $map[$inputType] ?? $inputType;
if ($dataTypeName === 'ContactReference' || $dataTypeName === 'EntityReference') {
$inputType = 'EntityRef';
}
if (in_array($inputType, ['Select', 'EntityRef'], TRUE) && !empty($data['serialize'])) {
$inputAttrs['multiple'] = TRUE;
}
if ($inputType == 'Date' && !empty($inputAttrs['formatType'])) {
self::setLegacyDateFormat($inputAttrs);
}
// Number input for numeric fields
if ($inputType === 'Text' && in_array($dataTypeName, ['Integer', 'Float'], TRUE)) {
$inputType = 'Number';
// Todo: make 'step' configurable for the custom field
$inputAttrs['step'] = $dataTypeName === 'Integer' ? 1 : .01;
}
if ($inputType == 'Text' && !empty($data['maxlength'])) {
$inputAttrs['maxlength'] = (int) $data['maxlength'];
}
if ($inputType == 'TextArea') {
foreach (['rows', 'cols', 'note_rows', 'note_columns'] as $prop) {
if (!empty($data[$prop])) {
$key = str_replace('note_', '', $prop);
// per @colemanw https://github.com/civicrm/civicrm-core/pull/28388#issuecomment-1835717428
$key = str_replace('columns', 'cols', $key);
$inputAttrs[$key] = (int) $data[$prop];
}
}
}
// Ensure all keys use lower_case not camelCase
foreach ($inputAttrs as $key => $val) {
if ($key !== strtolower($key)) {
unset($inputAttrs[$key]);
$key = \CRM_Utils_String::convertStringToSnakeCase($key);
$inputAttrs[$key] = $val;
}
// Format EntityRef filter property (core and custom fields)
if ($key === 'filter' && is_array($val)) {
$filters = [];
foreach ($val as $filter) {
[$k, $v] = explode('=', $filter);
// Explode comma-separated values
$filters[$k] = strpos($v, ',') ? explode(',', $v) : $v;
}
// Legacy APIv3 custom field stuff
if ($dataTypeName === 'ContactReference') {
if (!empty($filters['group'])) {
$filters['groups'] = $filters['group'];
}
unset($filters['action'], $filters['group']);
}
$inputAttrs['filter'] = $filters;
}
}
// Custom autocompletes
if (!empty($data['option_group_id']) && $inputType === 'EntityRef') {
$fieldSpec->setFkEntity('OptionValue');
$inputAttrs['filter']['option_group_id'] = $data['option_group_id'];
}
$fieldSpec
->setInputType($inputType)
->setInputAttrs($inputAttrs);
}

/**
* Copied from \Civi\Api4\Service\Spec\SpecFormatter
*
* @param array $inputAttrs
*/
private static function setLegacyDateFormat(&$inputAttrs) {
if (empty(\Civi::$statics['legacyDatePrefs'][$inputAttrs['formatType']])) {
\Civi::$statics['legacyDatePrefs'][$inputAttrs['formatType']] = [];
$params = ['name' => $inputAttrs['formatType']];
\CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_PreferencesDate', $params, \Civi::$statics['legacyDatePrefs'][$inputAttrs['formatType']]);

Check warning on line 214 in Civi/Api4/Service/Spec/Provider/EckEntitySpecProvider.php

View workflow job for this annotation

GitHub Actions / PHP_CodeSniffer

Line exceeds 120 characters; contains 140 characters
}
$dateFormat = \Civi::$statics['legacyDatePrefs'][$inputAttrs['formatType']];
unset($inputAttrs['formatType']);
$inputAttrs['time'] = !empty($dateFormat['time_format']);
$inputAttrs['date'] = TRUE;
$inputAttrs['start_date_years'] = (int) $dateFormat['start'];
$inputAttrs['end_date_years'] = (int) $dateFormat['end'];
}

/**
* Copied from \Civi\Api4\Service\Spec\SpecGatherer::setDynamicFk()
*
* @param array{name: string, FKClassName: string, bao: string, type: int, DFKEntities: array<mixed>} $DAOField
* @param \Civi\Api4\Service\Spec\RequestSpec $spec
*
* Adds metadata about dynamic foreign key fields.
*
Expand Down

0 comments on commit 737ca0f

Please sign in to comment.