[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
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 }
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 |