@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
at upstream/main 344 lines 8.7 kB view raw
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}