[
'max_expr' => 99,
'optional' => FALSE,
+ 'ui_defaults' => [
+ ['type' => 'SqlField', 'placeholder' => ts('If')],
+ ['type' => 'SqlField', 'placeholder' => ts('Else')],
+ ],
],
];
}
'max_expr' => 99,
'optional' => FALSE,
'must_be' => ['SqlField', 'SqlString'],
+ 'ui_defaults' => [
+ ['placeholder' => ts('Plus')],
+ ],
],
];
}
* @return string
*/
public static function getTitle(): string {
- return ts('Combine text');
+ return ts('Combine if');
}
/**
* @return string
*/
public static function getDescription(): string {
- return ts('Multiple values concatenated into a single string.');
+ return ts('Joined text, only if all values are not null.');
}
}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Query;
+
+/**
+ * Sql function
+ */
+class SqlFunctionCONCAT_WS extends SqlFunction {
+
+ protected static $category = self::CATEGORY_STRING;
+
+ protected static $dataType = 'String';
+
+ protected static function params(): array {
+ return [
+ [
+ 'max_expr' => 99,
+ 'optional' => FALSE,
+ 'must_be' => ['SqlField', 'SqlString'],
+ 'ui_defaults' => [
+ ['type' => 'SqlString', 'placeholder' => ts('Separator')],
+ ['type' => 'SqlField', 'placeholder' => ts('Plus')],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public static function getTitle(): string {
+ return ts('Combine text');
+ }
+
+ /**
+ * @return string
+ */
+ public static function getDescription(): string {
+ return ts('Every non-null value joined by a separator.');
+ }
+
+}
'max_expr' => 99,
'min_expr' => 2,
'optional' => FALSE,
+ 'ui_defaults' => [
+ ['type' => 'SqlField', 'placeholder' => ts('If')],
+ ['type' => 'SqlField', 'placeholder' => ts('Else')],
+ ],
],
];
}
'min_expr' => 3,
'max_expr' => 3,
'optional' => FALSE,
+ 'ui_defaults' => [
+ ['type' => 'SqlField', 'placeholder' => ts('If')],
+ ['type' => 'SqlField', 'placeholder' => ts('Then')],
+ ['type' => 'SqlField', 'placeholder' => ts('Else')],
+ ],
],
];
}
'max_expr' => 99,
'min_expr' => 2,
'optional' => FALSE,
+ 'ui_defaults' => [
+ ['type' => 'SqlField', 'placeholder' => ts('If')],
+ ['type' => 'SqlField', 'placeholder' => ts('Else')],
+ ],
],
];
}
'min_expr' => 2,
'max_expr' => 2,
'optional' => FALSE,
+ 'ui_defaults' => [
+ ['type' => 'SqlField', 'placeholder' => ts('Preferred')],
+ ['type' => 'SqlField', 'placeholder' => ts('Alternate')],
+ ],
],
];
}
'max_expr' => 3,
'optional' => FALSE,
'must_be' => ['SqlString', 'SqlField'],
+ 'ui_defaults' => [
+ ['type' => 'SqlField', 'placeholder' => ts('Source')],
+ ['type' => 'SqlString', 'placeholder' => ts('Find')],
+ ['type' => 'SqlString', 'placeholder' => ts('Replace')],
+ ],
],
];
}
'min_expr' => 1,
'max_expr' => 2,
'must_be' => ['SqlNumber', 'SqlField'],
+ 'ui_defaults' => [
+ ['type' => 'SqlField', 'placeholder' => ts('Number')],
+ ['type' => 'SqlNumber', 'placeholder' => ts('Decimals')],
+ ],
],
];
}
var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
ctrl = this;
+ var defaultUiDefaults = {type: 'SqlField', placeholder: ts('Select')};
+
var allTypes = {
aggregate: ts('Aggregate'),
comparison: ts('Comparison'),
this.$onInit = function() {
var info = searchMeta.parseExpr(ctrl.expr);
+ ctrl.fieldArg = _.findWhere(info.args, {type: 'field'});
ctrl.args = info.args;
ctrl.fn = info.fn;
ctrl.fnName = !info.fn ? '' : info.fn.name;
};
this.addArg = function(exprType) {
- exprType = exprType || ctrl.fn.params[0].must_be[0];
+ exprType = exprType || ctrl.getUiDefault(ctrl.args.length).type;
ctrl.args.push({
type: ctrl.exprTypes[exprType].type,
value: exprType === 'SqlNumber' ? 0 : ''
ctrl.modifierName = null;
ctrl.modifier = null;
}
+ // Push args to reach the minimum
while (ctrl.args.length < ctrl.fn.params[0].min_expr) {
ctrl.addArg();
}
}
+ this.getUiDefault = function(index) {
+ if (ctrl.fn.params[0].ui_defaults) {
+ return ctrl.fn.params[0].ui_defaults[index] || _.last(ctrl.fn.params[0].ui_defaults);
+ }
+ defaultUiDefaults.type = ctrl.fn.params[0].must_be[0];
+ return defaultUiDefaults;
+ };
+
// On-demand options for dropdown function selector
this.getFunctions = function() {
var allowedTypes = [], functions = [];
- if (ctrl.expr && ctrl.args[0] && ctrl.args[0].field) {
+ if (ctrl.expr && ctrl.fieldArg) {
if (ctrl.crmSearchAdmin.canAggregate(ctrl.expr)) {
allowedTypes.push('aggregate');
} else {
allowedTypes.push('comparison', 'string');
- if (_.includes(['Integer', 'Float', 'Date', 'Timestamp'], ctrl.args[0].field.data_type)) {
+ if (_.includes(['Integer', 'Float', 'Date', 'Timestamp'], ctrl.fieldArg.field.data_type)) {
allowedTypes.push('math');
}
- if (_.includes(['Date', 'Timestamp'], ctrl.args[0].field.data_type)) {
+ if (_.includes(['Date', 'Timestamp'], ctrl.fieldArg.field.data_type)) {
allowedTypes.push('date');
}
}
this.selectFunction = function() {
ctrl.fn = _.find(CRM.crmSearchAdmin.functions, {name: ctrl.fnName});
- ctrl.args.length = 1;
- initFunction();
+ ctrl.args = [ctrl.fieldArg];
+ if (ctrl.fn) {
+ var exprType, pos = 0,
+ uiDefaults = ctrl.fn.params[0].ui_defaults || [];
+ // Add non-field args to the beginning if needed
+ while (uiDefaults[pos] && uiDefaults[pos].type && uiDefaults[pos].type !== 'SqlField') {
+ exprType = uiDefaults[pos].type;
+ ctrl.args.splice(pos, 0, {
+ type: ctrl.exprTypes[exprType].type,
+ value: exprType === 'SqlNumber' ? 0 : ''
+ });
+ ++pos;
+ }
+ initFunction();
+ }
ctrl.writeExpr();
};
this.changeArg = function(index) {
var val = ctrl.args[index].value;
// Delete empty value
- if (!val && ctrl.args.length > ctrl.fn.params[0].min_expr) {
+ if (index && !val && ctrl.args.length > ctrl.fn.params[0].min_expr) {
ctrl.args.splice(index, 1);
}
ctrl.writeExpr();
<div class="form-inline">
<input class="form-control" style="width: 15em;" ng-model="$ctrl.fnName" crm-ui-select="{data: $ctrl.getFunctions, placeholder: ts('Function')}" ng-change="$ctrl.selectFunction()">
- <label>{{ $ctrl.args[0].field.label }}</label>
+ <label>{{ $ctrl.fieldArg.field.label }}</label>
<label ng-if="$ctrl.modifierName">
<input type="checkbox" ng-checked="!!$ctrl.modifier" ng-click="$ctrl.toggleModifier()">
{{ $ctrl.modifierLabel }}
</label>
- <div class="form-group" ng-repeat="arg in $ctrl.args" ng-if="$index">
+ <div class="form-group" ng-repeat="arg in $ctrl.args" ng-if="arg !== $ctrl.fieldArg">
<span ng-switch="arg.type">
- <input ng-switch-when="number" class="form-control" type="number" ng-model="arg.value" ng-change="$ctrl.changeArg($index)" ng-model-options="{updateOn: 'blur'}">
- <input ng-switch-when="string" class="form-control" ng-model="arg.value" ng-change="$ctrl.changeArg($index)" ng-model-options="{updateOn: 'blur'}">
- <input ng-switch-default class="form-control" ng-model="arg.value" crm-ui-select="{data: $ctrl.getFields, placeholder: ts('Field')}" ng-change="$ctrl.changeArg($index)">
+ <input ng-switch-when="number" class="form-control" type="number" ng-model="arg.value" placeholder="{{ $ctrl.getUiDefault($index).placeholder }}" ng-change="$ctrl.changeArg($index)" ng-model-options="{updateOn: 'blur'}">
+ <input ng-switch-when="string" class="form-control" ng-model="arg.value" placeholder="{{ $ctrl.getUiDefault($index).placeholder }}" ng-change="$ctrl.changeArg($index)" ng-trim="false" ng-model-options="{updateOn: 'blur'}">
+ <input ng-switch-default class="form-control" ng-model="arg.value" crm-ui-select="{data: $ctrl.getFields, placeholder: $ctrl.getUiDefault($index).placeholder}" ng-change="$ctrl.changeArg($index)">
</span>
</div>
<div class="btn-group" ng-if="$ctrl.args.length < $ctrl.fn.params[0].max_expr">
$this->assertEquals(FALSE, $result[$aids[2]]['duration_isnull']);
}
+ public function testStringFunctions() {
+ $sampleData = [
+ ['first_name' => 'abc', 'middle_name' => 'q', 'last_name' => 'tester1', 'source' => '123'],
+ ];
+ $cid = Contact::save(FALSE)
+ ->setRecords($sampleData)
+ ->execute()->first()['id'];
+
+ $result = Contact::get(FALSE)
+ ->addWhere('id', '=', $cid)
+ ->addSelect('CONCAT_WS("|", first_name, middle_name, last_name) AS concat_ws')
+ ->execute()->first();
+
+ $this->assertEquals('abc|q|tester1', $result['concat_ws']);
+ }
+
public function testIncorrectNumberOfArguments() {
try {
Activity::get(FALSE)