@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Provide "builtin" high-level result orders

Summary:
Ref T7803. Currently, available high-level orders are spread across Query and SearchEngine classes and implemented separately for each application.

Lift the concept of "builtin" (high-level, user-facing, named) orders (similar to "builtin" queries in ApplicationSearch) into the root Query class, and let it drive the SearchEngine implementation. This allows you to define a new order in one place and have it automatically work across the entire stack.

This will also let Conduit expose this information in a straightforward way.

Test Plan:
- Used ApplicationSearch in Diffusion.
- Used all result orderings.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7803

Differential Revision: https://secure.phabricator.com/D12379

+184 -81
+1 -1
src/applications/owners/controller/PhabricatorOwnersListController.php
··· 153 153 $callsigns = array('' => pht('(Any Repository)')); 154 154 $repositories = id(new PhabricatorRepositoryQuery()) 155 155 ->setViewer($user) 156 - ->setOrder(PhabricatorRepositoryQuery::ORDER_CALLSIGN) 156 + ->setOrder('callsign') 157 157 ->execute(); 158 158 foreach ($repositories as $repository) { 159 159 $callsigns[$repository->getCallsign()] =
+1 -21
src/applications/ponder/query/PonderQuestionQuery.php
··· 3 3 final class PonderQuestionQuery 4 4 extends PhabricatorCursorPagedPolicyAwareQuery { 5 5 6 - const ORDER_CREATED = 'order-created'; 7 - const ORDER_HOTTEST = 'order-hottest'; 8 - 9 6 private $ids; 10 7 private $phids; 11 8 private $authorPHIDs; 12 9 private $answererPHIDs; 13 - private $order = self::ORDER_CREATED; 14 10 15 11 private $status = 'status-any'; 16 12 const STATUS_ANY = 'status-any'; ··· 55 51 return $this; 56 52 } 57 53 58 - public function setOrder($order) { 59 - $this->order = $order; 60 - return $this; 61 - } 62 - 63 54 private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 64 55 $where = array(); 65 56 ··· 110 101 return $this->formatWhereClause($where); 111 102 } 112 103 113 - private function buildOrderByClause(AphrontDatabaseConnection $conn_r) { 114 - switch ($this->order) { 115 - case self::ORDER_HOTTEST: 116 - return qsprintf($conn_r, 'ORDER BY q.heat DESC, q.id DESC'); 117 - case self::ORDER_CREATED: 118 - return qsprintf($conn_r, 'ORDER BY q.id DESC'); 119 - default: 120 - throw new Exception("Unknown order '{$this->order}'!"); 121 - } 122 - } 123 - 124 104 protected function loadPage() { 125 105 $question = new PonderQuestion(); 126 106 $conn_r = $question->establishConnection('r'); ··· 131 111 $question->getTableName(), 132 112 $this->buildJoinsClause($conn_r), 133 113 $this->buildWhereClause($conn_r), 134 - $this->buildOrderByClause($conn_r), 114 + $this->buildOrderClause($conn_r), 135 115 $this->buildLimitClause($conn_r)); 136 116 137 117 return $question->loadAllFromArray($data);
+19 -27
src/applications/repository/query/PhabricatorRepositoryQuery.php
··· 23 23 const STATUS_ALL = 'status-all'; 24 24 private $status = self::STATUS_ALL; 25 25 26 - const ORDER_CREATED = 'order-created'; 27 - const ORDER_COMMITTED = 'order-committed'; 28 - const ORDER_CALLSIGN = 'order-callsign'; 29 - const ORDER_NAME = 'order-name'; 30 - const ORDER_SIZE = 'order-size'; 31 - 32 26 const HOSTED_PHABRICATOR = 'hosted-phab'; 33 27 const HOSTED_REMOTE = 'hosted-remote'; 34 28 const HOSTED_ALL = 'hosted-all'; ··· 124 118 return $this; 125 119 } 126 120 127 - public function setOrder($order) { 128 - switch ($order) { 129 - case self::ORDER_CREATED: 130 - $this->setOrderVector(array('id')); 131 - break; 132 - case self::ORDER_COMMITTED: 133 - $this->setOrderVector(array('committed', 'id')); 134 - break; 135 - case self::ORDER_CALLSIGN: 136 - $this->setOrderVector(array('callsign')); 137 - break; 138 - case self::ORDER_NAME: 139 - $this->setOrderVector(array('name', 'id')); 140 - break; 141 - case self::ORDER_SIZE: 142 - $this->setOrderVector(array('size', 'id')); 143 - break; 144 - default: 145 - throw new Exception(pht('Unknown order "%s".', $order)); 146 - } 147 - return $this; 121 + public function getBuiltinOrders() { 122 + return array( 123 + 'committed' => array( 124 + 'vector' => array('committed', 'id'), 125 + 'name' => pht('Most Recent Commit'), 126 + ), 127 + 'name' => array( 128 + 'vector' => array('name', 'id'), 129 + 'name' => pht('Name'), 130 + ), 131 + 'callsign' => array( 132 + 'vector' => array('callsign'), 133 + 'name' => pht('Callsign'), 134 + ), 135 + 'size' => array( 136 + 'vector' => array('size', 'id'), 137 + 'name' => pht('Size'), 138 + ), 139 + ) + parent::getBuiltinOrders(); 148 140 } 149 141 150 142 public function getIdentifierMap() {
+6 -31
src/applications/repository/query/PhabricatorRepositorySearchEngine.php
··· 27 27 28 28 public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 29 29 $query = id(new PhabricatorRepositoryQuery()) 30 + ->setDefaultBuiltinOrder() 30 31 ->needProjectPHIDs(true) 31 32 ->needCommitCounts(true) 32 33 ->needMostRecentCommits(true); ··· 43 44 } 44 45 45 46 $order = $saved->getParameter('order'); 46 - $order = idx($this->getOrderValues(), $order); 47 47 if ($order) { 48 48 $query->setOrder($order); 49 - } else { 50 - $query->setOrder(head($this->getOrderValues())); 51 49 } 52 50 53 51 $hosted = $saved->getParameter('hosted'); ··· 125 123 $name, 126 124 isset($types[$key])); 127 125 } 126 + $form->appendChild($type_control); 128 127 129 - $form 130 - ->appendChild($type_control) 131 - ->appendChild( 132 - id(new AphrontFormSelectControl()) 133 - ->setName('order') 134 - ->setLabel(pht('Order')) 135 - ->setValue($saved_query->getParameter('order')) 136 - ->setOptions($this->getOrderOptions())); 128 + $this->appendOrderFieldsToForm( 129 + $form, 130 + $saved_query, 131 + new PhabricatorRepositoryQuery()); 137 132 } 138 133 139 134 protected function getURI($path) { ··· 177 172 '' => PhabricatorRepositoryQuery::STATUS_ALL, 178 173 'open' => PhabricatorRepositoryQuery::STATUS_OPEN, 179 174 'closed' => PhabricatorRepositoryQuery::STATUS_CLOSED, 180 - ); 181 - } 182 - 183 - private function getOrderOptions() { 184 - return array( 185 - 'committed' => pht('Most Recent Commit'), 186 - 'name' => pht('Name'), 187 - 'callsign' => pht('Callsign'), 188 - 'created' => pht('Date Created'), 189 - 'size' => pht('Commit Count'), 190 - ); 191 - } 192 - 193 - private function getOrderValues() { 194 - return array( 195 - 'committed' => PhabricatorRepositoryQuery::ORDER_COMMITTED, 196 - 'name' => PhabricatorRepositoryQuery::ORDER_NAME, 197 - 'callsign' => PhabricatorRepositoryQuery::ORDER_CALLSIGN, 198 - 'created' => PhabricatorRepositoryQuery::ORDER_CREATED, 199 - 'size' => PhabricatorRepositoryQuery::ORDER_SIZE, 200 175 ); 201 176 } 202 177
+19
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 9 9 * @task builtin Builtin Queries 10 10 * @task uri Query URIs 11 11 * @task dates Date Filters 12 + * @task order Result Ordering 12 13 * @task read Reading Utilities 13 14 * @task exec Paging and Executing Queries 14 15 * @task render Rendering Results ··· 576 577 ->setValue($end_str)); 577 578 } 578 579 580 + 581 + /* -( Result Ordering )---------------------------------------------------- */ 582 + 583 + protected function appendOrderFieldsToForm( 584 + AphrontFormView $form, 585 + PhabricatorSavedQuery $saved, 586 + PhabricatorCursorPagedPolicyAwareQuery $query) { 587 + 588 + $orders = $query->getBuiltinOrders(); 589 + $orders = ipull($orders, 'name'); 590 + 591 + $form->appendControl( 592 + id(new AphrontFormSelectControl()) 593 + ->setLabel(pht('Order')) 594 + ->setName('order') 595 + ->setOptions($orders) 596 + ->setValue($saved->getParameter('order'))); 597 + } 579 598 580 599 /* -( Paging and Executing Queries )--------------------------------------- */ 581 600
+138 -1
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
··· 17 17 protected $applicationSearchOrders = array(); 18 18 private $internalPaging; 19 19 private $orderVector; 20 + private $builtinOrder; 20 21 21 22 protected function getPagingValue($result) { 22 23 if (!is_object($result)) { ··· 427 428 428 429 429 430 /** 431 + * Select a result ordering. 432 + * 433 + * This is a high-level method which selects an ordering from a predefined 434 + * list of builtin orders, as provided by @{method:getBuiltinOrders}. These 435 + * options are user-facing and not exhaustive, but are generally convenient 436 + * and meaningful. 437 + * 438 + * You can also use @{method:setOrderVector} to specify a low-level ordering 439 + * across individual orderable columns. This offers greater control but is 440 + * also more involved. 441 + * 442 + * @param string Key of a builtin order supported by this query. 443 + * @return this 444 + * @task order 445 + */ 446 + public function setOrder($order) { 447 + $orders = $this->getBuiltinOrders(); 448 + 449 + if (empty($orders[$order])) { 450 + throw new Exception( 451 + pht( 452 + 'Query "%s" does not support a builtin order "%s". Supported orders '. 453 + 'are: %s.', 454 + get_class($this), 455 + $order, 456 + implode(', ', array_keys($orders)))); 457 + } 458 + 459 + $this->builtinOrder = $order; 460 + $this->orderVector = null; 461 + 462 + return $this; 463 + } 464 + 465 + 466 + /** 467 + * Select the default builtin result ordering. 468 + * 469 + * This sets the result order to the default order among the builtin result 470 + * orders (see @{method:getBuiltinOrders}). This is often the same as the 471 + * query's builtin default order vector, but some objects have different 472 + * default vectors (which are internally-facing) and builtin orders (which 473 + * are user-facing). 474 + * 475 + * For example, repositories sort by ID internally (which is efficient and 476 + * consistent), but sort by most recent commit as a default builtin (which 477 + * better aligns with user expectations). 478 + * 479 + * @return this 480 + */ 481 + public function setDefaultBuiltinOrder() { 482 + return $this->setOrder(head_key($this->getBuiltinOrders())); 483 + } 484 + 485 + 486 + /** 487 + * Get builtin orders for this class. 488 + * 489 + * In application UIs, we want to be able to present users with a small 490 + * selection of meaningful order options (like "Order by Title") rather than 491 + * an exhaustive set of column ordering options. 492 + * 493 + * Meaningful user-facing orders are often really orders across multiple 494 + * columns: for example, a "title" ordering is usually implemented as a 495 + * "title, id" ordering under the hood. 496 + * 497 + * Builtin orders provide a mapping from convenient, understandable 498 + * user-facing orders to implementations. 499 + * 500 + * A builtin order should provide these keys: 501 + * 502 + * - `vector` (`list<string>`): The actual order vector to use. 503 + * - `name` (`string`): Human-readable order name. 504 + * 505 + * @return map<string, wild> Map from builtin order keys to specification. 506 + * @task order 507 + */ 508 + public function getBuiltinOrders() { 509 + $orders = array( 510 + 'newest' => array( 511 + 'vector' => array('id'), 512 + 'name' => pht('Creation (Newest First)'), 513 + 'aliases' => array('created'), 514 + ), 515 + 'oldest' => array( 516 + 'vector' => array('-id'), 517 + 'name' => pht('Creation (Oldest First)'), 518 + ), 519 + ); 520 + 521 + $object = $this->newResultObject(); 522 + if ($object instanceof PhabricatorCustomFieldInterface) { 523 + $list = PhabricatorCustomField::getObjectFields( 524 + $object, 525 + PhabricatorCustomField::ROLE_APPLICATIONSEARCH); 526 + foreach ($list->getFields() as $field) { 527 + $index = $field->buildOrderIndex(); 528 + if (!$index) { 529 + continue; 530 + } 531 + 532 + $key = $field->getFieldKey(); 533 + $digest = $field->getFieldIndex(); 534 + 535 + $full_key = 'custom:'.$key; 536 + $orders[$full_key] = array( 537 + 'vector' => array($full_key, 'id'), 538 + 'name' => $field->getFieldName(), 539 + ); 540 + } 541 + } 542 + 543 + return $orders; 544 + } 545 + 546 + 547 + /** 548 + * Set a low-level column ordering. 549 + * 550 + * This is a low-level method which offers granular control over column 551 + * ordering. In most cases, applications can more easily use 552 + * @{method:setOrder} to choose a high-level builtin order. 553 + * 554 + * To set an order vector, specify a list of order keys as provided by 555 + * @{method:getOrderableColumns}. 556 + * 557 + * @param PhabricatorQueryOrderVector|list<string> List of order keys. 558 + * @return this 430 559 * @task order 431 560 */ 432 561 public function setOrderVector($vector) { ··· 485 614 486 615 487 616 /** 617 + * Get the effective order vector. 618 + * 619 + * @return PhabricatorQueryOrderVector Effective vector. 488 620 * @task order 489 621 */ 490 622 protected function getOrderVector() { 491 623 if (!$this->orderVector) { 492 - $vector = $this->getDefaultOrderVector(); 624 + if ($this->builtinOrder !== null) { 625 + $builtin_order = idx($this->getBuiltinOrders(), $this->builtinOrder); 626 + $vector = $builtin_order['vector']; 627 + } else { 628 + $vector = $this->getDefaultOrderVector(); 629 + } 493 630 $vector = PhabricatorQueryOrderVector::newFromVector($vector); 494 631 495 632 // We call setOrderVector() here to apply checks to the default vector.