@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<?php
2
3abstract class PhabricatorApplicationTransactionQuery
4 extends PhabricatorCursorPagedPolicyAwareQuery {
5
6 private $ids;
7 private $phids;
8 private $objectPHIDs;
9 private $authorPHIDs;
10 private $transactionTypes;
11 private $withComments;
12 private $createdMin;
13 private $createdMax;
14 private $aggregatePagingCursor;
15
16 private $needComments = true;
17 private $needHandles = true;
18
19 final public static function newQueryForObject(
20 PhabricatorApplicationTransactionInterface $object) {
21
22 $xaction = $object->getApplicationTransactionTemplate();
23 $target_class = get_class($xaction);
24
25 $queries = id(new PhutilClassMapQuery())
26 ->setAncestorClass(self::class)
27 ->execute();
28 foreach ($queries as $query) {
29 $query_xaction = $query->getTemplateApplicationTransaction();
30 $query_class = get_class($query_xaction);
31
32 if ($query_class === $target_class) {
33 return id(clone $query);
34 }
35 }
36
37 return null;
38 }
39
40 abstract public function getTemplateApplicationTransaction();
41
42 public function withIDs(array $ids) {
43 $this->ids = $ids;
44 return $this;
45 }
46
47 public function withPHIDs(array $phids) {
48 $this->phids = $phids;
49 return $this;
50 }
51
52 public function withObjectPHIDs(array $object_phids) {
53 $this->objectPHIDs = $object_phids;
54 return $this;
55 }
56
57 public function withAuthorPHIDs(array $author_phids) {
58 $this->authorPHIDs = $author_phids;
59 return $this;
60 }
61
62 public function withTransactionTypes(array $transaction_types) {
63 $this->transactionTypes = $transaction_types;
64 return $this;
65 }
66
67 public function withComments($with_comments) {
68 $this->withComments = $with_comments;
69 return $this;
70 }
71
72 public function withDateCreatedBetween($min, $max) {
73 $this->createdMin = $min;
74 $this->createdMax = $max;
75 return $this;
76 }
77
78 public function needComments($need) {
79 $this->needComments = $need;
80 return $this;
81 }
82
83 public function needHandles($need) {
84 $this->needHandles = $need;
85 return $this;
86 }
87
88 public function setAggregatePagingCursor(PhabricatorQueryCursor $cursor) {
89 $this->aggregatePagingCursor = $cursor;
90 return $this;
91 }
92
93 public function getAggregatePagingCursor() {
94 return $this->aggregatePagingCursor;
95 }
96
97 protected function willExecute() {
98 $cursor_object = $this->getAggregatePagingCursor();
99 if ($cursor_object) {
100 $this->nextPage(array($cursor_object->getObject()));
101 }
102 }
103
104 protected function loadPage() {
105 $table = $this->getTemplateApplicationTransaction();
106
107 $xactions = $this->loadStandardPage($table);
108
109 foreach ($xactions as $xaction) {
110 $xaction->attachViewer($this->getViewer());
111 }
112
113 if ($this->needComments) {
114 $comment_phids = array_filter(mpull($xactions, 'getCommentPHID'));
115
116 $comments = array();
117 if ($comment_phids) {
118 $comments =
119 id(new PhabricatorApplicationTransactionTemplatedCommentQuery())
120 ->setTemplate($table->getApplicationTransactionCommentObject())
121 ->setViewer($this->getViewer())
122 ->withPHIDs($comment_phids)
123 ->execute();
124 $comments = mpull($comments, null, 'getPHID');
125 }
126
127 foreach ($xactions as $xaction) {
128 if ($xaction->getCommentPHID()) {
129 $comment = idx($comments, $xaction->getCommentPHID());
130 if ($comment) {
131 $xaction->attachComment($comment);
132 }
133 }
134 }
135 } else {
136 foreach ($xactions as $xaction) {
137 $xaction->setCommentNotLoaded(true);
138 }
139 }
140
141 return $xactions;
142 }
143
144 protected function willFilterPage(array $xactions) {
145 $object_phids = array_keys(mpull($xactions, null, 'getObjectPHID'));
146
147 $objects = id(new PhabricatorObjectQuery())
148 ->setViewer($this->getViewer())
149 ->setParentQuery($this)
150 ->withPHIDs($object_phids)
151 ->execute();
152
153 foreach ($xactions as $key => $xaction) {
154 $object_phid = $xaction->getObjectPHID();
155 if (empty($objects[$object_phid])) {
156 unset($xactions[$key]);
157 continue;
158 }
159 $xaction->attachObject($objects[$object_phid]);
160 }
161
162 // NOTE: We have to do this after loading objects, because the objects
163 // may help determine which handles are required (for example, in the case
164 // of custom fields).
165
166 if ($this->needHandles) {
167 $phids = array();
168 foreach ($xactions as $xaction) {
169 $phids[$xaction->getPHID()] = $xaction->getRequiredHandlePHIDs();
170 }
171 $handles = array();
172 $merged = array_mergev($phids);
173 if ($merged) {
174 $handles = $this->getViewer()->loadHandles($merged);
175 $handles = iterator_to_array($handles);
176 }
177 foreach ($xactions as $xaction) {
178 $xaction->setHandles(
179 array_select_keys(
180 $handles,
181 $phids[$xaction->getPHID()]));
182 }
183 }
184
185 return $xactions;
186 }
187
188 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
189 $where = parent::buildWhereClauseParts($conn);
190
191 if ($this->ids !== null) {
192 $where[] = qsprintf(
193 $conn,
194 'x.id IN (%Ld)',
195 $this->ids);
196 }
197
198 if ($this->phids !== null) {
199 $where[] = qsprintf(
200 $conn,
201 'x.phid IN (%Ls)',
202 $this->phids);
203 }
204
205 if ($this->objectPHIDs !== null) {
206 $where[] = qsprintf(
207 $conn,
208 'x.objectPHID IN (%Ls)',
209 $this->objectPHIDs);
210 }
211
212 if ($this->authorPHIDs !== null) {
213 $where[] = qsprintf(
214 $conn,
215 'x.authorPHID IN (%Ls)',
216 $this->authorPHIDs);
217 }
218
219 if ($this->transactionTypes !== null) {
220 $where[] = qsprintf(
221 $conn,
222 'x.transactionType IN (%Ls)',
223 $this->transactionTypes);
224 }
225
226 if ($this->withComments !== null) {
227 if (!$this->withComments) {
228 $where[] = qsprintf(
229 $conn,
230 'c.id IS NULL');
231 }
232 }
233
234 if ($this->createdMin !== null) {
235 $where[] = qsprintf(
236 $conn,
237 'x.dateCreated >= %d',
238 $this->createdMin);
239 }
240
241 if ($this->createdMax !== null) {
242 $where[] = qsprintf(
243 $conn,
244 'x.dateCreated <= %d',
245 $this->createdMax);
246 }
247
248 return $where;
249 }
250
251 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
252 $joins = parent::buildJoinClauseParts($conn);
253
254 if ($this->withComments !== null) {
255 $xaction = $this->getTemplateApplicationTransaction();
256 $comment = $xaction->getApplicationTransactionCommentObject();
257
258 // Not every transaction type has comments, so we may be able to
259 // implement this constraint trivially.
260
261 if (!$comment) {
262 if ($this->withComments) {
263 throw new PhabricatorEmptyQueryException();
264 } else {
265 // If we're querying for transactions with no comments and the
266 // transaction type does not support comments, we don't need to
267 // do anything.
268 }
269 } else {
270 if ($this->withComments) {
271 $joins[] = qsprintf(
272 $conn,
273 'JOIN %T c ON x.phid = c.transactionPHID',
274 $comment->getTableName());
275 } else {
276 $joins[] = qsprintf(
277 $conn,
278 'LEFT JOIN %T c ON x.phid = c.transactionPHID',
279 $comment->getTableName());
280 }
281 }
282 }
283
284 return $joins;
285 }
286
287 protected function shouldGroupQueryResultRows() {
288 if ($this->withComments !== null) {
289 return true;
290 }
291
292 return parent::shouldGroupQueryResultRows();
293 }
294
295 /**
296 * Get name of class of application queried by the query.
297 *
298 * To be overwritten by child classes when applicable.
299 *
300 * @return string|null Application class name
301 */
302 public function getQueryApplicationClass() {
303 return null;
304 }
305
306 protected function getPrimaryTableAlias() {
307 return 'x';
308 }
309
310 protected function newPagingMapFromPartialObject($object) {
311 return parent::newPagingMapFromPartialObject($object) + array(
312 'created' => $object->getDateCreated(),
313 'phid' => $object->getPHID(),
314 );
315 }
316
317 public function getBuiltinOrders() {
318 return parent::getBuiltinOrders() + array(
319 'global' => array(
320 'vector' => array('created', 'phid'),
321 'name' => pht('Global'),
322 ),
323 );
324 }
325
326 public function getOrderableColumns() {
327 return parent::getOrderableColumns() + array(
328 'created' => array(
329 'table' => 'x',
330 'column' => 'dateCreated',
331 'type' => 'int',
332 ),
333 'phid' => array(
334 'table' => 'x',
335 'column' => 'phid',
336 'type' => 'string',
337 'reverse' => true,
338 'unique' => true,
339 ),
340 );
341 }
342
343
344}