Path: blob/master/src/applications/feed/query/PhabricatorFeedTransactionQuery.php
12242 views
<?php12final class PhabricatorFeedTransactionQuery3extends PhabricatorCursorPagedPolicyAwareQuery {45private $phids;6private $authorPHIDs;7private $objectTypes;8private $createdMin;9private $createdMax;1011public function withPHIDs(array $phids) {12$this->phids = $phids;13return $this;14}1516public function withAuthorPHIDs(array $phids) {17$this->authorPHIDs = $phids;18return $this;19}2021public function withObjectTypes(array $types) {22$this->objectTypes = $types;23return $this;24}2526public function withDateCreatedBetween($min, $max) {27$this->createdMin = $min;28$this->createdMax = $max;29return $this;30}3132public function newResultObject() {33// Return an arbitrary valid transaction object. The actual query may34// return objects of any subclass of "ApplicationTransaction" when it is35// executed, but we need to pick something concrete here to make some36// integrations work (like automatic handling of PHIDs in data export).37return new PhabricatorUserTransaction();38}3940protected function loadPage() {41$queries = $this->newTransactionQueries();4243$xactions = array();4445if ($this->shouldLimitResults()) {46$limit = $this->getRawResultLimit();47if (!$limit) {48$limit = null;49}50} else {51$limit = null;52}5354// We're doing a bit of manual work to get paging working, because this55// query aggregates the results of a large number of subqueries.5657// Overall, we're ordering transactions by "<dateCreated, phid>". Ordering58// by PHID is not very meaningful, but we don't need the ordering to be59// especially meaningful, just consistent. Using PHIDs is easy and does60// everything we need it to technically.6162// To actually configure paging, if we have an external cursor, we load63// the internal cursor first. Then we pass it to each subquery and the64// subqueries pretend they just loaded a page where it was the last object.65// This configures their queries properly and we can aggregate a cohesive66// set of results by combining all the queries.6768$cursor = $this->getExternalCursorString();69if ($cursor !== null) {70$cursor_object = $this->newInternalCursorFromExternalCursor($cursor);71} else {72$cursor_object = null;73}7475$is_reversed = $this->getIsQueryOrderReversed();7677$created_min = $this->createdMin;78$created_max = $this->createdMax;7980$xaction_phids = $this->phids;81$author_phids = $this->authorPHIDs;8283foreach ($queries as $query) {84$query->withDateCreatedBetween($created_min, $created_max);8586if ($xaction_phids !== null) {87$query->withPHIDs($xaction_phids);88}8990if ($author_phids !== null) {91$query->withAuthorPHIDs($author_phids);92}9394if ($limit !== null) {95$query->setLimit($limit);96}9798if ($cursor_object !== null) {99$query100->setAggregatePagingCursor($cursor_object)101->setIsQueryOrderReversed($is_reversed);102}103104$query->setOrder('global');105106$query_xactions = $query->execute();107foreach ($query_xactions as $query_xaction) {108$xactions[] = $query_xaction;109}110111$xactions = msortv($xactions, 'newGlobalSortVector');112if ($is_reversed) {113$xactions = array_reverse($xactions);114}115116if ($limit !== null) {117$xactions = array_slice($xactions, 0, $limit);118119// If we've found enough transactions to fill up the entire requested120// page size, we can narrow the search window: transactions after the121// last transaction we've found so far can't possibly be part of the122// result set.123124if (count($xactions) === $limit) {125$last_date = last($xactions)->getDateCreated();126if ($is_reversed) {127if ($created_max === null) {128$created_max = $last_date;129} else {130$created_max = min($created_max, $last_date);131}132} else {133if ($created_min === null) {134$created_min = $last_date;135} else {136$created_min = max($created_min, $last_date);137}138}139}140}141}142143return $xactions;144}145146public function getQueryApplicationClass() {147return 'PhabricatorFeedApplication';148}149150private function newTransactionQueries() {151$viewer = $this->getViewer();152153$queries = id(new PhutilClassMapQuery())154->setAncestorClass('PhabricatorApplicationTransactionQuery')155->execute();156157$type_map = array();158159// If we're querying for specific transaction PHIDs, we only need to160// consider queries which may load transactions with subtypes present161// in the list.162163// For example, if we're loading Maniphest Task transaction PHIDs, we know164// we only have to look at Maniphest Task transactions, since other types165// of objects will never have the right transaction PHIDs.166167$xaction_phids = $this->phids;168if ($xaction_phids) {169foreach ($xaction_phids as $xaction_phid) {170$type_map[phid_get_subtype($xaction_phid)] = true;171}172}173174$object_types = $this->objectTypes;175if ($object_types) {176$object_types = array_fuse($object_types);177}178179$results = array();180foreach ($queries as $query) {181$query_type = $query->getTemplateApplicationTransaction()182->getApplicationTransactionType();183184if ($type_map) {185if (!isset($type_map[$query_type])) {186continue;187}188}189190if ($object_types) {191if (!isset($object_types[$query_type])) {192continue;193}194}195196$results[] = id(clone $query)197->setViewer($viewer)198->setParentQuery($this);199}200201return $results;202}203204protected function newExternalCursorStringForResult($object) {205return (string)$object->getPHID();206}207208protected function applyExternalCursorConstraintsToQuery(209PhabricatorCursorPagedPolicyAwareQuery $subquery,210$cursor) {211$subquery->withPHIDs(array($cursor));212}213214}215216217