*/ final class DifferentialChangesetQuery extends PhabricatorCursorPagedPolicyAwareQuery { private $ids; private $phids; private $diffPHIDs; private $diffs; private $needAttachToDiffs; private $needHunks; public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } /** * @param array $diffs */ public function withDiffs(array $diffs) { assert_instances_of($diffs, DifferentialDiff::class); $this->diffs = $diffs; return $this; } public function withDiffPHIDs(array $phids) { $this->diffPHIDs = $phids; return $this; } public function needAttachToDiffs($attach) { $this->needAttachToDiffs = $attach; return $this; } public function needHunks($need) { $this->needHunks = $need; return $this; } protected function willExecute() { // If we fail to load any changesets (which is possible in the case of an // empty commit) we'll never call didFilterPage(). Attach empty changeset // lists now so that we end up with the right result. if ($this->needAttachToDiffs) { foreach ($this->diffs as $diff) { $diff->attachChangesets(array()); } } } public function newResultObject() { return new DifferentialChangeset(); } protected function willFilterPage(array $changesets) { // First, attach all the diffs we already have. We can just do this // directly without worrying about querying for them. When we don't have // a diff, record that we need to load it. if ($this->diffs) { $have_diffs = mpull($this->diffs, null, 'getID'); } else { $have_diffs = array(); } $must_load = array(); foreach ($changesets as $key => $changeset) { $diff_id = $changeset->getDiffID(); if (isset($have_diffs[$diff_id])) { $changeset->attachDiff($have_diffs[$diff_id]); } else { $must_load[$key] = $changeset; } } // Load all the diffs we don't have. $need_diff_ids = mpull($must_load, 'getDiffID'); $more_diffs = array(); if ($need_diff_ids) { $more_diffs = id(new DifferentialDiffQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withIDs($need_diff_ids) ->execute(); $more_diffs = mpull($more_diffs, null, 'getID'); } // Attach the diffs we loaded. foreach ($must_load as $key => $changeset) { $diff_id = $changeset->getDiffID(); if (isset($more_diffs[$diff_id])) { $changeset->attachDiff($more_diffs[$diff_id]); } else { // We didn't have the diff, and could not load it (it does not exist, // or we can't see it), so filter this result out. unset($changesets[$key]); } } return $changesets; } protected function didFilterPage(array $changesets) { if ($this->needAttachToDiffs) { $changeset_groups = mgroup($changesets, 'getDiffID'); foreach ($this->diffs as $diff) { $diff_changesets = idx($changeset_groups, $diff->getID(), array()); $diff->attachChangesets($diff_changesets); } } if ($this->needHunks) { id(new DifferentialHunkQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withChangesets($changesets) ->needAttachToChangesets(true) ->execute(); } return $changesets; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->diffs !== null) { $where[] = qsprintf( $conn, 'diffID IN (%Ld)', mpull($this->diffs, 'getID')); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->diffPHIDs !== null) { $diff_ids = queryfx_all( $conn, 'SELECT id FROM %R WHERE phid IN (%Ls)', new DifferentialDiff(), $this->diffPHIDs); $diff_ids = ipull($diff_ids, 'id', null); if (!$diff_ids) { throw new PhabricatorEmptyQueryException(); } $where[] = qsprintf( $conn, 'diffID IN (%Ld)', $diff_ids); } return $where; } public function getQueryApplicationClass() { return PhabricatorDifferentialApplication::class; } }