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