Skip to content
Snippets Groups Projects
Unverified Commit bc040f2c authored by Eileen McNaughton's avatar Eileen McNaughton Committed by GitHub
Browse files

Merge pull request #17482 from colemanw/orderBySuffix

APIv4 - Support pseudoconstant suffixes in orderBy clause
parents 71f55384 a4499ec5
Branches
Tags
No related merge requests found
......@@ -222,7 +222,20 @@ class Api4SelectQuery extends SelectQuery {
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");
}
}
......@@ -241,7 +254,7 @@ class Api4SelectQuery extends SelectQuery {
*/
protected function buildGroupBy() {
foreach ($this->groupBy as $item) {
$this->query->groupBy($this->renderExpression($item));
$this->query->groupBy($this->getExpression($item)->render($this->apiFieldSpec));
}
}
......@@ -374,16 +387,6 @@ class Api4SelectQuery extends SelectQuery {
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
*/
......
......@@ -138,7 +138,7 @@
</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">
......
......@@ -28,6 +28,7 @@
$scope.havingOptions = [];
$scope.fieldsAndJoins = [];
$scope.fieldsAndJoinsAndFunctions = [];
$scope.fieldsAndJoinsAndFunctionsWithSuffixes = [];
$scope.fieldsAndJoinsAndFunctionsAndWildcards = [];
$scope.availableParams = {};
$scope.params = {};
......@@ -357,6 +358,7 @@
$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']);
......@@ -382,10 +384,12 @@
});
}
$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'});
......
......@@ -266,6 +266,7 @@ class BasicActionsTest extends UnitTestCase {
$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']);
......@@ -277,6 +278,12 @@ class BasicActionsTest extends UnitTestCase {
$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();
......
......@@ -196,6 +196,33 @@ class PseudoconstantTest extends BaseCustomValueTest {
$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() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment