mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-18 21:02:41 +01:00
Support ordering in SearchField
Summary: Ref T8441. Ref T7715. Automatically generate a modern "Order" control in ApplicationSearch for engines which fully support SearchField. Notably, this allows the standard "Order" control to automatically support custom field orders. We do this in Maniphest today, but in an ad-hoc way. Test Plan: Performed order-by queries in Almanac (Services), Pholio, Files, People, Projects, and Paste. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T7715, T8441 Differential Revision: https://secure.phabricator.com/D13193
This commit is contained in:
parent
3cdaf52ce9
commit
bf87976d25
17 changed files with 229 additions and 154 deletions
|
@ -2520,6 +2520,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php',
|
||||
'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php',
|
||||
'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php',
|
||||
'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php',
|
||||
'PhabricatorSearchOwnersField' => 'applications/search/field/PhabricatorSearchOwnersField.php',
|
||||
'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php',
|
||||
'PhabricatorSearchProjectsField' => 'applications/search/field/PhabricatorSearchProjectsField.php',
|
||||
|
@ -6024,6 +6025,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow',
|
||||
'PhabricatorSearchManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController',
|
||||
'PhabricatorSearchOrderField' => 'PhabricatorSearchField',
|
||||
'PhabricatorSearchOwnersField' => 'PhabricatorSearchTokenizerField',
|
||||
'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorSearchProjectsField' => 'PhabricatorSearchTokenizerField',
|
||||
|
|
|
@ -60,47 +60,35 @@ final class AlmanacServiceQuery
|
|||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new AlmanacService();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT service.* FROM %T service %Q %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildJoinClause($conn_r),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($data);
|
||||
return $this->loadStandardPage(new AlmanacService());
|
||||
}
|
||||
|
||||
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
|
||||
$joins = array();
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$joins = parent::buildJoinClauseParts($conn);
|
||||
|
||||
if ($this->devicePHIDs !== null) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN %T binding ON service.phid = binding.servicePHID',
|
||||
id(new AlmanacBinding())->getTableName());
|
||||
}
|
||||
|
||||
return implode(' ', $joins);
|
||||
return $joins;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'service.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'service.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
@ -112,49 +100,47 @@ final class AlmanacServiceQuery
|
|||
}
|
||||
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'service.nameIndex IN (%Ls)',
|
||||
$hashes);
|
||||
}
|
||||
|
||||
if ($this->serviceClasses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'service.serviceClass IN (%Ls)',
|
||||
$this->serviceClasses);
|
||||
}
|
||||
|
||||
if ($this->devicePHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'binding.devicePHID IN (%Ls)',
|
||||
$this->devicePHIDs);
|
||||
}
|
||||
|
||||
if ($this->locked !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'service.isLocked = %d',
|
||||
(int)$this->locked);
|
||||
}
|
||||
|
||||
if ($this->namePrefix !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'service.name LIKE %>',
|
||||
$this->namePrefix);
|
||||
}
|
||||
|
||||
if ($this->nameSuffix !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'service.name LIKE %<',
|
||||
$this->nameSuffix);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $services) {
|
||||
|
@ -192,10 +178,14 @@ final class AlmanacServiceQuery
|
|||
return parent::didFilterPage($services);
|
||||
}
|
||||
|
||||
public function getPrimaryTableAlias() {
|
||||
return 'service';
|
||||
}
|
||||
|
||||
public function getOrderableColumns() {
|
||||
return parent::getOrderableColumns() + array(
|
||||
'name' => array(
|
||||
'table' => 'service',
|
||||
'table' => $this->getPrimaryTableAlias(),
|
||||
'column' => 'name',
|
||||
'type' => 'string',
|
||||
'unique' => true,
|
||||
|
|
|
@ -11,30 +11,26 @@ final class AlmanacServiceSearchEngine
|
|||
return 'PhabricatorAlmanacApplication';
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
|
||||
$this->saveQueryOrder($saved, $request);
|
||||
|
||||
return $saved;
|
||||
public function newQuery() {
|
||||
return new AlmanacServiceQuery();
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$query = id(new AlmanacServiceQuery());
|
||||
public function newResultObject() {
|
||||
// NOTE: We need to attach a service type in order to generate custom
|
||||
// field definitions.
|
||||
return AlmanacService::initializeNewService()
|
||||
->attachServiceType(new AlmanacCustomServiceType());
|
||||
}
|
||||
|
||||
$this->setQueryOrder($query, $saved);
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved) {
|
||||
|
||||
$this->appendOrderFieldsToForm(
|
||||
$form,
|
||||
$saved,
|
||||
new AlmanacServiceQuery());
|
||||
protected function buildCustomSearchFields() {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
|
@ -62,12 +58,6 @@ final class AlmanacServiceSearchEngine
|
|||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
protected function getRequiredHandlePHIDsForResultList(
|
||||
array $services,
|
||||
PhabricatorSavedQuery $query) {
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function renderResultList(
|
||||
array $services,
|
||||
PhabricatorSavedQuery $query,
|
||||
|
|
|
@ -117,6 +117,10 @@ final class PhabricatorFileQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorFile();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$files = $this->loadStandardPage(new PhabricatorFile());
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ final class PhabricatorFileSearchEngine
|
|||
return 'PhabricatorFilesApplication';
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorFile();
|
||||
public function newQuery() {
|
||||
return new PhabricatorFileQuery();
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
|
@ -46,7 +46,7 @@ final class PhabricatorFileSearchEngine
|
|||
}
|
||||
|
||||
public function buildQueryFromParameters(array $map) {
|
||||
$query = id(new PhabricatorFileQuery());
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['authorPHIDs']) {
|
||||
$query->withAuthorPHIDs($map['authorPHIDs']);
|
||||
|
|
|
@ -193,7 +193,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function newResultObject() {
|
||||
public function newResultObject() {
|
||||
return new ManiphestTask();
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ final class PhabricatorPasteQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function newResultObject() {
|
||||
public function newResultObject() {
|
||||
return new PhabricatorPaste();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,13 @@ final class PhabricatorPasteSearchEngine
|
|||
return 'PhabricatorPasteApplication';
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorPaste();
|
||||
public function newQuery() {
|
||||
return id(new PhabricatorPasteQuery())
|
||||
->needContent(true);
|
||||
}
|
||||
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = id(new PhabricatorPasteQuery())
|
||||
->needContent(true);
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['authorPHIDs']) {
|
||||
$query->withAuthorPHIDs($map['authorPHIDs']);
|
||||
|
|
|
@ -113,19 +113,13 @@ final class PhabricatorPeopleQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new PhabricatorUser();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
public function newResultObject() {
|
||||
return new PhabricatorUser();
|
||||
}
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T user %Q %Q %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildJoinsClause($conn_r),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildGroupClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
protected function loadPage() {
|
||||
$table = new PhabricatorUser();
|
||||
$data = $this->loadStandardPageRows($table);
|
||||
|
||||
if ($this->needPrimaryEmail) {
|
||||
$table->putInSet(new LiskDAOSet());
|
||||
|
@ -225,23 +219,21 @@ final class PhabricatorPeopleQuery
|
|||
return $users;
|
||||
}
|
||||
|
||||
protected function buildGroupClause(AphrontDatabaseConnection $conn) {
|
||||
protected function shouldGroupQueryResultRows() {
|
||||
if ($this->nameTokens) {
|
||||
return qsprintf(
|
||||
$conn,
|
||||
'GROUP BY user.id');
|
||||
} else {
|
||||
return $this->buildApplicationSearchGroupClause($conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::shouldGroupQueryResultRows();
|
||||
}
|
||||
|
||||
private function buildJoinsClause($conn_r) {
|
||||
$joins = array();
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$joins = parent::buildJoinClauseParts($conn);
|
||||
|
||||
if ($this->emails) {
|
||||
$email_table = new PhabricatorUserEmail();
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN %T email ON email.userPHID = user.PHID',
|
||||
$email_table->getTableName());
|
||||
}
|
||||
|
@ -250,7 +242,7 @@ final class PhabricatorPeopleQuery
|
|||
foreach ($this->nameTokens as $key => $token) {
|
||||
$token_table = 'token_'.$key;
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>',
|
||||
PhabricatorUser::NAMETOKEN_TABLE,
|
||||
$token_table,
|
||||
|
@ -260,110 +252,105 @@ final class PhabricatorPeopleQuery
|
|||
}
|
||||
}
|
||||
|
||||
$joins[] = $this->buildApplicationSearchJoinClause($conn_r);
|
||||
|
||||
$joins = implode(' ', $joins);
|
||||
return $joins;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->usernames !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.userName IN (%Ls)',
|
||||
$this->usernames);
|
||||
}
|
||||
|
||||
if ($this->emails !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'email.address IN (%Ls)',
|
||||
$this->emails);
|
||||
}
|
||||
|
||||
if ($this->realnames !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.realName IN (%Ls)',
|
||||
$this->realnames);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->dateCreatedAfter) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.dateCreated >= %d',
|
||||
$this->dateCreatedAfter);
|
||||
}
|
||||
|
||||
if ($this->dateCreatedBefore) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.dateCreated <= %d',
|
||||
$this->dateCreatedBefore);
|
||||
}
|
||||
|
||||
if ($this->isAdmin !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.isAdmin = %d',
|
||||
(int)$this->isAdmin);
|
||||
}
|
||||
|
||||
if ($this->isDisabled !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.isDisabled = %d',
|
||||
(int)$this->isDisabled);
|
||||
}
|
||||
|
||||
if ($this->isApproved !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.isApproved = %d',
|
||||
(int)$this->isApproved);
|
||||
}
|
||||
|
||||
if ($this->isSystemAgent !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.isSystemAgent = %d',
|
||||
(int)$this->isSystemAgent);
|
||||
}
|
||||
|
||||
if ($this->isMailingList !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.isMailingList = %d',
|
||||
(int)$this->isMailingList);
|
||||
}
|
||||
|
||||
if (strlen($this->nameLike)) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'user.username LIKE %~ OR user.realname LIKE %~',
|
||||
$this->nameLike,
|
||||
$this->nameLike);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
|
|
|
@ -11,8 +11,10 @@ final class PhabricatorPeopleSearchEngine
|
|||
return 'PhabricatorPeopleApplication';
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorUser();
|
||||
public function newQuery() {
|
||||
return id(new PhabricatorPeopleQuery())
|
||||
->needPrimaryEmail(true)
|
||||
->needProfileImage(true);
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
|
@ -77,9 +79,7 @@ final class PhabricatorPeopleSearchEngine
|
|||
}
|
||||
|
||||
public function buildQueryFromParameters(array $map) {
|
||||
$query = id(new PhabricatorPeopleQuery())
|
||||
->needPrimaryEmail(true)
|
||||
->needProfileImage(true);
|
||||
$query = $this->newQuery();
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
|
|
|
@ -53,6 +53,10 @@ final class PholioMockQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PholioMock();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$mocks = $this->loadStandardPage(new PholioMock());
|
||||
|
||||
|
|
|
@ -10,11 +10,14 @@ final class PholioMockSearchEngine extends PhabricatorApplicationSearchEngine {
|
|||
return 'PhabricatorPholioApplication';
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PholioMock();
|
||||
public function newQuery() {
|
||||
return id(new PholioMockQuery())
|
||||
->needCoverFiles(true)
|
||||
->needImages(true)
|
||||
->needTokenCounts(true);
|
||||
}
|
||||
|
||||
public function buildCustomSearchFields() {
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchUsersField())
|
||||
->setKey('authorPHIDs')
|
||||
|
@ -30,10 +33,7 @@ final class PholioMockSearchEngine extends PhabricatorApplicationSearchEngine {
|
|||
}
|
||||
|
||||
public function buildQueryFromParameters(array $map) {
|
||||
$query = id(new PholioMockQuery())
|
||||
->needCoverFiles(true)
|
||||
->needImages(true)
|
||||
->needTokenCounts(true);
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['authorPHIDs']) {
|
||||
$query->withAuthorPHIDs($map['authorPHIDs']);
|
||||
|
|
|
@ -95,12 +95,25 @@ final class PhabricatorProjectQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorProject();
|
||||
}
|
||||
|
||||
protected function getDefaultOrderVector() {
|
||||
return array('name');
|
||||
}
|
||||
|
||||
public function getOrderableColumns() {
|
||||
public function getBuiltinOrders() {
|
||||
return array(
|
||||
'name' => array(
|
||||
'vector' => array('name'),
|
||||
'name' => pht('Name'),
|
||||
),
|
||||
) + parent::getBuiltinOrders();
|
||||
}
|
||||
|
||||
public function getOrderableColumns() {
|
||||
return parent::getOrderableColumns() + array(
|
||||
'name' => array(
|
||||
'table' => $this->getPrimaryTableAlias(),
|
||||
'column' => 'name',
|
||||
|
|
|
@ -11,8 +11,9 @@ final class PhabricatorProjectSearchEngine
|
|||
return 'PhabricatorProjectApplication';
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorProject();
|
||||
public function newQuery() {
|
||||
return id(new PhabricatorProjectQuery())
|
||||
->needImages(true);
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
|
@ -41,8 +42,7 @@ final class PhabricatorProjectSearchEngine
|
|||
|
||||
|
||||
public function buildQueryFromParameters(array $map) {
|
||||
$query = id(new PhabricatorProjectQuery())
|
||||
->needImages(true);
|
||||
$query = $this->newQuery();
|
||||
|
||||
if (strlen($map['name'])) {
|
||||
$tokens = PhabricatorTypeaheadDatasource::tokenizeString($map['name']);
|
||||
|
|
|
@ -27,6 +27,19 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
const CONTEXT_PANEL = 'panel';
|
||||
|
||||
public function newResultObject() {
|
||||
// We may be able to get this automatically if newQuery() is implemented.
|
||||
$query = $this->newQuery();
|
||||
if ($query) {
|
||||
$object = $query->newResultObject();
|
||||
if ($object) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function newQuery() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -98,15 +111,15 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
$fields = $this->buildSearchFields();
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$parameters = array();
|
||||
$map = array();
|
||||
foreach ($fields as $field) {
|
||||
$field->setViewer($viewer);
|
||||
$field->readValueFromSavedQuery($saved);
|
||||
$value = $field->getValueForQuery($field->getValue());
|
||||
$parameters[$field->getKey()] = $value;
|
||||
$map[$field->getKey()] = $value;
|
||||
}
|
||||
|
||||
$query = $this->buildQueryFromParameters($parameters);
|
||||
$query = $this->buildQueryFromParameters($map);
|
||||
|
||||
$object = $this->newResultObject();
|
||||
if (!$object) {
|
||||
|
@ -114,25 +127,25 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
}
|
||||
|
||||
if ($object instanceof PhabricatorSubscribableInterface) {
|
||||
if (!empty($parameters['subscriberPHIDs'])) {
|
||||
if (!empty($map['subscriberPHIDs'])) {
|
||||
$query->withEdgeLogicPHIDs(
|
||||
PhabricatorObjectHasSubscriberEdgeType::EDGECONST,
|
||||
PhabricatorQueryConstraint::OPERATOR_OR,
|
||||
$parameters['subscriberPHIDs']);
|
||||
$map['subscriberPHIDs']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($object instanceof PhabricatorProjectInterface) {
|
||||
if (!empty($parameters['projectPHIDs'])) {
|
||||
if (!empty($map['projectPHIDs'])) {
|
||||
$query->withEdgeLogicConstraints(
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||
$parameters['projectPHIDs']);
|
||||
$map['projectPHIDs']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($object instanceof PhabricatorSpacesInterface) {
|
||||
if (!empty($parameters['spacePHIDs'])) {
|
||||
$query->withSpacePHIDs($parameters['spacePHIDs']);
|
||||
if (!empty($map['spacePHIDs'])) {
|
||||
$query->withSpacePHIDs($map['spacePHIDs']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,6 +153,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
$this->applyCustomFieldsToQuery($query, $saved);
|
||||
}
|
||||
|
||||
$this->setQueryOrder($query, $saved);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
@ -185,30 +200,28 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
}
|
||||
|
||||
$object = $this->newResultObject();
|
||||
if (!$object) {
|
||||
return $fields;
|
||||
}
|
||||
if ($object) {
|
||||
if ($object instanceof PhabricatorSubscribableInterface) {
|
||||
$fields[] = id(new PhabricatorSearchSubscribersField())
|
||||
->setLabel(pht('Subscribers'))
|
||||
->setKey('subscriberPHIDs')
|
||||
->setAliases(array('subscriber', 'subscribers'));
|
||||
}
|
||||
|
||||
if ($object instanceof PhabricatorSubscribableInterface) {
|
||||
$fields[] = id(new PhabricatorSearchSubscribersField())
|
||||
->setLabel(pht('Subscribers'))
|
||||
->setKey('subscriberPHIDs')
|
||||
->setAliases(array('subscriber', 'subscribers'));
|
||||
}
|
||||
if ($object instanceof PhabricatorProjectInterface) {
|
||||
$fields[] = id(new PhabricatorSearchProjectsField())
|
||||
->setKey('projectPHIDs')
|
||||
->setAliases(array('project', 'projects'))
|
||||
->setLabel(pht('Projects'));
|
||||
}
|
||||
|
||||
if ($object instanceof PhabricatorProjectInterface) {
|
||||
$fields[] = id(new PhabricatorSearchProjectsField())
|
||||
->setKey('projectPHIDs')
|
||||
->setAliases(array('project', 'projects'))
|
||||
->setLabel(pht('Projects'));
|
||||
}
|
||||
|
||||
if ($object instanceof PhabricatorSpacesInterface) {
|
||||
if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) {
|
||||
$fields[] = id(new PhabricatorSearchSpacesField())
|
||||
->setKey('spacePHIDs')
|
||||
->setAliases(array('space', 'spaces'))
|
||||
->setLabel(pht('Spaces'));
|
||||
if ($object instanceof PhabricatorSpacesInterface) {
|
||||
if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) {
|
||||
$fields[] = id(new PhabricatorSearchSpacesField())
|
||||
->setKey('spacePHIDs')
|
||||
->setAliases(array('space', 'spaces'))
|
||||
->setLabel(pht('Spaces'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,6 +229,17 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
$fields[] = $custom_field;
|
||||
}
|
||||
|
||||
$query = $this->newQuery();
|
||||
if ($query) {
|
||||
$orders = $query->getBuiltinOrders();
|
||||
$orders = ipull($orders, 'name');
|
||||
|
||||
$fields[] = id(new PhabricatorSearchOrderField())
|
||||
->setLabel(pht('Order'))
|
||||
->setKey('order')
|
||||
->setOptions($orders);
|
||||
}
|
||||
|
||||
$field_map = array();
|
||||
foreach ($fields as $field) {
|
||||
$key = $field->getKey();
|
||||
|
@ -890,6 +914,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
|
||||
$order = $saved->getParameter('order');
|
||||
$builtin = $query->getBuiltinOrders();
|
||||
|
||||
if (strlen($order) && isset($builtin[$order])) {
|
||||
$query->setOrder($order);
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSearchOrderField
|
||||
extends PhabricatorSearchField {
|
||||
|
||||
private $options;
|
||||
|
||||
public function setOptions(array $options) {
|
||||
$this->options = $options;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOptions() {
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
protected function getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getValueFromRequest(AphrontRequest $request, $key) {
|
||||
return $request->getStr($key);
|
||||
}
|
||||
|
||||
protected function newControl() {
|
||||
return id(new AphrontFormSelectControl())
|
||||
->setOptions($this->getOptions());
|
||||
}
|
||||
|
||||
}
|
|
@ -200,7 +200,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
return null;
|
||||
}
|
||||
|
||||
protected function newResultObject() {
|
||||
public function newResultObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -844,6 +844,9 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
'column' => 'indexValue',
|
||||
'type' => $index->getIndexValueType(),
|
||||
'null' => 'tail',
|
||||
'customfield' => true,
|
||||
'customfield.index.table' => $index->getTableName(),
|
||||
'customfield.index.key' => $digest,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1229,6 +1232,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Get rid of this.
|
||||
foreach ($this->applicationSearchOrders as $key => $order) {
|
||||
$table = $order['table'];
|
||||
$index = $order['index'];
|
||||
|
@ -1247,6 +1251,32 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
|||
$index);
|
||||
}
|
||||
|
||||
$phid_column = $this->getApplicationSearchObjectPHIDColumn();
|
||||
$orderable = $this->getOrderableColumns();
|
||||
|
||||
$vector = $this->getOrderVector();
|
||||
foreach ($vector as $order) {
|
||||
$spec = $orderable[$order->getOrderKey()];
|
||||
if (empty($spec['customfield'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$table = $spec['customfield.index.table'];
|
||||
$alias = $spec['table'];
|
||||
$key = $spec['customfield.index.key'];
|
||||
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
'LEFT JOIN %T %T ON %T.objectPHID = %Q
|
||||
AND %T.indexKey = %s',
|
||||
$table,
|
||||
$alias,
|
||||
$alias,
|
||||
$phid_column,
|
||||
$alias,
|
||||
$key);
|
||||
}
|
||||
|
||||
return implode(' ', $joins);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue