1
0
Fork 0
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:
epriestley 2015-06-08 12:21:48 -07:00
parent 3cdaf52ce9
commit bf87976d25
17 changed files with 229 additions and 154 deletions

View file

@ -2520,6 +2520,7 @@ phutil_register_library_map(array(
'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php', 'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php',
'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php', 'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php',
'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php', 'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php',
'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php',
'PhabricatorSearchOwnersField' => 'applications/search/field/PhabricatorSearchOwnersField.php', 'PhabricatorSearchOwnersField' => 'applications/search/field/PhabricatorSearchOwnersField.php',
'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php', 'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php',
'PhabricatorSearchProjectsField' => 'applications/search/field/PhabricatorSearchProjectsField.php', 'PhabricatorSearchProjectsField' => 'applications/search/field/PhabricatorSearchProjectsField.php',
@ -6024,6 +6025,7 @@ phutil_register_library_map(array(
'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow', 'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow',
'PhabricatorSearchManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorSearchManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController',
'PhabricatorSearchOrderField' => 'PhabricatorSearchField',
'PhabricatorSearchOwnersField' => 'PhabricatorSearchTokenizerField', 'PhabricatorSearchOwnersField' => 'PhabricatorSearchTokenizerField',
'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorSearchProjectsField' => 'PhabricatorSearchTokenizerField', 'PhabricatorSearchProjectsField' => 'PhabricatorSearchTokenizerField',

View file

@ -60,47 +60,35 @@ final class AlmanacServiceQuery
} }
protected function loadPage() { protected function loadPage() {
$table = new AlmanacService(); return $this->loadStandardPage(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);
} }
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = array(); $joins = parent::buildJoinClauseParts($conn);
if ($this->devicePHIDs !== null) { if ($this->devicePHIDs !== null) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn_r, $conn,
'JOIN %T binding ON service.phid = binding.servicePHID', 'JOIN %T binding ON service.phid = binding.servicePHID',
id(new AlmanacBinding())->getTableName()); id(new AlmanacBinding())->getTableName());
} }
return implode(' ', $joins); return $joins;
} }
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = array(); $where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) { if ($this->ids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'service.id IN (%Ld)', 'service.id IN (%Ld)',
$this->ids); $this->ids);
} }
if ($this->phids !== null) { if ($this->phids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'service.phid IN (%Ls)', 'service.phid IN (%Ls)',
$this->phids); $this->phids);
} }
@ -112,49 +100,47 @@ final class AlmanacServiceQuery
} }
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'service.nameIndex IN (%Ls)', 'service.nameIndex IN (%Ls)',
$hashes); $hashes);
} }
if ($this->serviceClasses !== null) { if ($this->serviceClasses !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'service.serviceClass IN (%Ls)', 'service.serviceClass IN (%Ls)',
$this->serviceClasses); $this->serviceClasses);
} }
if ($this->devicePHIDs !== null) { if ($this->devicePHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'binding.devicePHID IN (%Ls)', 'binding.devicePHID IN (%Ls)',
$this->devicePHIDs); $this->devicePHIDs);
} }
if ($this->locked !== null) { if ($this->locked !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'service.isLocked = %d', 'service.isLocked = %d',
(int)$this->locked); (int)$this->locked);
} }
if ($this->namePrefix !== null) { if ($this->namePrefix !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'service.name LIKE %>', 'service.name LIKE %>',
$this->namePrefix); $this->namePrefix);
} }
if ($this->nameSuffix !== null) { if ($this->nameSuffix !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'service.name LIKE %<', 'service.name LIKE %<',
$this->nameSuffix); $this->nameSuffix);
} }
$where[] = $this->buildPagingClause($conn_r); return $where;
return $this->formatWhereClause($where);
} }
protected function willFilterPage(array $services) { protected function willFilterPage(array $services) {
@ -192,10 +178,14 @@ final class AlmanacServiceQuery
return parent::didFilterPage($services); return parent::didFilterPage($services);
} }
public function getPrimaryTableAlias() {
return 'service';
}
public function getOrderableColumns() { public function getOrderableColumns() {
return parent::getOrderableColumns() + array( return parent::getOrderableColumns() + array(
'name' => array( 'name' => array(
'table' => 'service', 'table' => $this->getPrimaryTableAlias(),
'column' => 'name', 'column' => 'name',
'type' => 'string', 'type' => 'string',
'unique' => true, 'unique' => true,

View file

@ -11,30 +11,26 @@ final class AlmanacServiceSearchEngine
return 'PhabricatorAlmanacApplication'; return 'PhabricatorAlmanacApplication';
} }
public function buildSavedQueryFromRequest(AphrontRequest $request) { public function newQuery() {
$saved = new PhabricatorSavedQuery(); return new AlmanacServiceQuery();
$this->saveQueryOrder($saved, $request);
return $saved;
} }
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { public function newResultObject() {
$query = id(new AlmanacServiceQuery()); // 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; return $query;
} }
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$this->appendOrderFieldsToForm( protected function buildCustomSearchFields() {
$form, return array();
$saved,
new AlmanacServiceQuery());
} }
protected function getURI($path) { protected function getURI($path) {
@ -62,12 +58,6 @@ final class AlmanacServiceSearchEngine
return parent::buildSavedQueryFromBuiltin($query_key); return parent::buildSavedQueryFromBuiltin($query_key);
} }
protected function getRequiredHandlePHIDsForResultList(
array $services,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList( protected function renderResultList(
array $services, array $services,
PhabricatorSavedQuery $query, PhabricatorSavedQuery $query,

View file

@ -117,6 +117,10 @@ final class PhabricatorFileQuery
return $this; return $this;
} }
public function newResultObject() {
return new PhabricatorFile();
}
protected function loadPage() { protected function loadPage() {
$files = $this->loadStandardPage(new PhabricatorFile()); $files = $this->loadStandardPage(new PhabricatorFile());

View file

@ -11,8 +11,8 @@ final class PhabricatorFileSearchEngine
return 'PhabricatorFilesApplication'; return 'PhabricatorFilesApplication';
} }
public function newResultObject() { public function newQuery() {
return new PhabricatorFile(); return new PhabricatorFileQuery();
} }
protected function buildCustomSearchFields() { protected function buildCustomSearchFields() {
@ -46,7 +46,7 @@ final class PhabricatorFileSearchEngine
} }
public function buildQueryFromParameters(array $map) { public function buildQueryFromParameters(array $map) {
$query = id(new PhabricatorFileQuery()); $query = $this->newQuery();
if ($map['authorPHIDs']) { if ($map['authorPHIDs']) {
$query->withAuthorPHIDs($map['authorPHIDs']); $query->withAuthorPHIDs($map['authorPHIDs']);

View file

@ -193,7 +193,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
return $this; return $this;
} }
protected function newResultObject() { public function newResultObject() {
return new ManiphestTask(); return new ManiphestTask();
} }

View file

@ -67,7 +67,7 @@ final class PhabricatorPasteQuery
return $this; return $this;
} }
protected function newResultObject() { public function newResultObject() {
return new PhabricatorPaste(); return new PhabricatorPaste();
} }

View file

@ -11,13 +11,13 @@ final class PhabricatorPasteSearchEngine
return 'PhabricatorPasteApplication'; return 'PhabricatorPasteApplication';
} }
public function newResultObject() { public function newQuery() {
return new PhabricatorPaste(); return id(new PhabricatorPasteQuery())
->needContent(true);
} }
protected function buildQueryFromParameters(array $map) { protected function buildQueryFromParameters(array $map) {
$query = id(new PhabricatorPasteQuery()) $query = $this->newQuery();
->needContent(true);
if ($map['authorPHIDs']) { if ($map['authorPHIDs']) {
$query->withAuthorPHIDs($map['authorPHIDs']); $query->withAuthorPHIDs($map['authorPHIDs']);

View file

@ -113,19 +113,13 @@ final class PhabricatorPeopleQuery
return $this; return $this;
} }
protected function loadPage() { public function newResultObject() {
$table = new PhabricatorUser(); return new PhabricatorUser();
$conn_r = $table->establishConnection('r'); }
$data = queryfx_all( protected function loadPage() {
$conn_r, $table = new PhabricatorUser();
'SELECT * FROM %T user %Q %Q %Q %Q %Q', $data = $this->loadStandardPageRows($table);
$table->getTableName(),
$this->buildJoinsClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildGroupClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
if ($this->needPrimaryEmail) { if ($this->needPrimaryEmail) {
$table->putInSet(new LiskDAOSet()); $table->putInSet(new LiskDAOSet());
@ -225,23 +219,21 @@ final class PhabricatorPeopleQuery
return $users; return $users;
} }
protected function buildGroupClause(AphrontDatabaseConnection $conn) { protected function shouldGroupQueryResultRows() {
if ($this->nameTokens) { if ($this->nameTokens) {
return qsprintf( return true;
$conn,
'GROUP BY user.id');
} else {
return $this->buildApplicationSearchGroupClause($conn);
} }
return parent::shouldGroupQueryResultRows();
} }
private function buildJoinsClause($conn_r) { protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = array(); $joins = parent::buildJoinClauseParts($conn);
if ($this->emails) { if ($this->emails) {
$email_table = new PhabricatorUserEmail(); $email_table = new PhabricatorUserEmail();
$joins[] = qsprintf( $joins[] = qsprintf(
$conn_r, $conn,
'JOIN %T email ON email.userPHID = user.PHID', 'JOIN %T email ON email.userPHID = user.PHID',
$email_table->getTableName()); $email_table->getTableName());
} }
@ -250,7 +242,7 @@ final class PhabricatorPeopleQuery
foreach ($this->nameTokens as $key => $token) { foreach ($this->nameTokens as $key => $token) {
$token_table = 'token_'.$key; $token_table = 'token_'.$key;
$joins[] = qsprintf( $joins[] = qsprintf(
$conn_r, $conn,
'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>', 'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>',
PhabricatorUser::NAMETOKEN_TABLE, PhabricatorUser::NAMETOKEN_TABLE,
$token_table, $token_table,
@ -260,110 +252,105 @@ final class PhabricatorPeopleQuery
} }
} }
$joins[] = $this->buildApplicationSearchJoinClause($conn_r);
$joins = implode(' ', $joins);
return $joins; return $joins;
} }
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = array(); $where = parent::buildWhereClauseParts($conn);
if ($this->usernames !== null) { if ($this->usernames !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.userName IN (%Ls)', 'user.userName IN (%Ls)',
$this->usernames); $this->usernames);
} }
if ($this->emails !== null) { if ($this->emails !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'email.address IN (%Ls)', 'email.address IN (%Ls)',
$this->emails); $this->emails);
} }
if ($this->realnames !== null) { if ($this->realnames !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.realName IN (%Ls)', 'user.realName IN (%Ls)',
$this->realnames); $this->realnames);
} }
if ($this->phids !== null) { if ($this->phids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.phid IN (%Ls)', 'user.phid IN (%Ls)',
$this->phids); $this->phids);
} }
if ($this->ids !== null) { if ($this->ids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.id IN (%Ld)', 'user.id IN (%Ld)',
$this->ids); $this->ids);
} }
if ($this->dateCreatedAfter) { if ($this->dateCreatedAfter) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.dateCreated >= %d', 'user.dateCreated >= %d',
$this->dateCreatedAfter); $this->dateCreatedAfter);
} }
if ($this->dateCreatedBefore) { if ($this->dateCreatedBefore) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.dateCreated <= %d', 'user.dateCreated <= %d',
$this->dateCreatedBefore); $this->dateCreatedBefore);
} }
if ($this->isAdmin !== null) { if ($this->isAdmin !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.isAdmin = %d', 'user.isAdmin = %d',
(int)$this->isAdmin); (int)$this->isAdmin);
} }
if ($this->isDisabled !== null) { if ($this->isDisabled !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.isDisabled = %d', 'user.isDisabled = %d',
(int)$this->isDisabled); (int)$this->isDisabled);
} }
if ($this->isApproved !== null) { if ($this->isApproved !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.isApproved = %d', 'user.isApproved = %d',
(int)$this->isApproved); (int)$this->isApproved);
} }
if ($this->isSystemAgent !== null) { if ($this->isSystemAgent !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.isSystemAgent = %d', 'user.isSystemAgent = %d',
(int)$this->isSystemAgent); (int)$this->isSystemAgent);
} }
if ($this->isMailingList !== null) { if ($this->isMailingList !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.isMailingList = %d', 'user.isMailingList = %d',
(int)$this->isMailingList); (int)$this->isMailingList);
} }
if (strlen($this->nameLike)) { if (strlen($this->nameLike)) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'user.username LIKE %~ OR user.realname LIKE %~', 'user.username LIKE %~ OR user.realname LIKE %~',
$this->nameLike, $this->nameLike,
$this->nameLike); $this->nameLike);
} }
$where[] = $this->buildPagingClause($conn_r); return $where;
return $this->formatWhereClause($where);
} }
protected function getPrimaryTableAlias() { protected function getPrimaryTableAlias() {

View file

@ -11,8 +11,10 @@ final class PhabricatorPeopleSearchEngine
return 'PhabricatorPeopleApplication'; return 'PhabricatorPeopleApplication';
} }
public function newResultObject() { public function newQuery() {
return new PhabricatorUser(); return id(new PhabricatorPeopleQuery())
->needPrimaryEmail(true)
->needProfileImage(true);
} }
protected function buildCustomSearchFields() { protected function buildCustomSearchFields() {
@ -77,9 +79,7 @@ final class PhabricatorPeopleSearchEngine
} }
public function buildQueryFromParameters(array $map) { public function buildQueryFromParameters(array $map) {
$query = id(new PhabricatorPeopleQuery()) $query = $this->newQuery();
->needPrimaryEmail(true)
->needProfileImage(true);
$viewer = $this->requireViewer(); $viewer = $this->requireViewer();

View file

@ -53,6 +53,10 @@ final class PholioMockQuery
return $this; return $this;
} }
public function newResultObject() {
return new PholioMock();
}
protected function loadPage() { protected function loadPage() {
$mocks = $this->loadStandardPage(new PholioMock()); $mocks = $this->loadStandardPage(new PholioMock());

View file

@ -10,11 +10,14 @@ final class PholioMockSearchEngine extends PhabricatorApplicationSearchEngine {
return 'PhabricatorPholioApplication'; return 'PhabricatorPholioApplication';
} }
public function newResultObject() { public function newQuery() {
return new PholioMock(); return id(new PholioMockQuery())
->needCoverFiles(true)
->needImages(true)
->needTokenCounts(true);
} }
public function buildCustomSearchFields() { protected function buildCustomSearchFields() {
return array( return array(
id(new PhabricatorSearchUsersField()) id(new PhabricatorSearchUsersField())
->setKey('authorPHIDs') ->setKey('authorPHIDs')
@ -30,10 +33,7 @@ final class PholioMockSearchEngine extends PhabricatorApplicationSearchEngine {
} }
public function buildQueryFromParameters(array $map) { public function buildQueryFromParameters(array $map) {
$query = id(new PholioMockQuery()) $query = $this->newQuery();
->needCoverFiles(true)
->needImages(true)
->needTokenCounts(true);
if ($map['authorPHIDs']) { if ($map['authorPHIDs']) {
$query->withAuthorPHIDs($map['authorPHIDs']); $query->withAuthorPHIDs($map['authorPHIDs']);

View file

@ -95,12 +95,25 @@ final class PhabricatorProjectQuery
return $this; return $this;
} }
public function newResultObject() {
return new PhabricatorProject();
}
protected function getDefaultOrderVector() { protected function getDefaultOrderVector() {
return array('name'); return array('name');
} }
public function getOrderableColumns() { public function getBuiltinOrders() {
return array( return array(
'name' => array(
'vector' => array('name'),
'name' => pht('Name'),
),
) + parent::getBuiltinOrders();
}
public function getOrderableColumns() {
return parent::getOrderableColumns() + array(
'name' => array( 'name' => array(
'table' => $this->getPrimaryTableAlias(), 'table' => $this->getPrimaryTableAlias(),
'column' => 'name', 'column' => 'name',

View file

@ -11,8 +11,9 @@ final class PhabricatorProjectSearchEngine
return 'PhabricatorProjectApplication'; return 'PhabricatorProjectApplication';
} }
public function newResultObject() { public function newQuery() {
return new PhabricatorProject(); return id(new PhabricatorProjectQuery())
->needImages(true);
} }
protected function buildCustomSearchFields() { protected function buildCustomSearchFields() {
@ -41,8 +42,7 @@ final class PhabricatorProjectSearchEngine
public function buildQueryFromParameters(array $map) { public function buildQueryFromParameters(array $map) {
$query = id(new PhabricatorProjectQuery()) $query = $this->newQuery();
->needImages(true);
if (strlen($map['name'])) { if (strlen($map['name'])) {
$tokens = PhabricatorTypeaheadDatasource::tokenizeString($map['name']); $tokens = PhabricatorTypeaheadDatasource::tokenizeString($map['name']);

View file

@ -27,6 +27,19 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
const CONTEXT_PANEL = 'panel'; const CONTEXT_PANEL = 'panel';
public function newResultObject() { 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; return null;
} }
@ -98,15 +111,15 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$fields = $this->buildSearchFields(); $fields = $this->buildSearchFields();
$viewer = $this->requireViewer(); $viewer = $this->requireViewer();
$parameters = array(); $map = array();
foreach ($fields as $field) { foreach ($fields as $field) {
$field->setViewer($viewer); $field->setViewer($viewer);
$field->readValueFromSavedQuery($saved); $field->readValueFromSavedQuery($saved);
$value = $field->getValueForQuery($field->getValue()); $value = $field->getValueForQuery($field->getValue());
$parameters[$field->getKey()] = $value; $map[$field->getKey()] = $value;
} }
$query = $this->buildQueryFromParameters($parameters); $query = $this->buildQueryFromParameters($map);
$object = $this->newResultObject(); $object = $this->newResultObject();
if (!$object) { if (!$object) {
@ -114,25 +127,25 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
} }
if ($object instanceof PhabricatorSubscribableInterface) { if ($object instanceof PhabricatorSubscribableInterface) {
if (!empty($parameters['subscriberPHIDs'])) { if (!empty($map['subscriberPHIDs'])) {
$query->withEdgeLogicPHIDs( $query->withEdgeLogicPHIDs(
PhabricatorObjectHasSubscriberEdgeType::EDGECONST, PhabricatorObjectHasSubscriberEdgeType::EDGECONST,
PhabricatorQueryConstraint::OPERATOR_OR, PhabricatorQueryConstraint::OPERATOR_OR,
$parameters['subscriberPHIDs']); $map['subscriberPHIDs']);
} }
} }
if ($object instanceof PhabricatorProjectInterface) { if ($object instanceof PhabricatorProjectInterface) {
if (!empty($parameters['projectPHIDs'])) { if (!empty($map['projectPHIDs'])) {
$query->withEdgeLogicConstraints( $query->withEdgeLogicConstraints(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
$parameters['projectPHIDs']); $map['projectPHIDs']);
} }
} }
if ($object instanceof PhabricatorSpacesInterface) { if ($object instanceof PhabricatorSpacesInterface) {
if (!empty($parameters['spacePHIDs'])) { if (!empty($map['spacePHIDs'])) {
$query->withSpacePHIDs($parameters['spacePHIDs']); $query->withSpacePHIDs($map['spacePHIDs']);
} }
} }
@ -140,6 +153,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$this->applyCustomFieldsToQuery($query, $saved); $this->applyCustomFieldsToQuery($query, $saved);
} }
$this->setQueryOrder($query, $saved);
return $query; return $query;
} }
@ -185,30 +200,28 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
} }
$object = $this->newResultObject(); $object = $this->newResultObject();
if (!$object) { if ($object) {
return $fields; if ($object instanceof PhabricatorSubscribableInterface) {
} $fields[] = id(new PhabricatorSearchSubscribersField())
->setLabel(pht('Subscribers'))
->setKey('subscriberPHIDs')
->setAliases(array('subscriber', 'subscribers'));
}
if ($object instanceof PhabricatorSubscribableInterface) { if ($object instanceof PhabricatorProjectInterface) {
$fields[] = id(new PhabricatorSearchSubscribersField()) $fields[] = id(new PhabricatorSearchProjectsField())
->setLabel(pht('Subscribers')) ->setKey('projectPHIDs')
->setKey('subscriberPHIDs') ->setAliases(array('project', 'projects'))
->setAliases(array('subscriber', 'subscribers')); ->setLabel(pht('Projects'));
} }
if ($object instanceof PhabricatorProjectInterface) { if ($object instanceof PhabricatorSpacesInterface) {
$fields[] = id(new PhabricatorSearchProjectsField()) if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) {
->setKey('projectPHIDs') $fields[] = id(new PhabricatorSearchSpacesField())
->setAliases(array('project', 'projects')) ->setKey('spacePHIDs')
->setLabel(pht('Projects')); ->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; $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(); $field_map = array();
foreach ($fields as $field) { foreach ($fields as $field) {
$key = $field->getKey(); $key = $field->getKey();
@ -890,6 +914,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$order = $saved->getParameter('order'); $order = $saved->getParameter('order');
$builtin = $query->getBuiltinOrders(); $builtin = $query->getBuiltinOrders();
if (strlen($order) && isset($builtin[$order])) { if (strlen($order) && isset($builtin[$order])) {
$query->setOrder($order); $query->setOrder($order);
} else { } else {

View file

@ -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());
}
}

View file

@ -200,7 +200,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
return null; return null;
} }
protected function newResultObject() { public function newResultObject() {
return null; return null;
} }
@ -844,6 +844,9 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
'column' => 'indexValue', 'column' => 'indexValue',
'type' => $index->getIndexValueType(), 'type' => $index->getIndexValueType(),
'null' => 'tail', '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) { foreach ($this->applicationSearchOrders as $key => $order) {
$table = $order['table']; $table = $order['table'];
$index = $order['index']; $index = $order['index'];
@ -1247,6 +1251,32 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
$index); $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); return implode(' ', $joins);
} }