[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/repository/query/ -> PhabricatorRepositoryQuery.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1