Skip to content

Commit

Permalink
Merge pull request #26065 from colemanw/sqlFunctionOptions
Browse files Browse the repository at this point in the history
SearchKit - Display option values for field transformations
  • Loading branch information
colemanw authored Apr 17, 2023
2 parents f018f63 + c123e9c commit 5c7f605
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Civi/Api4/Query/SqlExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public static function convert(string $expression, $parseAlias = FALSE, $mustBe
$className = 'SqlEquation';
}
// If there are brackets but not the first character, we have a function
elseif ($bracketPos && $lastChar === ')') {
elseif ($bracketPos && preg_match('/^\w+\(.*\)(:[a-z]+)?$/', $expr)) {
$fnName = substr($expr, 0, $bracketPos);
if ($fnName !== strtoupper($fnName)) {
throw new \CRM_Core_Exception('Sql function must be uppercase.');
Expand Down
44 changes: 41 additions & 3 deletions Civi/Api4/Query/SqlFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ abstract class SqlFunction extends SqlExpression {
*/
protected $args = [];

/**
* Pseudoconstant suffix (for functions with option lists)
* @var string
*/
private $suffix;

/**
* Used for categorizing functions in the UI
*
Expand All @@ -47,7 +53,12 @@ abstract class SqlFunction extends SqlExpression {
* Parse the argument string into an array of function arguments
*/
protected function initialize() {
$arg = trim(substr($this->expr, strpos($this->expr, '(') + 1, -1));
$matches = [];
// Capture function argument string and possible suffix
preg_match('/[_A-Z]+\((.*)\)(:[a-z]+)?$/', $this->expr, $matches);
$arg = $matches[1];
$this->setSuffix($matches[2] ?? NULL);
// Parse function arguments string, match to declared function params
foreach ($this->getParams() as $idx => $param) {
$prefix = NULL;
$name = $param['name'] ?: ($idx + 1);
Expand Down Expand Up @@ -96,7 +107,7 @@ protected function initialize() {
}

/**
* Change $dataType according to output of function
* Set $dataType and convert value by suffix
*
* @see \Civi\Api4\Utils\FormattingUtil::formatOutputValues
* @param string $value
Expand All @@ -107,6 +118,24 @@ public function formatOutputValue($value, &$dataType) {
if (static::$dataType) {
$dataType = static::$dataType;
}
if (isset($value) && $this->suffix && $this->suffix !== 'id') {
$dataType = 'String';
$option = $this->getOptions()[$value] ?? NULL;
// Option contains an array of suffix keys
if (is_array($option)) {
return $option[$this->suffix] ?? NULL;
}
// Flat arrays are name/value pairs
elseif ($this->suffix === 'label') {
return $option;
}
elseif ($this->suffix === 'name') {
return $value;
}
else {
return NULL;
}
}
return $value;
}

Expand Down Expand Up @@ -150,7 +179,7 @@ private function renderArg($arg, Api4SelectQuery $query): string {
* @inheritDoc
*/
public function getAlias(): string {
return $this->alias ?? $this->getName() . ':' . implode('_', $this->fields);
return $this->alias ?? $this->getName() . ':' . implode('_', $this->fields) . ($this->suffix ? ':' . $this->suffix : '');
}

/**
Expand Down Expand Up @@ -229,6 +258,15 @@ public function getType(): string {
return 'SqlFunction';
}

/**
* @param string|null $suffix
*/
private function setSuffix(?string $suffix): void {
$this->suffix = $suffix ?
str_replace(':', '', $suffix) :
NULL;
}

/**
* @return string
*/
Expand Down
6 changes: 4 additions & 2 deletions ext/search_kit/ang/crmSearchAdmin.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,12 @@
return {field: field, join: join};
}
function parseFnArgs(info, expr) {
var fnName = expr.split('(')[0],
argString = expr.substr(fnName.length + 1, expr.length - fnName.length - 2);
var matches = /([_A-Z]+)\((.*)\)(:[a-z]+)?$/.exec(expr),
fnName = matches[1],
argString = matches[2];
info.fn = _.find(CRM.crmSearchAdmin.functions, {name: fnName || 'e'});
info.data_type = (info.fn && info.fn.data_type) || null;
info.suffix = matches[3];

function getKeyword(whitelist) {
var keyword;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@
ctrl.expr += args.join('');
ctrl.expr += ')';
if (ctrl.mode === 'select') {
// Add pseudoconstant suffix if function has an option list
if (ctrl.fn.options) {
ctrl.expr += ':label';
}
ctrl.expr += ' AS ' + makeAlias();
}
} else {
Expand Down
6 changes: 6 additions & 0 deletions tests/phpunit/api/v4/Action/SqlFunctionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ public function testDateFunctions() {
->addSelect('YEAR(birth_date) AS year')
->addSelect('QUARTER(birth_date) AS quarter')
->addSelect('MONTH(birth_date) AS month')
->addSelect('MONTH(birth_date):label AS month_name')
->addSelect('MONTH(birth_date):label')
->addSelect('EXTRACT(YEAR_MONTH FROM birth_date) AS year_month')
->addWhere('last_name', '=', $lastName)
->addOrderBy('id')
Expand All @@ -242,12 +244,16 @@ public function testDateFunctions() {
$this->assertEquals(2009, $result[0]['year']);
$this->assertEquals(4, $result[0]['quarter']);
$this->assertEquals(11, $result[0]['month']);
$this->assertEquals('November', $result[0]['month_name']);
$this->assertEquals('November', $result[0]['MONTH:birth_date:label']);
$this->assertEquals('200911', $result[0]['year_month']);

$this->assertEquals(0, $result[1]['diff']);
$this->assertEquals(2010, $result[1]['year']);
$this->assertEquals(1, $result[1]['quarter']);
$this->assertEquals(1, $result[1]['month']);
$this->assertEquals('January', $result[1]['month_name']);
$this->assertEquals('January', $result[1]['MONTH:birth_date:label']);
$this->assertEquals('201001', $result[1]['year_month']);
}

Expand Down

0 comments on commit 5c7f605

Please sign in to comment.