if ($dir !== 'ASC' && $dir !== 'DESC') {
throw new \API_Exception("Invalid sort direction. Cannot order by $item $dir");
}
- $this->query->orderBy($this->renderExpression($item) . " $dir");
+ $expr = $this->getExpression($item);
+ $column = $expr->render($this->apiFieldSpec);
+
+ // Use FIELD() function to sort on pseudoconstant values
+ $suffix = strstr($item, ':');
+ if ($suffix && $expr->getType() === 'SqlField') {
+ $field = $this->getField($item);
+ $options = FormattingUtil::getPseudoconstantList($field['entity'], $field['name'], substr($suffix, 1));
+ if ($options) {
+ asort($options);
+ $column = "FIELD($column,'" . implode("','", array_keys($options)) . "')";
+ }
+ }
+ $this->query->orderBy("$column $dir");
}
}
*/
protected function buildGroupBy() {
foreach ($this->groupBy as $item) {
- $this->query->groupBy($this->renderExpression($item));
+ $this->query->groupBy($this->getExpression($item)->render($this->apiFieldSpec));
}
}
return $sqlExpr;
}
- /**
- * @param string $expr
- * @return string
- * @throws \API_Exception
- */
- protected function renderExpression(string $expr) {
- $sqlExpr = $this->getExpression($expr);
- return $sqlExpr->render($this->apiFieldSpec);
- }
-
/**
* @inheritDoc
*/
</div>
</div>
<div class="api4-input form-inline">
- <input class="collapsible-optgroups form-control huge" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoinsAndFunctions}" placeholder="Add orderBy" />
+ <input class="collapsible-optgroups form-control huge" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoinsAndFunctionsWithSuffixes}" placeholder="Add orderBy" />
</div>
</fieldset>
<fieldset ng-if="::availableParams.limit && availableParams.offset">
$scope.havingOptions = [];
$scope.fieldsAndJoins = [];
$scope.fieldsAndJoinsAndFunctions = [];
+ $scope.fieldsAndJoinsAndFunctionsWithSuffixes = [];
$scope.fieldsAndJoinsAndFunctionsAndWildcards = [];
$scope.availableParams = {};
$scope.params = {};
$scope.action = $routeParams.api4action;
$scope.fieldsAndJoins.length = 0;
$scope.fieldsAndJoinsAndFunctions.length = 0;
+ $scope.fieldsAndJoinsAndFunctionsWithSuffixes.length = 0;
$scope.fieldsAndJoinsAndFunctionsAndWildcards.length = 0;
if (!actions.length) {
formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params']);
});
}
$scope.fieldsAndJoinsAndFunctions = addJoins($scope.fields.concat(functions), true);
+ $scope.fieldsAndJoinsAndFunctionsWithSuffixes = addJoins(getFieldList($scope.action, ['name', 'label']).concat(functions), false, ['name', 'label']);
$scope.fieldsAndJoinsAndFunctionsAndWildcards = addJoins(getFieldList($scope.action, ['name', 'label']).concat(functions), true, ['name', 'label']);
} else {
$scope.fieldsAndJoins = getFieldList($scope.action, ['name']);
$scope.fieldsAndJoinsAndFunctions = $scope.fields;
+ $scope.fieldsAndJoinsAndFunctionsWithSuffixes = getFieldList($scope.action, ['name', 'label']);
$scope.fieldsAndJoinsAndFunctionsAndWildcards = getFieldList($scope.action, ['name', 'label']);
}
$scope.fieldsAndJoinsAndFunctionsAndWildcards.unshift({id: '*', text: '*', 'description': 'All core ' + $scope.entity + ' fields'});
$results = MockBasicEntity::get()
->addSelect('*', 'group:label', 'group:name', 'fruit:name', 'fruit:color', 'fruit:label')
+ ->addOrderBy('fruit:color', "DESC")
->execute();
$this->assertEquals('round', $results[0]['shape']);
$this->assertEquals('banana', $results[0]['fruit:name']);
$this->assertEquals('yellow', $results[0]['fruit:color']);
+ // Reverse order
+ $results = MockBasicEntity::get()
+ ->addOrderBy('fruit:color')
+ ->execute();
+ $this->assertEquals('two', $results[0]['group']);
+
// Cannot match to a non-unique option property like :color on create
try {
MockBasicEntity::create()->addValue('fruit:color', 'yellow')->execute();
$this->assertEquals('blü', $result['myPseudoconstantTest.Color:label']);
$this->assertEquals('bl_', $result['myPseudoconstantTest.Color:name']);
$this->assertEquals('b', $result['myPseudoconstantTest.Color']);
+
+ $cid1 = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'two')
+ ->addValue('myPseudoconstantTest.Technicolor:label', 'RED')
+ ->execute()->first()['id'];
+ $cid2 = Contact::create()
+ ->setCheckPermissions(FALSE)
+ ->addValue('first_name', 'two')
+ ->addValue('myPseudoconstantTest.Technicolor:label', 'GREEN')
+ ->execute()->first()['id'];
+
+ // Test ordering by label
+ $result = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', 'IN', [$cid1, $cid2])
+ ->addSelect('id')
+ ->addOrderBy('myPseudoconstantTest.Technicolor:label')
+ ->execute()->first()['id'];
+ $this->assertEquals($cid2, $result);
+ $result = Contact::get()
+ ->setCheckPermissions(FALSE)
+ ->addWhere('id', 'IN', [$cid1, $cid2])
+ ->addSelect('id')
+ ->addOrderBy('myPseudoconstantTest.Technicolor:label', 'DESC')
+ ->execute()->first()['id'];
+ $this->assertEquals($cid1, $result);
}
public function testJoinOptions() {