[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/infrastructure/edges/query/ -> PhabricatorEdgeQuery.php (source)

   1  <?php
   2  
   3  /**
   4   * Load object edges created by @{class:PhabricatorEdgeEditor}.
   5   *
   6   *   name=Querying Edges
   7   *   $src  = $earth_phid;
   8   *   $type = PhabricatorEdgeConfig::TYPE_BODY_HAS_SATELLITE;
   9   *
  10   *   // Load the earth's satellites.
  11   *   $satellite_edges = id(new PhabricatorEdgeQuery())
  12   *     ->withSourcePHIDs(array($src))
  13   *     ->withEdgeTypes(array($type))
  14   *     ->execute();
  15   *
  16   * For more information on edges, see @{article:Using Edges}.
  17   *
  18   * @task config   Configuring the Query
  19   * @task exec     Executing the Query
  20   * @task internal Internal
  21   */
  22  final class PhabricatorEdgeQuery extends PhabricatorQuery {
  23  
  24    private $sourcePHIDs;
  25    private $destPHIDs;
  26    private $edgeTypes;
  27    private $resultSet;
  28  
  29    const ORDER_OLDEST_FIRST = 'order:oldest';
  30    const ORDER_NEWEST_FIRST = 'order:newest';
  31    private $order = self::ORDER_NEWEST_FIRST;
  32  
  33    private $needEdgeData;
  34  
  35  
  36  /* -(  Configuring the Query  )---------------------------------------------- */
  37  
  38  
  39    /**
  40     * Find edges originating at one or more source PHIDs. You MUST provide this
  41     * to execute an edge query.
  42     *
  43     * @param list List of source PHIDs.
  44     * @return this
  45     *
  46     * @task config
  47     */
  48    public function withSourcePHIDs(array $source_phids) {
  49      $this->sourcePHIDs = $source_phids;
  50      return $this;
  51    }
  52  
  53  
  54    /**
  55     * Find edges terminating at one or more destination PHIDs.
  56     *
  57     * @param list List of destination PHIDs.
  58     * @return this
  59     *
  60     */
  61    public function withDestinationPHIDs(array $dest_phids) {
  62      $this->destPHIDs = $dest_phids;
  63      return $this;
  64    }
  65  
  66  
  67    /**
  68     * Find edges of specific types.
  69     *
  70     * @param list List of PhabricatorEdgeConfig type constants.
  71     * @return this
  72     *
  73     * @task config
  74     */
  75    public function withEdgeTypes(array $types) {
  76      $this->edgeTypes = $types;
  77      return $this;
  78    }
  79  
  80  
  81    /**
  82     * Configure the order edge results are returned in.
  83     *
  84     * @param const Order constant.
  85     * @return this
  86     *
  87     * @task config
  88     */
  89    public function setOrder($order) {
  90      $this->order = $order;
  91      return $this;
  92    }
  93  
  94  
  95    /**
  96     * When loading edges, also load edge data.
  97     *
  98     * @param bool True to load edge data.
  99     * @return this
 100     *
 101     * @task config
 102     */
 103    public function needEdgeData($need) {
 104      $this->needEdgeData = $need;
 105      return $this;
 106    }
 107  
 108  
 109  /* -(  Executing the Query  )------------------------------------------------ */
 110  
 111  
 112    /**
 113     * Convenience method for loading destination PHIDs with one source and one
 114     * edge type. Equivalent to building a full query, but simplifies a common
 115     * use case.
 116     *
 117     * @param phid  Source PHID.
 118     * @param const Edge type.
 119     * @return list<phid> List of destination PHIDs.
 120     */
 121    public static function loadDestinationPHIDs($src_phid, $edge_type) {
 122      $edges = id(new PhabricatorEdgeQuery())
 123        ->withSourcePHIDs(array($src_phid))
 124        ->withEdgeTypes(array($edge_type))
 125        ->execute();
 126      return array_keys($edges[$src_phid][$edge_type]);
 127    }
 128  
 129    /**
 130     * Convenience method for loading a single edge's metadata for
 131     * a given source, destination, and edge type. Returns null
 132     * if the edge does not exist or does not have metadata. Builds
 133     * and immediately executes a full query.
 134     *
 135     * @param phid  Source PHID.
 136     * @param const Edge type.
 137     * @param phid  Destination PHID.
 138     * @return wild Edge annotation (or null).
 139     */
 140    public static function loadSingleEdgeData($src_phid, $edge_type, $dest_phid) {
 141      $edges = id(new PhabricatorEdgeQuery())
 142        ->withSourcePHIDs(array($src_phid))
 143        ->withEdgeTypes(array($edge_type))
 144        ->withDestinationPHIDs(array($dest_phid))
 145        ->needEdgeData(true)
 146        ->execute();
 147  
 148      if (isset($edges[$src_phid][$edge_type][$dest_phid]['data'])) {
 149        return $edges[$src_phid][$edge_type][$dest_phid]['data'];
 150      }
 151      return null;
 152    }
 153  
 154  
 155    /**
 156     * Load specified edges.
 157     *
 158     * @task exec
 159     */
 160    public function execute() {
 161      if (!$this->sourcePHIDs) {
 162        throw new Exception(
 163        'You must use withSourcePHIDs() to query edges.');
 164      }
 165  
 166      $sources = phid_group_by_type($this->sourcePHIDs);
 167  
 168      $result = array();
 169  
 170      // When a query specifies types, make sure we return data for all queried
 171      // types.
 172      if ($this->edgeTypes) {
 173        foreach ($this->sourcePHIDs as $phid) {
 174          foreach ($this->edgeTypes as $type) {
 175            $result[$phid][$type] = array();
 176          }
 177        }
 178      }
 179  
 180      foreach ($sources as $type => $phids) {
 181        $conn_r = PhabricatorEdgeConfig::establishConnection($type, 'r');
 182  
 183        $where = $this->buildWhereClause($conn_r);
 184        $order = $this->buildOrderClause($conn_r);
 185  
 186        $edges = queryfx_all(
 187          $conn_r,
 188          'SELECT edge.* FROM %T edge %Q %Q',
 189          PhabricatorEdgeConfig::TABLE_NAME_EDGE,
 190          $where,
 191          $order);
 192  
 193        if ($this->needEdgeData) {
 194          $data_ids = array_filter(ipull($edges, 'dataID'));
 195          $data_map = array();
 196          if ($data_ids) {
 197            $data_rows = queryfx_all(
 198              $conn_r,
 199              'SELECT edgedata.* FROM %T edgedata WHERE id IN (%Ld)',
 200              PhabricatorEdgeConfig::TABLE_NAME_EDGEDATA,
 201              $data_ids);
 202            foreach ($data_rows as $row) {
 203              $data_map[$row['id']] = idx(
 204                json_decode($row['data'], true),
 205                'data');
 206            }
 207          }
 208          foreach ($edges as $key => $edge) {
 209            $edges[$key]['data'] = idx($data_map, $edge['dataID'], array());
 210          }
 211        }
 212  
 213        foreach ($edges as $edge) {
 214          $result[$edge['src']][$edge['type']][$edge['dst']] = $edge;
 215        }
 216      }
 217  
 218      $this->resultSet = $result;
 219      return $result;
 220    }
 221  
 222  
 223    /**
 224     * Convenience function for selecting edge destination PHIDs after calling
 225     * execute().
 226     *
 227     * Returns a flat list of PHIDs matching the provided source PHID and type
 228     * filters. By default, the filters are empty so all PHIDs will be returned.
 229     * For example, if you're doing a batch query from several sources, you might
 230     * write code like this:
 231     *
 232     *   $query = new PhabricatorEdgeQuery();
 233     *   $query->setViewer($viewer);
 234     *   $query->withSourcePHIDs(mpull($objects, 'getPHID'));
 235     *   $query->withEdgeTypes(array($some_type));
 236     *   $query->execute();
 237     *
 238     *   // Gets all of the destinations.
 239     *   $all_phids = $query->getDestinationPHIDs();
 240     *   $handles = id(new PhabricatorHandleQuery())
 241     *     ->setViewer($viewer)
 242     *     ->withPHIDs($all_phids)
 243     *     ->execute();
 244     *
 245     *   foreach ($objects as $object) {
 246     *     // Get all of the destinations for the given object.
 247     *     $dst_phids = $query->getDestinationPHIDs(array($object->getPHID()));
 248     *     $object->attachHandles(array_select_keys($handles, $dst_phids));
 249     *   }
 250     *
 251     * @param list? List of PHIDs to select, or empty to select all.
 252     * @param list? List of edge types to select, or empty to select all.
 253     * @return list<phid> List of matching destination PHIDs.
 254     */
 255    public function getDestinationPHIDs(
 256      array $src_phids = array(),
 257      array $types = array()) {
 258      if ($this->resultSet === null) {
 259        throw new Exception(
 260          'You must execute() a query before you you can getDestinationPHIDs().');
 261      }
 262  
 263      $src_phids = array_fill_keys($src_phids, true);
 264      $types = array_fill_keys($types, true);
 265  
 266      $result_phids = array();
 267      foreach ($this->resultSet as $src => $edges_by_type) {
 268        if ($src_phids && empty($src_phids[$src])) {
 269          continue;
 270        }
 271        foreach ($edges_by_type as $type => $edges_by_dst) {
 272          if ($types && empty($types[$type])) {
 273            continue;
 274          }
 275          foreach ($edges_by_dst as $dst => $edge) {
 276            $result_phids[$dst] = true;
 277          }
 278        }
 279      }
 280  
 281      return array_keys($result_phids);
 282    }
 283  
 284  
 285  /* -(  Internals  )---------------------------------------------------------- */
 286  
 287  
 288    /**
 289     * @task internal
 290     */
 291    private function buildWhereClause($conn_r) {
 292      $where = array();
 293  
 294      if ($this->sourcePHIDs) {
 295        $where[] = qsprintf(
 296          $conn_r,
 297          'edge.src IN (%Ls)',
 298          $this->sourcePHIDs);
 299      }
 300  
 301      if ($this->edgeTypes) {
 302        $where[] = qsprintf(
 303          $conn_r,
 304          'edge.type IN (%Ls)',
 305          $this->edgeTypes);
 306      }
 307  
 308      if ($this->destPHIDs) {
 309        // potentially complain if $this->edgeType was not set
 310        $where[] = qsprintf(
 311          $conn_r,
 312          'edge.dst IN (%Ls)',
 313          $this->destPHIDs);
 314      }
 315  
 316      return $this->formatWhereClause($where);
 317    }
 318  
 319  
 320    /**
 321     * @task internal
 322     */
 323    private function buildOrderClause($conn_r) {
 324      if ($this->order == self::ORDER_NEWEST_FIRST) {
 325        return 'ORDER BY edge.dateCreated DESC, edge.seq DESC';
 326      } else {
 327        return 'ORDER BY edge.dateCreated ASC, edge.seq ASC';
 328      }
 329    }
 330  
 331  }


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