Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/feed/query/PhabricatorFeedTransactionQuery.php
12242 views
1
<?php
2
3
final class PhabricatorFeedTransactionQuery
4
extends PhabricatorCursorPagedPolicyAwareQuery {
5
6
private $phids;
7
private $authorPHIDs;
8
private $objectTypes;
9
private $createdMin;
10
private $createdMax;
11
12
public function withPHIDs(array $phids) {
13
$this->phids = $phids;
14
return $this;
15
}
16
17
public function withAuthorPHIDs(array $phids) {
18
$this->authorPHIDs = $phids;
19
return $this;
20
}
21
22
public function withObjectTypes(array $types) {
23
$this->objectTypes = $types;
24
return $this;
25
}
26
27
public function withDateCreatedBetween($min, $max) {
28
$this->createdMin = $min;
29
$this->createdMax = $max;
30
return $this;
31
}
32
33
public function newResultObject() {
34
// Return an arbitrary valid transaction object. The actual query may
35
// return objects of any subclass of "ApplicationTransaction" when it is
36
// executed, but we need to pick something concrete here to make some
37
// integrations work (like automatic handling of PHIDs in data export).
38
return new PhabricatorUserTransaction();
39
}
40
41
protected function loadPage() {
42
$queries = $this->newTransactionQueries();
43
44
$xactions = array();
45
46
if ($this->shouldLimitResults()) {
47
$limit = $this->getRawResultLimit();
48
if (!$limit) {
49
$limit = null;
50
}
51
} else {
52
$limit = null;
53
}
54
55
// We're doing a bit of manual work to get paging working, because this
56
// query aggregates the results of a large number of subqueries.
57
58
// Overall, we're ordering transactions by "<dateCreated, phid>". Ordering
59
// by PHID is not very meaningful, but we don't need the ordering to be
60
// especially meaningful, just consistent. Using PHIDs is easy and does
61
// everything we need it to technically.
62
63
// To actually configure paging, if we have an external cursor, we load
64
// the internal cursor first. Then we pass it to each subquery and the
65
// subqueries pretend they just loaded a page where it was the last object.
66
// This configures their queries properly and we can aggregate a cohesive
67
// set of results by combining all the queries.
68
69
$cursor = $this->getExternalCursorString();
70
if ($cursor !== null) {
71
$cursor_object = $this->newInternalCursorFromExternalCursor($cursor);
72
} else {
73
$cursor_object = null;
74
}
75
76
$is_reversed = $this->getIsQueryOrderReversed();
77
78
$created_min = $this->createdMin;
79
$created_max = $this->createdMax;
80
81
$xaction_phids = $this->phids;
82
$author_phids = $this->authorPHIDs;
83
84
foreach ($queries as $query) {
85
$query->withDateCreatedBetween($created_min, $created_max);
86
87
if ($xaction_phids !== null) {
88
$query->withPHIDs($xaction_phids);
89
}
90
91
if ($author_phids !== null) {
92
$query->withAuthorPHIDs($author_phids);
93
}
94
95
if ($limit !== null) {
96
$query->setLimit($limit);
97
}
98
99
if ($cursor_object !== null) {
100
$query
101
->setAggregatePagingCursor($cursor_object)
102
->setIsQueryOrderReversed($is_reversed);
103
}
104
105
$query->setOrder('global');
106
107
$query_xactions = $query->execute();
108
foreach ($query_xactions as $query_xaction) {
109
$xactions[] = $query_xaction;
110
}
111
112
$xactions = msortv($xactions, 'newGlobalSortVector');
113
if ($is_reversed) {
114
$xactions = array_reverse($xactions);
115
}
116
117
if ($limit !== null) {
118
$xactions = array_slice($xactions, 0, $limit);
119
120
// If we've found enough transactions to fill up the entire requested
121
// page size, we can narrow the search window: transactions after the
122
// last transaction we've found so far can't possibly be part of the
123
// result set.
124
125
if (count($xactions) === $limit) {
126
$last_date = last($xactions)->getDateCreated();
127
if ($is_reversed) {
128
if ($created_max === null) {
129
$created_max = $last_date;
130
} else {
131
$created_max = min($created_max, $last_date);
132
}
133
} else {
134
if ($created_min === null) {
135
$created_min = $last_date;
136
} else {
137
$created_min = max($created_min, $last_date);
138
}
139
}
140
}
141
}
142
}
143
144
return $xactions;
145
}
146
147
public function getQueryApplicationClass() {
148
return 'PhabricatorFeedApplication';
149
}
150
151
private function newTransactionQueries() {
152
$viewer = $this->getViewer();
153
154
$queries = id(new PhutilClassMapQuery())
155
->setAncestorClass('PhabricatorApplicationTransactionQuery')
156
->execute();
157
158
$type_map = array();
159
160
// If we're querying for specific transaction PHIDs, we only need to
161
// consider queries which may load transactions with subtypes present
162
// in the list.
163
164
// For example, if we're loading Maniphest Task transaction PHIDs, we know
165
// we only have to look at Maniphest Task transactions, since other types
166
// of objects will never have the right transaction PHIDs.
167
168
$xaction_phids = $this->phids;
169
if ($xaction_phids) {
170
foreach ($xaction_phids as $xaction_phid) {
171
$type_map[phid_get_subtype($xaction_phid)] = true;
172
}
173
}
174
175
$object_types = $this->objectTypes;
176
if ($object_types) {
177
$object_types = array_fuse($object_types);
178
}
179
180
$results = array();
181
foreach ($queries as $query) {
182
$query_type = $query->getTemplateApplicationTransaction()
183
->getApplicationTransactionType();
184
185
if ($type_map) {
186
if (!isset($type_map[$query_type])) {
187
continue;
188
}
189
}
190
191
if ($object_types) {
192
if (!isset($object_types[$query_type])) {
193
continue;
194
}
195
}
196
197
$results[] = id(clone $query)
198
->setViewer($viewer)
199
->setParentQuery($this);
200
}
201
202
return $results;
203
}
204
205
protected function newExternalCursorStringForResult($object) {
206
return (string)$object->getPHID();
207
}
208
209
protected function applyExternalCursorConstraintsToQuery(
210
PhabricatorCursorPagedPolicyAwareQuery $subquery,
211
$cursor) {
212
$subquery->withPHIDs(array($cursor));
213
}
214
215
}
216
217