[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/project/query/ -> PhabricatorProjectColumnPositionQuery.php (source)

   1  <?php
   2  
   3  final class PhabricatorProjectColumnPositionQuery
   4    extends PhabricatorCursorPagedPolicyAwareQuery {
   5  
   6    private $ids;
   7    private $boardPHIDs;
   8    private $objectPHIDs;
   9    private $columns;
  10  
  11    private $needColumns;
  12    private $skipImplicitCreate;
  13  
  14    public function withIDs(array $ids) {
  15      $this->ids = $ids;
  16      return $this;
  17    }
  18  
  19    public function withBoardPHIDs(array $board_phids) {
  20      $this->boardPHIDs = $board_phids;
  21      return $this;
  22    }
  23  
  24    public function withObjectPHIDs(array $object_phids) {
  25      $this->objectPHIDs = $object_phids;
  26      return $this;
  27    }
  28  
  29    /**
  30     * Find objects in specific columns.
  31     *
  32     * NOTE: Using this method activates logic which constructs virtual
  33     * column positions for objects not in any column, if you pass a default
  34     * column. Normally these results are not returned.
  35     *
  36     * @param list<PhabricatorProjectColumn> Columns to look for objects in.
  37     * @return this
  38     */
  39    public function withColumns(array $columns) {
  40      assert_instances_of($columns, 'PhabricatorProjectColumn');
  41      $this->columns = $columns;
  42      return $this;
  43    }
  44  
  45    public function needColumns($need_columns) {
  46      $this->needColumns = true;
  47      return $this;
  48    }
  49  
  50  
  51    /**
  52     * Skip implicit creation of column positions which are implied but do not
  53     * yet exist.
  54     *
  55     * This is primarily useful internally.
  56     *
  57     * @param bool  True to skip implicit creation of column positions.
  58     * @return this
  59     */
  60    public function setSkipImplicitCreate($skip) {
  61      $this->skipImplicitCreate = $skip;
  62      return $this;
  63    }
  64  
  65    // NOTE: For now, boards are always attached to projects. However, they might
  66    // not be in the future. This generalization just anticipates a future where
  67    // we let other types of objects (like users) have boards, or let boards
  68    // contain other types of objects.
  69  
  70    private function newPositionObject() {
  71      return new PhabricatorProjectColumnPosition();
  72    }
  73  
  74    private function newColumnQuery() {
  75      return new PhabricatorProjectColumnQuery();
  76    }
  77  
  78    private function getBoardMembershipEdgeTypes() {
  79      return array(
  80        PhabricatorProjectProjectHasObjectEdgeType::EDGECONST,
  81      );
  82    }
  83  
  84    private function getBoardMembershipPHIDTypes() {
  85      return array(
  86        ManiphestTaskPHIDType::TYPECONST,
  87      );
  88    }
  89  
  90    protected function loadPage() {
  91      $table = $this->newPositionObject();
  92      $conn_r = $table->establishConnection('r');
  93  
  94      // We're going to find results by combining two queries: one query finds
  95      // objects on a board column, while the other query finds objects not on
  96      // any board column and virtually puts them on the default column.
  97  
  98      $unions = array();
  99  
 100      // First, find all the stuff that's actually on a column.
 101  
 102      $unions[] = qsprintf(
 103        $conn_r,
 104        'SELECT * FROM %T %Q',
 105        $table->getTableName(),
 106        $this->buildWhereClause($conn_r));
 107  
 108      // If we have a default column, find all the stuff that's not in any
 109      // column and put it in the default column.
 110  
 111      $must_type_filter = false;
 112      if ($this->columns && !$this->skipImplicitCreate) {
 113        $default_map = array();
 114        foreach ($this->columns as $column) {
 115          if ($column->isDefaultColumn()) {
 116            $default_map[$column->getProjectPHID()] = $column->getPHID();
 117          }
 118        }
 119  
 120        if ($default_map) {
 121          $where = array();
 122  
 123          // Find the edges attached to the boards we have default columns for.
 124  
 125          $where[] = qsprintf(
 126            $conn_r,
 127            'e.src IN (%Ls)',
 128            array_keys($default_map));
 129  
 130          // Find only edges which describe a board relationship.
 131  
 132          $where[] = qsprintf(
 133            $conn_r,
 134            'e.type IN (%Ld)',
 135            $this->getBoardMembershipEdgeTypes());
 136  
 137          if ($this->boardPHIDs !== null) {
 138            // This should normally be redundant, but construct it anyway if
 139            // the caller has told us to.
 140            $where[] = qsprintf(
 141              $conn_r,
 142              'e.src IN (%Ls)',
 143              $this->boardPHIDs);
 144          }
 145  
 146          if ($this->objectPHIDs !== null) {
 147            $where[] = qsprintf(
 148              $conn_r,
 149              'e.dst IN (%Ls)',
 150              $this->objectPHIDs);
 151          }
 152  
 153          $where[] = qsprintf(
 154            $conn_r,
 155            'p.id IS NULL');
 156  
 157          $where = $this->formatWhereClause($where);
 158  
 159          $unions[] = qsprintf(
 160            $conn_r,
 161            'SELECT NULL id, e.src boardPHID, NULL columnPHID, e.dst objectPHID,
 162                0 sequence
 163              FROM %T e LEFT JOIN %T p
 164                ON e.src = p.boardPHID AND e.dst = p.objectPHID
 165                %Q',
 166            PhabricatorEdgeConfig::TABLE_NAME_EDGE,
 167            $table->getTableName(),
 168            $where);
 169  
 170          $must_type_filter = true;
 171        }
 172      }
 173  
 174      $data = queryfx_all(
 175        $conn_r,
 176        '%Q %Q %Q',
 177        implode(' UNION ALL ', $unions),
 178        $this->buildOrderClause($conn_r),
 179        $this->buildLimitClause($conn_r));
 180  
 181      // If we've picked up objects not in any column, we need to filter out any
 182      // matched objects which have the wrong edge type.
 183      if ($must_type_filter) {
 184        $allowed_types = array_fuse($this->getBoardMembershipPHIDTypes());
 185        foreach ($data as $id => $row) {
 186          if ($row['columnPHID'] === null) {
 187            $object_phid = $row['objectPHID'];
 188            if (empty($allowed_types[phid_get_type($object_phid)])) {
 189              unset($data[$id]);
 190            }
 191          }
 192        }
 193      }
 194  
 195      $positions = $table->loadAllFromArray($data);
 196  
 197      // Find the implied positions which don't exist yet. If there are any,
 198      // we're going to create them.
 199      $create = array();
 200      foreach ($positions as $position) {
 201        if ($position->getColumnPHID() === null) {
 202          $column_phid = idx($default_map, $position->getBoardPHID());
 203          $position->setColumnPHID($column_phid);
 204  
 205          $create[] = $position;
 206        }
 207      }
 208  
 209      if ($create) {
 210        // If we're adding several objects to a column, insert the column
 211        // position objects in object ID order. This means that newly added
 212        // objects float to the top, and when a group of newly added objects
 213        // float up at the same time, the most recently created ones end up
 214        // highest in the list.
 215  
 216        $objects = id(new PhabricatorObjectQuery())
 217          ->setViewer(PhabricatorUser::getOmnipotentUser())
 218          ->withPHIDs(mpull($create, 'getObjectPHID'))
 219          ->execute();
 220        $objects = mpull($objects, null, 'getPHID');
 221        $objects = msort($objects, 'getID');
 222  
 223        $create = mgroup($create, 'getObjectPHID');
 224        $create = array_select_keys($create, array_keys($objects)) + $create;
 225  
 226        $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
 227  
 228          foreach ($create as $object_phid => $create_positions) {
 229            foreach ($create_positions as $create_position) {
 230              $create_position->save();
 231            }
 232          }
 233  
 234        unset($unguarded);
 235      }
 236  
 237      return $positions;
 238    }
 239  
 240    protected function willFilterPage(array $page) {
 241  
 242      if ($this->needColumns) {
 243        $column_phids = mpull($page, 'getColumnPHID');
 244        $columns = $this->newColumnQuery()
 245          ->setParentQuery($this)
 246          ->setViewer($this->getViewer())
 247          ->withPHIDs($column_phids)
 248          ->execute();
 249        $columns = mpull($columns, null, 'getPHID');
 250  
 251        foreach ($page as $key => $position) {
 252          $column = idx($columns, $position->getColumnPHID());
 253          if (!$column) {
 254            unset($page[$key]);
 255            continue;
 256          }
 257  
 258          $position->attachColumn($column);
 259        }
 260      }
 261  
 262      return $page;
 263    }
 264  
 265    private function buildWhereClause($conn_r) {
 266      $where = array();
 267  
 268      if ($this->ids !== null) {
 269        $where[] = qsprintf(
 270          $conn_r,
 271          'id IN (%Ld)',
 272          $this->ids);
 273      }
 274  
 275      if ($this->boardPHIDs !== null) {
 276        $where[] = qsprintf(
 277          $conn_r,
 278          'boardPHID IN (%Ls)',
 279          $this->boardPHIDs);
 280      }
 281  
 282      if ($this->objectPHIDs !== null) {
 283        $where[] = qsprintf(
 284          $conn_r,
 285          'objectPHID IN (%Ls)',
 286          $this->objectPHIDs);
 287      }
 288  
 289      if ($this->columns !== null) {
 290        $where[] = qsprintf(
 291          $conn_r,
 292          'columnPHID IN (%Ls)',
 293          mpull($this->columns, 'getPHID'));
 294      }
 295  
 296      // NOTE: Explicitly not building the paging clause here, since it won't
 297      // work with the UNION.
 298  
 299      return $this->formatWhereClause($where);
 300    }
 301  
 302    public function getQueryApplicationClass() {
 303      return 'PhabricatorProjectApplication';
 304    }
 305  
 306  }


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