diff --git a/CRM/Api4/Page/Api4Explorer.php b/CRM/Api4/Page/Api4Explorer.php index 0f1125f5ca2c..9cd4508fc56b 100644 --- a/CRM/Api4/Page/Api4Explorer.php +++ b/CRM/Api4/Page/Api4Explorer.php @@ -61,6 +61,7 @@ public static function getSqlFunctions() { 'title' => $className::getTitle(), 'params' => $className::getParams(), 'category' => $className::getCategory(), + 'dataType' => $className::getDataType(), ]; } } diff --git a/Civi/Api4/Query/SqlFunction.php b/Civi/Api4/Query/SqlFunction.php index 44923c6a5626..c7ee3ad71338 100644 --- a/Civi/Api4/Query/SqlFunction.php +++ b/Civi/Api4/Query/SqlFunction.php @@ -18,11 +18,6 @@ */ abstract class SqlFunction extends SqlExpression { - /** - * @var array - */ - protected static $params = []; - /** * @var array[] */ @@ -35,6 +30,13 @@ abstract class SqlFunction extends SqlExpression { */ protected static $category; + /** + * Data type output by this function + * + * @var string + */ + protected static $dataType; + const CATEGORY_AGGREGATE = 'aggregate', CATEGORY_COMPARISON = 'comparison', CATEGORY_DATE = 'date', @@ -47,23 +49,57 @@ abstract class SqlFunction extends SqlExpression { protected function initialize() { $arg = trim(substr($this->expr, strpos($this->expr, '(') + 1, -1)); foreach ($this->getParams() as $idx => $param) { - $prefix = $this->captureKeyword($param['prefix'], $arg); + $prefix = NULL; + if ($param['prefix']) { + $prefix = $this->captureKeyword([$param['prefix']], $arg); + // Supply api_default + if (!$prefix && isset($param['api_default'])) { + $this->args[$idx] = [ + 'prefix' => $param['api_default']['prefix'] ?? [$param['prefix']], + 'expr' => array_map([parent::class, 'convert'], $param['api_default']['expr']), + 'suffix' => $param['api_default']['suffix'] ?? [], + ]; + continue; + } + if (!$prefix && !$param['optional']) { + throw new \API_Exception("Missing {$param['prefix']} for SQL function " . static::getName()); + } + } + elseif ($param['flag_before']) { + $prefix = $this->captureKeyword(array_keys($param['flag_before']), $arg); + } $this->args[$idx] = [ - 'prefix' => $prefix, + 'prefix' => (array) $prefix, 'expr' => [], - 'suffix' => NULL, + 'suffix' => [], ]; - if ($param['max_expr'] && isset($prefix) || in_array('', $param['prefix']) || !$param['optional']) { + if ($param['max_expr'] && (!$param['prefix'] || $param['prefix'] === $prefix)) { $exprs = $this->captureExpressions($arg, $param['must_be'], $param['cant_be']); if (count($exprs) < $param['min_expr'] || count($exprs) > $param['max_expr']) { throw new \API_Exception('Incorrect number of arguments for SQL function ' . static::getName()); } $this->args[$idx]['expr'] = $exprs; - $this->args[$idx]['suffix'] = $this->captureKeyword($param['suffix'], $arg); + + $this->args[$idx]['suffix'] = (array) $this->captureKeyword(array_keys($param['flag_after']), $arg); } } } + /** + * Change $dataType according to output of function + * + * @see \Civi\Api4\Utils\FormattingUtil::formatOutputValues + * @param string $value + * @param string $dataType + * @return string + */ + public function formatOutputValue($value, &$dataType) { + if (static::$dataType) { + $dataType = static::$dataType; + } + return $value; + } + /** * Shift a keyword off the beginning of the argument string and return it. * @@ -73,7 +109,7 @@ protected function initialize() { * @return mixed|null */ private function captureKeyword($keywords, &$arg) { - foreach (array_filter($keywords) as $key) { + foreach ($keywords as $key) { if (strpos($arg, $key . ' ') === 0) { $arg = ltrim(substr($arg, strlen($key))); return $key; @@ -183,23 +219,15 @@ public function render(array $fieldList): string { * @return string */ private function renderArg($arg, $param, $fieldList): string { - // Supply api_default - if (!isset($arg['prefix']) && !isset($arg['suffix']) && empty($arg['expr']) && !empty($param['api_default'])) { - $arg = [ - 'prefix' => $param['api_default']['prefix'] ?? reset($param['prefix']), - 'expr' => array_map([parent::class, 'convert'], $param['api_default']['expr'] ?? []), - 'suffix' => $param['api_default']['suffix'] ?? reset($param['suffix']), - ]; - } - $rendered = $arg['prefix'] ?? ''; + $rendered = implode(' ', $arg['prefix']); foreach ($arg['expr'] ?? [] as $idx => $expr) { if (strlen($rendered) || $idx) { $rendered .= $idx ? ', ' : ' '; } $rendered .= $expr->render($fieldList); } - if (isset($arg['suffix'])) { - $rendered .= (strlen($rendered) ? ' ' : '') . $arg['suffix']; + if ($arg['suffix']) { + $rendered .= (strlen($rendered) ? ' ' : '') . implode(' ', $arg['suffix']); } return $rendered; } @@ -224,15 +252,16 @@ public static function getName(): string { * Get the param metadata for this sql function. * @return array */ - public static function getParams(): array { + final public static function getParams(): array { $params = []; - foreach (static::$params as $param) { + foreach (static::params() as $param) { // Merge in defaults to ensure each param has these properties $params[] = $param + [ - 'prefix' => [], + 'prefix' => NULL, 'min_expr' => 1, 'max_expr' => 1, - 'suffix' => [], + 'flag_before' => [], + 'flag_after' => [], 'optional' => FALSE, 'must_be' => [], 'cant_be' => ['SqlWild'], @@ -242,6 +271,8 @@ public static function getParams(): array { return $params; } + abstract protected static function params(): array; + /** * Get the arguments passed to this sql function instance. * @return array[] @@ -257,6 +288,13 @@ public static function getCategory(): string { return static::$category; } + /** + * @return string|NULL + */ + public static function getDataType():? string { + return static::$dataType; + } + /** * @return string */ diff --git a/Civi/Api4/Query/SqlFunctionABS.php b/Civi/Api4/Query/SqlFunctionABS.php index a5909ec8181e..015906c4def6 100644 --- a/Civi/Api4/Query/SqlFunctionABS.php +++ b/Civi/Api4/Query/SqlFunctionABS.php @@ -18,12 +18,14 @@ class SqlFunctionABS extends SqlFunction { protected static $category = self::CATEGORY_MATH; - protected static $params = [ - [ - 'optional' => FALSE, - 'must_be' => ['SqlField', 'SqlNumber'], - ], - ]; + protected static function params(): array { + return [ + [ + 'optional' => FALSE, + 'must_be' => ['SqlField', 'SqlNumber'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionAVG.php b/Civi/Api4/Query/SqlFunctionAVG.php index a9787d2e96be..59e6548a7309 100644 --- a/Civi/Api4/Query/SqlFunctionAVG.php +++ b/Civi/Api4/Query/SqlFunctionAVG.php @@ -18,12 +18,14 @@ class SqlFunctionAVG extends SqlFunction { protected static $category = self::CATEGORY_AGGREGATE; - protected static $params = [ - [ - 'prefix' => ['', 'DISTINCT', 'ALL'], - 'must_be' => ['SqlField'], - ], - ]; + protected static function params(): array { + return [ + [ + 'flag_before' => ['DISTINCT' => ts('Distinct')], + 'must_be' => ['SqlField'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionCOALESCE.php b/Civi/Api4/Query/SqlFunctionCOALESCE.php index 324d62fecf76..62622be45f6f 100644 --- a/Civi/Api4/Query/SqlFunctionCOALESCE.php +++ b/Civi/Api4/Query/SqlFunctionCOALESCE.php @@ -18,12 +18,16 @@ class SqlFunctionCOALESCE extends SqlFunction { protected static $category = self::CATEGORY_COMPARISON; - protected static $params = [ - [ - 'max_expr' => 99, - 'optional' => FALSE, - ], - ]; + protected static $dataType = 'String'; + + protected static function params(): array { + return [ + [ + 'max_expr' => 99, + 'optional' => FALSE, + ], + ]; + } /** * @return string @@ -32,17 +36,4 @@ public static function getTitle(): string { return ts('Coalesce'); } - /** - * Prevent reformatting - * - * @see \Civi\Api4\Utils\FormattingUtil::formatOutputValues - * @param string $value - * @param string $dataType - * @return string|array - */ - public function formatOutputValue($value, &$dataType) { - $dataType = NULL; - return $value; - } - } diff --git a/Civi/Api4/Query/SqlFunctionCONCAT.php b/Civi/Api4/Query/SqlFunctionCONCAT.php index 94f90abdebfc..a1c201fec288 100644 --- a/Civi/Api4/Query/SqlFunctionCONCAT.php +++ b/Civi/Api4/Query/SqlFunctionCONCAT.php @@ -18,13 +18,17 @@ class SqlFunctionCONCAT extends SqlFunction { protected static $category = self::CATEGORY_STRING; - protected static $params = [ - [ - 'max_expr' => 99, - 'optional' => FALSE, - 'must_be' => ['SqlField', 'SqlString'], - ], - ]; + protected static $dataType = 'String'; + + protected static function params(): array { + return [ + [ + 'max_expr' => 99, + 'optional' => FALSE, + 'must_be' => ['SqlField', 'SqlString'], + ], + ]; + } /** * @return string @@ -33,17 +37,4 @@ public static function getTitle(): string { return ts('Combine'); } - /** - * Prevent reformatting of result - * - * @see \Civi\Api4\Utils\FormattingUtil::formatOutputValues - * @param string $value - * @param string $dataType - * @return string|array - */ - public function formatOutputValue($value, &$dataType) { - $dataType = NULL; - return $value; - } - } diff --git a/Civi/Api4/Query/SqlFunctionCOUNT.php b/Civi/Api4/Query/SqlFunctionCOUNT.php index 8d52c9c73393..c51e024856ab 100644 --- a/Civi/Api4/Query/SqlFunctionCOUNT.php +++ b/Civi/Api4/Query/SqlFunctionCOUNT.php @@ -18,27 +18,17 @@ class SqlFunctionCOUNT extends SqlFunction { protected static $category = self::CATEGORY_AGGREGATE; - protected static $params = [ - [ - 'prefix' => ['', 'DISTINCT', 'ALL'], - 'max_expr' => 1, - 'must_be' => ['SqlField', 'SqlWild'], - 'cant_be' => [], - ], - ]; + protected static $dataType = 'Integer'; - /** - * Reformat result as integer - * - * @see \Civi\Api4\Utils\FormattingUtil::formatOutputValues - * @param string $value - * @param string $dataType - * @return string|array - */ - public function formatOutputValue($value, &$dataType) { - // Count is always an integer - $dataType = 'Integer'; - return (int) $value; + protected static function params(): array { + return [ + [ + 'flag_before' => ['DISTINCT' => ts('Distinct')], + 'max_expr' => 1, + 'must_be' => ['SqlField', 'SqlWild'], + 'cant_be' => [], + ], + ]; } /** diff --git a/Civi/Api4/Query/SqlFunctionCURDATE.php b/Civi/Api4/Query/SqlFunctionCURDATE.php index b7687d711e7a..f81993b53662 100644 --- a/Civi/Api4/Query/SqlFunctionCURDATE.php +++ b/Civi/Api4/Query/SqlFunctionCURDATE.php @@ -18,6 +18,10 @@ class SqlFunctionCURDATE extends SqlFunction { protected static $category = self::CATEGORY_DATE; + protected static function params(): array { + return []; + } + /** * @return string */ diff --git a/Civi/Api4/Query/SqlFunctionDATE.php b/Civi/Api4/Query/SqlFunctionDATE.php new file mode 100644 index 000000000000..05362a510349 --- /dev/null +++ b/Civi/Api4/Query/SqlFunctionDATE.php @@ -0,0 +1,39 @@ + 1, + 'optional' => FALSE, + ], + ]; + } + + /** + * @return string + */ + public static function getTitle(): string { + return ts('Date Only'); + } + +} diff --git a/Civi/Api4/Query/SqlFunctionGREATEST.php b/Civi/Api4/Query/SqlFunctionGREATEST.php index 47212da6bbe4..549e59a3de2f 100644 --- a/Civi/Api4/Query/SqlFunctionGREATEST.php +++ b/Civi/Api4/Query/SqlFunctionGREATEST.php @@ -20,12 +20,14 @@ class SqlFunctionGREATEST extends SqlFunction { protected static $category = self::CATEGORY_COMPARISON; - protected static $params = [ - [ - 'max_expr' => 99, - 'optional' => FALSE, - ], - ]; + protected static function params(): array { + return [ + [ + 'max_expr' => 99, + 'optional' => FALSE, + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionGROUP_CONCAT.php b/Civi/Api4/Query/SqlFunctionGROUP_CONCAT.php index c4b9b923a14a..939c2c75536c 100644 --- a/Civi/Api4/Query/SqlFunctionGROUP_CONCAT.php +++ b/Civi/Api4/Query/SqlFunctionGROUP_CONCAT.php @@ -20,31 +20,33 @@ class SqlFunctionGROUP_CONCAT extends SqlFunction { protected static $category = self::CATEGORY_AGGREGATE; - protected static $params = [ - [ - 'prefix' => ['', 'DISTINCT', 'ALL'], - 'max_expr' => 1, - 'must_be' => ['SqlField', 'SqlFunction'], - 'optional' => FALSE, - ], - [ - 'prefix' => ['ORDER BY'], - 'max_expr' => 1, - 'suffix' => ['', 'ASC', 'DESC'], - 'must_be' => ['SqlField'], - 'optional' => TRUE, - ], - [ - 'prefix' => ['SEPARATOR'], - 'max_expr' => 1, - 'must_be' => ['SqlString'], - 'optional' => TRUE, - // @see self::formatOutput() - 'api_default' => [ - 'expr' => ['"' . \CRM_Core_DAO::VALUE_SEPARATOR . '"'], + protected static function params(): array { + return [ + [ + 'flag_before' => ['DISTINCT' => ts('Distinct')], + 'max_expr' => 1, + 'must_be' => ['SqlField', 'SqlFunction'], + 'optional' => FALSE, ], - ], - ]; + [ + 'prefix' => 'ORDER BY', + 'max_expr' => 1, + 'flag_after' => ['ASC' => ts('Ascending'), 'DESC' => ts('Descending')], + 'must_be' => ['SqlField'], + 'optional' => TRUE, + ], + [ + 'prefix' => 'SEPARATOR', + 'max_expr' => 1, + 'must_be' => ['SqlString'], + 'optional' => TRUE, + // @see self::formatOutput() + 'api_default' => [ + 'expr' => ['"' . \CRM_Core_DAO::VALUE_SEPARATOR . '"'], + ], + ], + ]; + } /** * Reformat result as array if using default separator @@ -57,16 +59,16 @@ class SqlFunctionGROUP_CONCAT extends SqlFunction { public function formatOutputValue($value, &$dataType) { $exprArgs = $this->getArgs(); // By default, values are split into an array and formatted according to the field's dataType - if (!$exprArgs[2]['prefix']) { + if (isset($exprArgs[2]['expr'][0]->expr) && $exprArgs[2]['expr'][0]->expr === \CRM_Core_DAO::VALUE_SEPARATOR) { $value = explode(\CRM_Core_DAO::VALUE_SEPARATOR, $value); // If the first expression is another sqlFunction, allow it to control the dataType - if ($exprArgs[0]['expr'][0] instanceof SqlFunction && is_callable([$exprArgs[0]['expr'][0], 'formatOutputValue'])) { + if ($exprArgs[0]['expr'][0] instanceof SqlFunction) { $exprArgs[0]['expr'][0]->formatOutputValue(NULL, $dataType); } } - // If using custom separator, unset $dataType to preserve raw string + // If using custom separator, preserve raw string else { - $dataType = NULL; + $dataType = 'String'; } return $value; } diff --git a/Civi/Api4/Query/SqlFunctionIF.php b/Civi/Api4/Query/SqlFunctionIF.php index 4a1e244c7dba..1b120d4fed94 100644 --- a/Civi/Api4/Query/SqlFunctionIF.php +++ b/Civi/Api4/Query/SqlFunctionIF.php @@ -18,13 +18,17 @@ class SqlFunctionIF extends SqlFunction { protected static $category = self::CATEGORY_COMPARISON; - protected static $params = [ - [ - 'min_expr' => 3, - 'max_expr' => 3, - 'optional' => FALSE, - ], - ]; + protected static $dataType = 'String'; + + protected static function params(): array { + return [ + [ + 'min_expr' => 3, + 'max_expr' => 3, + 'optional' => FALSE, + ], + ]; + } /** * @return string @@ -33,17 +37,4 @@ public static function getTitle(): string { return ts('If'); } - /** - * Prevent formatting based on first field - * - * @see \Civi\Api4\Utils\FormattingUtil::formatOutputValues - * @param string $value - * @param string $dataType - * @return string|array - */ - public function formatOutputValue($value, &$dataType) { - $dataType = NULL; - return $value; - } - } diff --git a/Civi/Api4/Query/SqlFunctionISNULL.php b/Civi/Api4/Query/SqlFunctionISNULL.php index 7b614a538111..beeaebc77d53 100644 --- a/Civi/Api4/Query/SqlFunctionISNULL.php +++ b/Civi/Api4/Query/SqlFunctionISNULL.php @@ -18,11 +18,15 @@ class SqlFunctionISNULL extends SqlFunction { protected static $category = self::CATEGORY_COMPARISON; - protected static $params = [ - [ - 'optional' => FALSE, - ], - ]; + protected static $dataType = 'Boolean'; + + protected static function params(): array { + return [ + [ + 'optional' => FALSE, + ], + ]; + } /** * @return string @@ -31,18 +35,4 @@ public static function getTitle(): string { return ts('Is null'); } - /** - * Reformat result as boolean - * - * @see \Civi\Api4\Utils\FormattingUtil::formatOutputValues - * @param string $value - * @param string $dataType - * @return string|array - */ - public function formatOutputValue($value, &$dataType) { - // Value is always TRUE or FALSE - $dataType = 'Boolean'; - return $value; - } - } diff --git a/Civi/Api4/Query/SqlFunctionLEAST.php b/Civi/Api4/Query/SqlFunctionLEAST.php index 89aa121e8cf8..6317647241de 100644 --- a/Civi/Api4/Query/SqlFunctionLEAST.php +++ b/Civi/Api4/Query/SqlFunctionLEAST.php @@ -20,12 +20,14 @@ class SqlFunctionLEAST extends SqlFunction { protected static $category = self::CATEGORY_COMPARISON; - protected static $params = [ - [ - 'max_expr' => 99, - 'optional' => FALSE, - ], - ]; + protected static function params(): array { + return [ + [ + 'max_expr' => 99, + 'optional' => FALSE, + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionLOWER.php b/Civi/Api4/Query/SqlFunctionLOWER.php index 0dc052123f02..47763c2bc4f2 100644 --- a/Civi/Api4/Query/SqlFunctionLOWER.php +++ b/Civi/Api4/Query/SqlFunctionLOWER.php @@ -18,12 +18,14 @@ class SqlFunctionLOWER extends SqlFunction { protected static $category = self::CATEGORY_STRING; - protected static $params = [ - [ - 'optional' => FALSE, - 'must_be' => ['SqlField', 'SqlString'], - ], - ]; + protected static function params(): array { + return [ + [ + 'optional' => FALSE, + 'must_be' => ['SqlField', 'SqlString'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionMAX.php b/Civi/Api4/Query/SqlFunctionMAX.php index f26a2c0aa58c..6e3a880bf4c2 100644 --- a/Civi/Api4/Query/SqlFunctionMAX.php +++ b/Civi/Api4/Query/SqlFunctionMAX.php @@ -20,12 +20,14 @@ class SqlFunctionMAX extends SqlFunction { protected static $category = self::CATEGORY_AGGREGATE; - protected static $params = [ - [ - 'prefix' => ['', 'DISTINCT', 'ALL'], - 'must_be' => ['SqlField'], - ], - ]; + protected static function params(): array { + return [ + [ + 'flag_before' => ['DISTINCT' => ts('Distinct')], + 'must_be' => ['SqlField'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionMIN.php b/Civi/Api4/Query/SqlFunctionMIN.php index 756aeb03921d..3c6a79227cd4 100644 --- a/Civi/Api4/Query/SqlFunctionMIN.php +++ b/Civi/Api4/Query/SqlFunctionMIN.php @@ -20,12 +20,14 @@ class SqlFunctionMIN extends SqlFunction { protected static $category = self::CATEGORY_AGGREGATE; - protected static $params = [ - [ - 'prefix' => ['', 'DISTINCT', 'ALL'], - 'must_be' => ['SqlField'], - ], - ]; + protected static function params(): array { + return [ + [ + 'flag_before' => ['DISTINCT' => ts('Distinct')], + 'must_be' => ['SqlField'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionNULLIF.php b/Civi/Api4/Query/SqlFunctionNULLIF.php index f5602cd2cda5..79e117a7fdb1 100644 --- a/Civi/Api4/Query/SqlFunctionNULLIF.php +++ b/Civi/Api4/Query/SqlFunctionNULLIF.php @@ -20,13 +20,15 @@ class SqlFunctionNULLIF extends SqlFunction { protected static $category = self::CATEGORY_COMPARISON; - protected static $params = [ - [ - 'min_expr' => 2, - 'max_expr' => 2, - 'optional' => FALSE, - ], - ]; + protected static function params(): array { + return [ + [ + 'min_expr' => 2, + 'max_expr' => 2, + 'optional' => FALSE, + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionREPLACE.php b/Civi/Api4/Query/SqlFunctionREPLACE.php index b43a7ab80246..27c7c1b51c98 100644 --- a/Civi/Api4/Query/SqlFunctionREPLACE.php +++ b/Civi/Api4/Query/SqlFunctionREPLACE.php @@ -18,20 +18,16 @@ class SqlFunctionREPLACE extends SqlFunction { protected static $category = self::CATEGORY_STRING; - protected static $params = [ - [ - 'optional' => FALSE, - 'must_be' => ['SqlField', 'SqlString'], - ], - [ - 'optional' => FALSE, - 'must_be' => ['SqlField', 'SqlString'], - ], - [ - 'optional' => FALSE, - 'must_be' => ['SqlField', 'SqlString'], - ], - ]; + protected static function params(): array { + return [ + [ + 'min_expr' => 3, + 'max_expr' => 3, + 'optional' => FALSE, + 'must_be' => ['SqlField', 'SqlString'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionROUND.php b/Civi/Api4/Query/SqlFunctionROUND.php index 28ee78caab01..5a57c584b1a1 100644 --- a/Civi/Api4/Query/SqlFunctionROUND.php +++ b/Civi/Api4/Query/SqlFunctionROUND.php @@ -18,16 +18,18 @@ class SqlFunctionROUND extends SqlFunction { protected static $category = self::CATEGORY_MATH; - protected static $params = [ - [ - 'optional' => FALSE, - 'must_be' => ['SqlField', 'SqlNumber'], - ], - [ - 'optional' => TRUE, - 'must_be' => ['SqlNumber'], - ], - ]; + protected static function params(): array { + return [ + [ + 'optional' => FALSE, + 'must_be' => ['SqlField', 'SqlNumber'], + ], + [ + 'optional' => TRUE, + 'must_be' => ['SqlNumber'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionSUM.php b/Civi/Api4/Query/SqlFunctionSUM.php index 81b49005f6de..28522692aa01 100644 --- a/Civi/Api4/Query/SqlFunctionSUM.php +++ b/Civi/Api4/Query/SqlFunctionSUM.php @@ -18,12 +18,14 @@ class SqlFunctionSUM extends SqlFunction { protected static $category = self::CATEGORY_AGGREGATE; - protected static $params = [ - [ - 'prefix' => ['', 'DISTINCT', 'ALL'], - 'must_be' => ['SqlField'], - ], - ]; + protected static function params(): array { + return [ + [ + 'flag_before' => ['DISTINCT' => ts('Distinct')], + 'must_be' => ['SqlField'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionTIME.php b/Civi/Api4/Query/SqlFunctionTIME.php new file mode 100644 index 000000000000..e55b48b8671d --- /dev/null +++ b/Civi/Api4/Query/SqlFunctionTIME.php @@ -0,0 +1,39 @@ + 1, + 'optional' => FALSE, + ], + ]; + } + + /** + * @return string + */ + public static function getTitle(): string { + return ts('Time Only'); + } + +} diff --git a/Civi/Api4/Query/SqlFunctionUPPER.php b/Civi/Api4/Query/SqlFunctionUPPER.php index ed4590e29077..cc21cd1aaadf 100644 --- a/Civi/Api4/Query/SqlFunctionUPPER.php +++ b/Civi/Api4/Query/SqlFunctionUPPER.php @@ -18,12 +18,14 @@ class SqlFunctionUPPER extends SqlFunction { protected static $category = self::CATEGORY_STRING; - protected static $params = [ - [ - 'optional' => FALSE, - 'must_be' => ['SqlField', 'SqlString'], - ], - ]; + protected static function params(): array { + return [ + [ + 'optional' => FALSE, + 'must_be' => ['SqlField', 'SqlString'], + ], + ]; + } /** * @return string diff --git a/Civi/Api4/Query/SqlFunctionYEAR.php b/Civi/Api4/Query/SqlFunctionYEAR.php new file mode 100644 index 000000000000..81b0ae52a57d --- /dev/null +++ b/Civi/Api4/Query/SqlFunctionYEAR.php @@ -0,0 +1,39 @@ + 1, + 'optional' => FALSE, + ], + ]; + } + + /** + * @return string + */ + public static function getTitle(): string { + return ts('Year Only'); + } + +} diff --git a/Civi/Api4/Utils/FormattingUtil.php b/Civi/Api4/Utils/FormattingUtil.php index 065ab8ea9d39..8bcc5de9c32a 100644 --- a/Civi/Api4/Utils/FormattingUtil.php +++ b/Civi/Api4/Utils/FormattingUtil.php @@ -200,13 +200,10 @@ public static function formatOutputValues(&$results, $fields, $entity, $action = $fieldName = \CRM_Utils_Array::first($fieldExpr->getFields()); $field = $fieldName && isset($fields[$fieldName]) ? $fields[$fieldName] : NULL; $dataType = $field['data_type'] ?? ($fieldName == 'id' ? 'Integer' : NULL); - // If Sql Function e.g. GROUP_CONCAT or COUNT wants to do its own formatting, apply + // Allow Sql Functions to do special formatting and/or alter the $dataType if (method_exists($fieldExpr, 'formatOutputValue') && is_string($value)) { $result[$key] = $value = $fieldExpr->formatOutputValue($value, $dataType); } - if (!$field) { - continue; - } if (!empty($field['output_formatters'])) { self::applyFormatters($result, $fieldName, $field, $value); $dataType = NULL; diff --git a/ext/search_kit/ang/crmSearchAdmin/compose/criteria.html b/ext/search_kit/ang/crmSearchAdmin/compose/criteria.html index 8aeb1187816c..64b1694b268f 100644 --- a/ext/search_kit/ang/crmSearchAdmin/compose/criteria.html +++ b/ext/search_kit/ang/crmSearchAdmin/compose/criteria.html @@ -35,11 +35,14 @@ crm-ui-select="{placeholder: ts('Group By'), data: fieldsForGroupBy, dropdownCss: {width: '300px'}}" on-crm-ui-select="$ctrl.addParam('groupBy', selection)" > -