[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhabricatorRepositoryQuery 4 extends PhabricatorCursorPagedPolicyAwareQuery { 5 6 private $ids; 7 private $phids; 8 private $callsigns; 9 private $types; 10 private $uuids; 11 private $nameContains; 12 private $remoteURIs; 13 private $anyProjectPHIDs; 14 15 const STATUS_OPEN = 'status-open'; 16 const STATUS_CLOSED = 'status-closed'; 17 const STATUS_ALL = 'status-all'; 18 private $status = self::STATUS_ALL; 19 20 const ORDER_CREATED = 'order-created'; 21 const ORDER_COMMITTED = 'order-committed'; 22 const ORDER_CALLSIGN = 'order-callsign'; 23 const ORDER_NAME = 'order-name'; 24 private $order = self::ORDER_CREATED; 25 26 const HOSTED_PHABRICATOR = 'hosted-phab'; 27 const HOSTED_REMOTE = 'hosted-remote'; 28 const HOSTED_ALL = 'hosted-all'; 29 private $hosted = self::HOSTED_ALL; 30 31 private $needMostRecentCommits; 32 private $needCommitCounts; 33 private $needProjectPHIDs; 34 35 public function withIDs(array $ids) { 36 $this->ids = $ids; 37 return $this; 38 } 39 40 public function withPHIDs(array $phids) { 41 $this->phids = $phids; 42 return $this; 43 } 44 45 public function withCallsigns(array $callsigns) { 46 $this->callsigns = $callsigns; 47 return $this; 48 } 49 50 public function withStatus($status) { 51 $this->status = $status; 52 return $this; 53 } 54 55 public function withHosted($hosted) { 56 $this->hosted = $hosted; 57 return $this; 58 } 59 60 public function withTypes(array $types) { 61 $this->types = $types; 62 return $this; 63 } 64 65 public function withUUIDs(array $uuids) { 66 $this->uuids = $uuids; 67 return $this; 68 } 69 70 public function withNameContains($contains) { 71 $this->nameContains = $contains; 72 return $this; 73 } 74 75 public function withRemoteURIs(array $uris) { 76 $this->remoteURIs = $uris; 77 return $this; 78 } 79 80 public function withAnyProjects(array $projects) { 81 $this->anyProjectPHIDs = $projects; 82 return $this; 83 } 84 85 public function needCommitCounts($need_counts) { 86 $this->needCommitCounts = $need_counts; 87 return $this; 88 } 89 90 public function needMostRecentCommits($need_commits) { 91 $this->needMostRecentCommits = $need_commits; 92 return $this; 93 } 94 95 public function needProjectPHIDs($need_phids) { 96 $this->needProjectPHIDs = $need_phids; 97 return $this; 98 } 99 100 public function setOrder($order) { 101 $this->order = $order; 102 return $this; 103 } 104 105 protected function loadPage() { 106 $table = new PhabricatorRepository(); 107 $conn_r = $table->establishConnection('r'); 108 109 $data = queryfx_all( 110 $conn_r, 111 'SELECT * FROM %T r %Q %Q %Q %Q', 112 $table->getTableName(), 113 $this->buildJoinsClause($conn_r), 114 $this->buildWhereClause($conn_r), 115 $this->buildOrderClause($conn_r), 116 $this->buildLimitClause($conn_r)); 117 118 $repositories = $table->loadAllFromArray($data); 119 120 if ($this->needCommitCounts) { 121 $sizes = ipull($data, 'size', 'id'); 122 foreach ($repositories as $id => $repository) { 123 $repository->attachCommitCount(nonempty($sizes[$id], 0)); 124 } 125 } 126 127 if ($this->needMostRecentCommits) { 128 $commit_ids = ipull($data, 'lastCommitID', 'id'); 129 $commit_ids = array_filter($commit_ids); 130 if ($commit_ids) { 131 $commits = id(new DiffusionCommitQuery()) 132 ->setViewer($this->getViewer()) 133 ->withIDs($commit_ids) 134 ->execute(); 135 } else { 136 $commits = array(); 137 } 138 foreach ($repositories as $id => $repository) { 139 $commit = null; 140 if (idx($commit_ids, $id)) { 141 $commit = idx($commits, $commit_ids[$id]); 142 } 143 $repository->attachMostRecentCommit($commit); 144 } 145 } 146 147 return $repositories; 148 } 149 150 public function willFilterPage(array $repositories) { 151 assert_instances_of($repositories, 'PhabricatorRepository'); 152 153 // TODO: Denormalize repository status into the PhabricatorRepository 154 // table so we can do this filtering in the database. 155 foreach ($repositories as $key => $repo) { 156 $status = $this->status; 157 switch ($status) { 158 case self::STATUS_OPEN: 159 if (!$repo->isTracked()) { 160 unset($repositories[$key]); 161 } 162 break; 163 case self::STATUS_CLOSED: 164 if ($repo->isTracked()) { 165 unset($repositories[$key]); 166 } 167 break; 168 case self::STATUS_ALL: 169 break; 170 default: 171 throw new Exception("Unknown status '{$status}'!"); 172 } 173 174 // TODO: This should also be denormalized. 175 $hosted = $this->hosted; 176 switch ($hosted) { 177 case self::HOSTED_PHABRICATOR: 178 if (!$repo->isHosted()) { 179 unset($repositories[$key]); 180 } 181 break; 182 case self::HOSTED_REMOTE: 183 if ($repo->isHosted()) { 184 unset($repositories[$key]); 185 } 186 break; 187 case self::HOSTED_ALL: 188 break; 189 default: 190 throw new Exception("Uknown hosted failed '$hosted}'!"); 191 } 192 } 193 194 // TODO: Denormalize this, too. 195 if ($this->remoteURIs) { 196 $try_uris = $this->getNormalizedPaths(); 197 $try_uris = array_fuse($try_uris); 198 foreach ($repositories as $key => $repository) { 199 if (!isset($try_uris[$repository->getNormalizedPath()])) { 200 unset($repositories[$key]); 201 } 202 } 203 } 204 205 return $repositories; 206 } 207 208 public function didFilterPage(array $repositories) { 209 if ($this->needProjectPHIDs) { 210 $type_project = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; 211 212 $edge_query = id(new PhabricatorEdgeQuery()) 213 ->withSourcePHIDs(mpull($repositories, 'getPHID')) 214 ->withEdgeTypes(array($type_project)); 215 $edge_query->execute(); 216 217 foreach ($repositories as $repository) { 218 $project_phids = $edge_query->getDestinationPHIDs( 219 array( 220 $repository->getPHID(), 221 )); 222 $repository->attachProjectPHIDs($project_phids); 223 } 224 } 225 226 return $repositories; 227 } 228 229 public function getReversePaging() { 230 switch ($this->order) { 231 case self::ORDER_CALLSIGN: 232 case self::ORDER_NAME: 233 return true; 234 } 235 return false; 236 } 237 238 protected function getPagingColumn() { 239 $order = $this->order; 240 switch ($order) { 241 case self::ORDER_CREATED: 242 return 'r.id'; 243 case self::ORDER_COMMITTED: 244 return 's.epoch'; 245 case self::ORDER_CALLSIGN: 246 return 'r.callsign'; 247 case self::ORDER_NAME: 248 return 'r.name'; 249 default: 250 throw new Exception("Unknown order '{$order}!'"); 251 } 252 } 253 254 private function loadCursorObject($id) { 255 $query = id(new PhabricatorRepositoryQuery()) 256 ->setViewer($this->getPagingViewer()) 257 ->withIDs(array((int)$id)); 258 259 if ($this->order == self::ORDER_COMMITTED) { 260 $query->needMostRecentCommits(true); 261 } 262 263 $results = $query->execute(); 264 return head($results); 265 } 266 267 protected function buildPagingClause(AphrontDatabaseConnection $conn_r) { 268 $default = parent::buildPagingClause($conn_r); 269 270 $before_id = $this->getBeforeID(); 271 $after_id = $this->getAfterID(); 272 273 if (!$before_id && !$after_id) { 274 return $default; 275 } 276 277 $order = $this->order; 278 if ($order == self::ORDER_CREATED) { 279 return $default; 280 } 281 282 if ($before_id) { 283 $cursor = $this->loadCursorObject($before_id); 284 } else { 285 $cursor = $this->loadCursorObject($after_id); 286 } 287 288 if (!$cursor) { 289 return null; 290 } 291 292 $id_column = array( 293 'name' => 'r.id', 294 'type' => 'int', 295 'value' => $cursor->getID(), 296 ); 297 298 $columns = array(); 299 switch ($order) { 300 case self::ORDER_COMMITTED: 301 $commit = $cursor->getMostRecentCommit(); 302 if (!$commit) { 303 return null; 304 } 305 $columns[] = array( 306 'name' => 's.epoch', 307 'type' => 'int', 308 'value' => $commit->getEpoch(), 309 ); 310 $columns[] = $id_column; 311 break; 312 case self::ORDER_CALLSIGN: 313 $columns[] = array( 314 'name' => 'r.callsign', 315 'type' => 'string', 316 'value' => $cursor->getCallsign(), 317 'reverse' => true, 318 ); 319 break; 320 case self::ORDER_NAME: 321 $columns[] = array( 322 'name' => 'r.name', 323 'type' => 'string', 324 'value' => $cursor->getName(), 325 'reverse' => true, 326 ); 327 $columns[] = $id_column; 328 break; 329 default: 330 throw new Exception("Unknown order '{$order}'!"); 331 } 332 333 return $this->buildPagingClauseFromMultipleColumns( 334 $conn_r, 335 $columns, 336 array( 337 // TODO: Clean up the column ordering stuff and then make this 338 // depend on getReversePaging(). 339 'reversed' => (bool)($before_id), 340 )); 341 } 342 343 private function buildJoinsClause(AphrontDatabaseConnection $conn_r) { 344 $joins = array(); 345 346 $join_summary_table = $this->needCommitCounts || 347 $this->needMostRecentCommits || 348 ($this->order == self::ORDER_COMMITTED); 349 350 if ($join_summary_table) { 351 $joins[] = qsprintf( 352 $conn_r, 353 'LEFT JOIN %T s ON r.id = s.repositoryID', 354 PhabricatorRepository::TABLE_SUMMARY); 355 } 356 357 if ($this->anyProjectPHIDs) { 358 $joins[] = qsprintf( 359 $conn_r, 360 'JOIN edge e ON e.src = r.phid'); 361 } 362 363 return implode(' ', $joins); 364 } 365 366 private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 367 $where = array(); 368 369 if ($this->ids) { 370 $where[] = qsprintf( 371 $conn_r, 372 'r.id IN (%Ld)', 373 $this->ids); 374 } 375 376 if ($this->phids) { 377 $where[] = qsprintf( 378 $conn_r, 379 'r.phid IN (%Ls)', 380 $this->phids); 381 } 382 383 if ($this->callsigns) { 384 $where[] = qsprintf( 385 $conn_r, 386 'r.callsign IN (%Ls)', 387 $this->callsigns); 388 } 389 390 if ($this->types) { 391 $where[] = qsprintf( 392 $conn_r, 393 'r.versionControlSystem IN (%Ls)', 394 $this->types); 395 } 396 397 if ($this->uuids) { 398 $where[] = qsprintf( 399 $conn_r, 400 'r.uuid IN (%Ls)', 401 $this->uuids); 402 } 403 404 if (strlen($this->nameContains)) { 405 $where[] = qsprintf( 406 $conn_r, 407 'name LIKE %~', 408 $this->nameContains); 409 } 410 411 if ($this->anyProjectPHIDs) { 412 $where[] = qsprintf( 413 $conn_r, 414 'e.dst IN (%Ls)', 415 $this->anyProjectPHIDs); 416 } 417 418 $where[] = $this->buildPagingClause($conn_r); 419 420 return $this->formatWhereClause($where); 421 } 422 423 424 public function getQueryApplicationClass() { 425 return 'PhabricatorDiffusionApplication'; 426 } 427 428 private function getNormalizedPaths() { 429 $normalized_uris = array(); 430 431 // Since we don't know which type of repository this URI is in the general 432 // case, just generate all the normalizations. We could refine this in some 433 // cases: if the query specifies VCS types, or the URI is a git-style URI 434 // or an `svn+ssh` URI, we could deduce how to normalize it. However, this 435 // would be more complicated and it's not clear if it matters in practice. 436 437 foreach ($this->remoteURIs as $uri) { 438 $normalized_uris[] = new PhabricatorRepositoryURINormalizer( 439 PhabricatorRepositoryURINormalizer::TYPE_GIT, 440 $uri); 441 $normalized_uris[] = new PhabricatorRepositoryURINormalizer( 442 PhabricatorRepositoryURINormalizer::TYPE_SVN, 443 $uri); 444 $normalized_uris[] = new PhabricatorRepositoryURINormalizer( 445 PhabricatorRepositoryURINormalizer::TYPE_MERCURIAL, 446 $uri); 447 } 448 449 return array_unique(mpull($normalized_uris, 'getNormalizedPath')); 450 } 451 452 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |