];
if ($param['max_expr'] && (!$param['name'] || $param['name'] === $prefix)) {
$exprs = $this->captureExpressions($arg, $param['must_be'], TRUE);
- if (count($exprs) < $param['min_expr'] || count($exprs) > $param['max_expr']) {
+ if (
+ (count($exprs) < $param['min_expr'] || count($exprs) > $param['max_expr']) &&
+ !(!$exprs && $param['optional'])
+ ) {
throw new \API_Exception('Incorrect number of arguments for SQL function ' . static::getName());
}
$this->args[$idx]['expr'] = $exprs;
// Trait provides base methods and properties common to all search display types
angular.module('crmSearchDisplay').factory('searchDisplayBaseTrait', function(crmApi4) {
var ts = CRM.ts('org.civicrm.search_kit'),
- runCount = 0;
+ runCount = 0,
+ seed = Date.now();
// Replace tokens keyed to rowData.
// Pass view=true to replace with view value, otherwise raw value is used.
var ctrl = this;
this.limit = this.settings.limit;
this.sort = this.settings.sort ? _.cloneDeep(this.settings.sort) : [];
+ this.seed = Date.now();
this.getResults = _.debounce(function() {
$scope.$apply(function() {
display: this.display,
sort: this.sort,
limit: this.limit,
+ seed: this.seed,
filters: _.assign({}, (this.afFieldset ? this.afFieldset.getFieldData() : {}), this.filters),
afform: this.afFieldset ? this.afFieldset.getFormName() : null
};
$this->assertStringContainsString('failed', $error);
}
+ /**
+ * Test running a searchDisplay with random sorting.
+ */
+ public function testSortByRand() {
+ $lastName = uniqid(__FUNCTION__);
+ $sampleData = [
+ ['first_name' => 'One', 'last_name' => $lastName],
+ ['first_name' => 'Two', 'last_name' => $lastName],
+ ['first_name' => 'Three', 'last_name' => $lastName],
+ ['first_name' => 'Four', 'last_name' => $lastName],
+ ];
+ Contact::save(FALSE)->setRecords($sampleData)->execute();
+
+ $params = [
+ 'checkPermissions' => FALSE,
+ 'return' => 'page:1',
+ 'savedSearch' => [
+ 'api_entity' => 'Contact',
+ 'api_params' => [
+ 'version' => 4,
+ 'select' => ['id', 'first_name', 'last_name'],
+ 'where' => [['last_name', '=', $lastName]],
+ ],
+ ],
+ 'display' => [
+ 'type' => 'list',
+ 'label' => '',
+ 'settings' => [
+ 'limit' => 20,
+ 'pager' => TRUE,
+ 'columns' => [
+ [
+ 'key' => 'first_name',
+ 'label' => 'First Name',
+ 'dataType' => 'String',
+ 'type' => 'field',
+ ],
+ ],
+ 'sort' => [
+ ['RAND()', 'ASC'],
+ ],
+ ],
+ ],
+ 'afform' => NULL,
+ ];
+
+ // Without seed, results are returned in unpredictable order
+ // (hard to test this, but we can at least assert we get the correct number of results back)
+ $unseeded = civicrm_api4('SearchDisplay', 'run', $params);
+ $this->assertCount(4, $unseeded);
+
+ // Seed must be an integer
+ $params['seed'] = 'hello';
+ try {
+ civicrm_api4('SearchDisplay', 'run', $params);
+ $this->fail();
+ }
+ catch (\API_Exception $e) {
+ }
+
+ // With a random seed, results should be shuffled in stable order
+ $params['seed'] = 12345678987654321;
+ $seeded = civicrm_api4('SearchDisplay', 'run', $params);
+
+ // Same seed, same order every time
+ for ($i = 0; $i <= 9; ++$i) {
+ $repeat = civicrm_api4('SearchDisplay', 'run', $params);
+ $this->assertEquals($seeded->column('id'), $repeat->column('id'));
+ }
+ }
+
}