Commit | Line | Data |
---|---|---|
f0acec37 CW |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
4 | | Copyright CiviCRM LLC. All rights reserved. | | |
5 | | | | |
6 | | This work is published under the GNU AGPLv3 license with some | | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
9 | +--------------------------------------------------------------------+ | |
10 | */ | |
11 | ||
12 | namespace Civi\Api4\Query; | |
13 | ||
14 | /** | |
15 | * Base class for all Sql functions. | |
16 | * | |
17 | * @package Civi\Api4\Query | |
18 | */ | |
19 | abstract class SqlFunction extends SqlExpression { | |
20 | ||
7ce7b1cd CW |
21 | /** |
22 | * @var array[] | |
23 | */ | |
f0acec37 CW |
24 | protected $args = []; |
25 | ||
e7f6def6 CW |
26 | /** |
27 | * Used for categorizing functions in the UI | |
28 | * | |
29 | * @var string | |
30 | */ | |
31 | protected static $category; | |
32 | ||
33 | const CATEGORY_AGGREGATE = 'aggregate', | |
34 | CATEGORY_COMPARISON = 'comparison', | |
35 | CATEGORY_DATE = 'date', | |
36 | CATEGORY_MATH = 'math', | |
37 | CATEGORY_STRING = 'string'; | |
38 | ||
f0acec37 CW |
39 | /** |
40 | * Parse the argument string into an array of function arguments | |
41 | */ | |
42 | protected function initialize() { | |
43 | $arg = trim(substr($this->expr, strpos($this->expr, '(') + 1, -1)); | |
7ce7b1cd | 44 | foreach ($this->getParams() as $idx => $param) { |
1b6a82ee | 45 | $prefix = NULL; |
0dc0821c CW |
46 | if ($param['name']) { |
47 | $prefix = $this->captureKeyword([$param['name']], $arg); | |
1b6a82ee CW |
48 | // Supply api_default |
49 | if (!$prefix && isset($param['api_default'])) { | |
50 | $this->args[$idx] = [ | |
0dc0821c | 51 | 'prefix' => [$param['name']], |
1b6a82ee | 52 | 'expr' => array_map([parent::class, 'convert'], $param['api_default']['expr']), |
0dc0821c | 53 | 'suffix' => [], |
1b6a82ee CW |
54 | ]; |
55 | continue; | |
56 | } | |
57 | if (!$prefix && !$param['optional']) { | |
0dc0821c | 58 | throw new \API_Exception("Missing {$param['name']} for SQL function " . static::getName()); |
1b6a82ee CW |
59 | } |
60 | } | |
61 | elseif ($param['flag_before']) { | |
62 | $prefix = $this->captureKeyword(array_keys($param['flag_before']), $arg); | |
63 | } | |
7ce7b1cd | 64 | $this->args[$idx] = [ |
1b6a82ee | 65 | 'prefix' => (array) $prefix, |
7ce7b1cd | 66 | 'expr' => [], |
1b6a82ee | 67 | 'suffix' => [], |
7ce7b1cd | 68 | ]; |
0dc0821c | 69 | if ($param['max_expr'] && (!$param['name'] || $param['name'] === $prefix)) { |
f4138bc4 | 70 | $exprs = $this->captureExpressions($arg, $param['must_be'], TRUE); |
fa7465e4 CW |
71 | if (count($exprs) < $param['min_expr'] || count($exprs) > $param['max_expr']) { |
72 | throw new \API_Exception('Incorrect number of arguments for SQL function ' . static::getName()); | |
73 | } | |
74 | $this->args[$idx]['expr'] = $exprs; | |
1b6a82ee CW |
75 | |
76 | $this->args[$idx]['suffix'] = (array) $this->captureKeyword(array_keys($param['flag_after']), $arg); | |
f0acec37 CW |
77 | } |
78 | } | |
79 | } | |
80 | ||
b0aa3463 CW |
81 | /** |
82 | * Change $dataType according to output of function | |
83 | * | |
84 | * @see \Civi\Api4\Utils\FormattingUtil::formatOutputValues | |
85 | * @param string $value | |
86 | * @param string $dataType | |
87 | * @return string | |
88 | */ | |
89 | public function formatOutputValue($value, &$dataType) { | |
90 | if (static::$dataType) { | |
91 | $dataType = static::$dataType; | |
92 | } | |
93 | return $value; | |
94 | } | |
95 | ||
7ce7b1cd CW |
96 | /** |
97 | * Render the expression for insertion into the sql query | |
98 | * | |
99 | * @param array $fieldList | |
100 | * @return string | |
101 | */ | |
f0acec37 | 102 | public function render(array $fieldList): string { |
7ce7b1cd | 103 | $output = ''; |
0dc0821c CW |
104 | foreach ($this->args as $arg) { |
105 | $rendered = $this->renderArg($arg, $fieldList); | |
7ce7b1cd CW |
106 | if (strlen($rendered)) { |
107 | $output .= (strlen($output) ? ' ' : '') . $rendered; | |
f0acec37 | 108 | } |
7ce7b1cd CW |
109 | } |
110 | return $this->getName() . '(' . $output . ')'; | |
111 | } | |
112 | ||
113 | /** | |
114 | * @param array $arg | |
7ce7b1cd CW |
115 | * @param array $fieldList |
116 | * @return string | |
117 | */ | |
0dc0821c | 118 | private function renderArg($arg, $fieldList): string { |
1b6a82ee | 119 | $rendered = implode(' ', $arg['prefix']); |
7ce7b1cd CW |
120 | foreach ($arg['expr'] ?? [] as $idx => $expr) { |
121 | if (strlen($rendered) || $idx) { | |
122 | $rendered .= $idx ? ', ' : ' '; | |
f0acec37 | 123 | } |
7ce7b1cd CW |
124 | $rendered .= $expr->render($fieldList); |
125 | } | |
1b6a82ee CW |
126 | if ($arg['suffix']) { |
127 | $rendered .= (strlen($rendered) ? ' ' : '') . implode(' ', $arg['suffix']); | |
f0acec37 | 128 | } |
7ce7b1cd | 129 | return $rendered; |
f0acec37 CW |
130 | } |
131 | ||
132 | /** | |
133 | * @inheritDoc | |
134 | */ | |
135 | public function getAlias(): string { | |
136 | return $this->alias ?? $this->getName() . ':' . implode('_', $this->fields); | |
137 | } | |
138 | ||
139 | /** | |
140 | * Get the name of this sql function. | |
141 | * @return string | |
142 | */ | |
143 | public static function getName(): string { | |
144 | $className = static::class; | |
145 | return substr($className, strrpos($className, 'SqlFunction') + 11); | |
146 | } | |
147 | ||
148 | /** | |
149 | * Get the param metadata for this sql function. | |
150 | * @return array | |
151 | */ | |
f19a0f00 | 152 | final public static function getParams(): array { |
f0acec37 | 153 | $params = []; |
f19a0f00 | 154 | foreach (static::params() as $param) { |
f0acec37 CW |
155 | // Merge in defaults to ensure each param has these properties |
156 | $params[] = $param + [ | |
0dc0821c | 157 | 'name' => NULL, |
fa7465e4 CW |
158 | 'min_expr' => 1, |
159 | 'max_expr' => 1, | |
1b6a82ee CW |
160 | 'flag_before' => [], |
161 | 'flag_after' => [], | |
f0acec37 | 162 | 'optional' => FALSE, |
173405e2 | 163 | 'must_be' => ['SqlField', 'SqlFunction', 'SqlString', 'SqlNumber', 'SqlNull'], |
7ce7b1cd | 164 | 'api_default' => NULL, |
f0acec37 CW |
165 | ]; |
166 | } | |
167 | return $params; | |
168 | } | |
169 | ||
f19a0f00 CW |
170 | abstract protected static function params(): array; |
171 | ||
7ce7b1cd CW |
172 | /** |
173 | * Get the arguments passed to this sql function instance. | |
174 | * @return array[] | |
175 | */ | |
176 | public function getArgs(): array { | |
177 | return $this->args; | |
178 | } | |
179 | ||
e7f6def6 CW |
180 | /** |
181 | * @return string | |
182 | */ | |
183 | public static function getCategory(): string { | |
184 | return static::$category; | |
185 | } | |
186 | ||
9cae8a07 CW |
187 | /** |
188 | * @return string | |
189 | */ | |
190 | abstract public static function getTitle(): string; | |
191 | ||
1fe4682d CW |
192 | /** |
193 | * @return string | |
194 | */ | |
195 | abstract public static function getDescription(): string; | |
196 | ||
f0acec37 | 197 | } |