Elgg  Version master
Relationships.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
5 use Doctrine\DBAL\Query\Expression\CompositeExpression;
12 
20 class Relationships extends Repository {
21 
26  public function calculate($function, $property, $property_type = null) {
27  if (!in_array(strtolower($function), QueryBuilder::CALCULATIONS)) {
28  throw new DomainException("'{$function}' is not a valid numeric function");
29  }
30 
31  if (!isset($property_type)) {
32  if (in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
33  $property_type = 'attribute';
34  } else {
35  $property_type = 'metadata';
36  }
37  }
38 
39  $join_column = $this->getJoinColumn();
40 
42 
43  switch ($property_type) {
44  case 'attribute':
45  if (!in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
46  throw new DomainException("'{$property}' is not a valid attribute");
47  }
48 
49  $alias = $select->joinEntitiesTable($select->getTableAlias(), $join_column, 'inner', 'e');
50  $select->select("{$function}({$alias}.{$property}) AS calculation");
51  break;
52 
53  case 'annotation':
55  if (!empty($this->options->annotation_name_value_pairs) && $this->options->annotation_name_value_pairs[0]->names != $property) {
56  $alias = $select->joinAnnotationTable($select->getTableAlias(), $join_column, $property);
57  }
58 
59  $select->select("{$function}({$alias}.value) AS calculation");
60  break;
61 
62  case 'metadata':
63  $alias = $select->joinMetadataTable($select->getTableAlias(), $join_column, $property);
64  $select->select("{$function}({$alias}.value) AS calculation");
65  break;
66  }
67 
68  $select = $this->buildQuery($select);
69 
70  $result = _elgg_services()->db->getDataRow($select);
71 
72  return $result ? (int) $result->calculation : 0;
73  }
74 
78  public function count() {
80 
81  $count_expr = $this->options->distinct ? "DISTINCT {$select->getTableAlias()}.id" : '*';
82  $select->select("COUNT({$count_expr}) AS total");
83 
84  $select = $this->buildQuery($select);
85 
86  $result = _elgg_services()->db->getDataRow($select);
87  if (empty($result)) {
88  return 0;
89  }
90 
91  return (int) $result->total;
92  }
93 
97  public function execute() {
98  if ($this->options->annotation_calculation) {
99  $clauses = $this->options->annotation_name_value_pairs;
100  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
101  throw new LogicException('Annotation calculation can not be performed on multiple annotation name value pairs merged with AND');
102  }
103 
104  $clause = array_shift($clauses);
105 
106  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
107  } elseif ($this->options->metadata_calculation) {
108  $clauses = $this->options->metadata_name_value_pairs;
109  if (count($clauses) > 1 && $this->options->metadata_name_value_pairs_operator !== 'OR') {
110  throw new LogicException('Metadata calculation can not be performed on multiple metadata name value pairs merged with AND');
111  }
112 
113  $clause = array_shift($clauses);
114 
115  return $this->calculate($this->options->metadata_calculation, $clause->names, 'metadata');
116  } elseif ($this->options->count) {
117  return $this->count();
118  } elseif ($this->options->batch) {
119  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
120  } else {
121  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
122  }
123  }
124 
128  public function get($limit = null, $offset = null, $callback = null) {
130 
131  $distinct = $this->options->distinct ? 'DISTINCT ' : '';
132  $select->select("{$distinct}{$select->getTableAlias()}.*");
133 
134  $this->expandInto($select, $select->getTableAlias());
135 
136  $select = $this->buildQuery($select);
137 
138  // add default ordering
139  $original_order = elgg_extract('order_by', $this->options->__original_options);
140  if (empty($this->options->order_by) && $original_order !== false) {
141  $select->addOrderBy("{$select->getTableAlias()}.time_created", 'desc');
142  $select->addOrderBy("{$select->getTableAlias()}.id", 'desc');
143  }
144 
145  if ($limit > 0) {
146  $select->setMaxResults((int) $limit);
147  $select->setFirstResult((int) $offset);
148  }
149 
150  $callback = $callback ?: $this->options->callback;
151  if (!isset($callback)) {
152  $callback = function ($row) {
153  return new \ElggRelationship($row);
154  };
155  }
156 
157  $results = _elgg_services()->db->getData($select, $callback);
158  if (!empty($results)) {
159  $preload = array_filter($results, function($e) {
160  return $e instanceof \ElggRelationship;
161  });
162 
163  _elgg_services()->entityPreloader->preload($preload, [
164  'guid_one',
165  'guid_two',
166  ]);
167  }
168 
169  return $results;
170  }
171 
179  protected function buildQuery(QueryBuilder $qb) {
180  $ands = [];
181 
182  foreach ($this->options->joins as $join) {
183  $join->prepare($qb, $qb->getTableAlias());
184  }
185 
186  foreach ($this->options->wheres as $where) {
187  $ands[] = $where->prepare($qb, $qb->getTableAlias());
188  }
189 
190  $ands[] = $this->buildEntityClause($qb);
191  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->metadata_name_value_pairs, $this->options->metadata_name_value_pairs_operator);
192  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->search_name_value_pairs, 'OR');
193  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
194  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
195 
196  $ands = $qb->merge($ands);
197  if (!empty($ands)) {
198  $qb->andWhere($ands);
199  }
200 
201  return $qb;
202  }
203 
212  protected function buildEntityClause(QueryBuilder $qb) {
213  $joined_alias = $qb->joinEntitiesTable($qb->getTableAlias(), $this->getJoinColumn(), 'inner', 'e');
214  return EntityWhereClause::factory($this->options)->prepare($qb, $joined_alias);
215  }
216 
227  protected function buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
228  $parts = [];
229 
230  $join_column = $this->getJoinColumn();
231 
232  foreach ($clauses as $clause) {
233  if ($clause instanceof MetadataWhereClause) {
234  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
235  $joined_alias = $qb->joinMetadataTable($qb->getTableAlias(), $join_column, null, 'inner', 'md');
236  } else {
237  $joined_alias = $qb->joinMetadataTable($qb->getTableAlias(), $join_column, $clause->names);
238  }
239 
240  $parts[] = $clause->prepare($qb, $joined_alias);
241  }
242  }
243 
244  return $qb->merge($parts, $boolean);
245  }
246 
257  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
258  $parts = [];
259 
260  $join_column = $this->getJoinColumn();
261 
262  foreach ($clauses as $clause) {
263  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
264  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), $join_column, null, 'inner', AnnotationsTable::DEFAULT_JOIN_ALIAS);
265  } else {
266  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), $join_column, $clause->names);
267  }
268 
269  $parts[] = $clause->prepare($qb, $joined_alias);
270  }
271 
272  return $qb->merge($parts, $boolean);
273  }
274 
284  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
285  $parts = [];
286 
287  foreach ($clauses as $clause) {
288  $parts[] = $clause->prepare($qb, $qb->getTableAlias());
289  }
290 
291  return $qb->merge($parts, $boolean);
292  }
293 
299  protected function getJoinColumn() {
300  if (!empty($this->options->inverse_relationship)) {
301  return 'guid_two';
302  }
303 
304  return 'guid_one';
305  }
306 }
const PRIMARY_ATTR_NAMES
Definition: ElggEntity.php:61
Builds queries for matching annotations against their properties.
Builds queries for filtering entities by their properties in the entities table.
Builds clauses for filtering entities by properties in metadata table.
Builds clauses for filtering entities by their properties in entity_relationships table.
Database abstraction query builder.
Relationships repository contains methods for fetching relationships from database or performing calc...
buildQuery(QueryBuilder $qb)
Build a database query.
buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process relationship pairs.
buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process annotation name value pairs Joins the annotation table on guid_one|guid_two in relationships ...
buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process metadata name value pairs Joins the metadata table on guid_one|guid_two in relationships tabl...
calculate($function, $property, $property_type=null)
Apply numeric calculation to a column.Calculation, e.g. max, min, avg Property name Property typeint|...
getJoinColumn()
Return the base column to use in joins.
execute()
Apply correct execution method based on calculation, count or other criteria.mixed
buildEntityClause(QueryBuilder $qb)
Process entity attribute wheres Joins entities table on guid_one|guid_two in relationships table and ...
Abstract methods for interfacing with the database.
Definition: Repository.php:15
expandInto(QueryBuilder $qb, $table_alias=null)
Extend query builder with select, group_by, having and order_by clauses from $options.
Definition: Repository.php:247
batch($limit=null, $offset=null, $callback=null)
Fetch rows as an ElggBatch.
Definition: Repository.php:120
static fromTable(string $table, ?string $alias=null)
Returns a QueryBuilder for selecting data from a given table.
Definition: Select.php:18
Exception thrown if a value does not adhere to a defined valid data domain.
Exception that represents error in the program logic.
if($item instanceof \ElggEntity) elseif($item instanceof \ElggRiverItem) elseif($item instanceof \ElggRelationship) elseif(is_callable([ $item, 'getType']))
Definition: item.php:48
_elgg_services()
Get the global service provider.
Definition: elgglib.php:337
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:240
if(empty($count)) $offset
Definition: pagination.php:26
$limit
Definition: pagination.php:28
$qb
Definition: queue.php:14
$results
Definition: content.php:22