Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve APIv4 selectUtils to handle join paths in fieldnames. #16904

Merged
merged 1 commit into from
Mar 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions Civi/Api4/Utils/SelectUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,32 @@ public static function isFieldSelected($field, $selects) {
}

/**
* Filters a list of fieldnames by matching a pattern which may contain * wildcards.
*
* For fieldnames joined with a dot (e.g. email.contact_id), wildcards are only allowed after the last dot.
*
* @param string $pattern
* @param array $fieldNames
* @return array
*/
public static function getMatchingFields($pattern, $fieldNames) {
// If the pattern is "select all" then we return all base fields (excluding those with a dot)
if ($pattern === '*') {
return $fieldNames;
return array_values(array_filter($fieldNames, function($field) {
return strpos($field, '.') === FALSE;
}));
}
$pattern = '/^' . str_replace('\*', '.*', preg_quote($pattern, '/')) . '$/';
return array_values(array_filter($fieldNames, function($field) use ($pattern) {
return preg_match($pattern, $field);
$dot = strrpos($pattern, '.');
$prefix = $dot === FALSE ? '' : substr($pattern, 0, $dot + 1);
$search = $dot === FALSE ? $pattern : substr($pattern, $dot + 1);
$search = '/^' . str_replace('\*', '.*', preg_quote($search, '/')) . '$/';
return array_values(array_filter($fieldNames, function($field) use ($search, $prefix) {
// Exclude fields that don't have the same join prefix
if (($prefix !== '' && strpos($field, $prefix) !== 0) || substr_count($prefix, '.') !== substr_count($field, '.')) {
return FALSE;
}
// Now strip the prefix and compare field name to the pattern
return preg_match($search, substr($field, strlen($prefix)));
}));
}

Expand Down
14 changes: 13 additions & 1 deletion tests/phpunit/api/v4/Utils/SelectUtilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ class SelectUtilTest extends UnitTestCase {
'reset_date',
'signature_text',
'signature_html',
'contact.id',
'contact.display_name',
'contact.sort_name',
'contact.phone.id',
'contact.phone.phone',
'contact.phone.phone_type_id',
];

public function getSelectExamples() {
Expand All @@ -52,6 +58,8 @@ public function getSelectExamples() {
['one', ['o*', 'two'], TRUE],
['one', ['*o', 'two'], FALSE],
['zoo', ['one', 'two'], FALSE],
['one.id', ['one.id', 'two'], TRUE],
['one.id', ['one.*', 'two'], TRUE],
];
}

Expand All @@ -67,14 +75,18 @@ public function testIsFieldSelected($field, $selects, $expected) {

public function getMatchingExamples() {
return [
[$this->emailFieldNames, '*'],
[array_slice($this->emailFieldNames, 0, 12), '*'],
[[], 'nothing'],
[['email'], 'email'],
[['contact_id', 'location_type_id'], '*_id'],
[['contact_id', 'location_type_id'], '*o*_id'],
[['contact_id'], 'con*_id'],
[['is_primary', 'is_billing', 'is_bulkmail'], 'is_*'],
[['is_billing', 'is_bulkmail'], 'is_*l*'],
[['contact.id', 'contact.display_name', 'contact.sort_name'], 'contact.*'],
[['contact.display_name', 'contact.sort_name'], 'contact.*_name'],
[['contact.phone.id', 'contact.phone.phone', 'contact.phone.phone_type_id'], 'contact.phone.*'],
[['contact.phone.phone', 'contact.phone.phone_type_id'], 'contact.phone.phone*'],
];
}

Expand Down