Elgg  Version 4.3
Relationships.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
14 
22 class Relationships extends Repository {
23 
28  public function calculate($function, $property, $property_type = null) {
29 
30  if (!in_array(strtolower($function), QueryBuilder::$calculations)) {
31  throw new InvalidArgumentException("'$function' is not a valid numeric function");
32  }
33 
34  if (!isset($property_type)) {
35  if (in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
36  $property_type = 'attribute';
37  } else {
38  $property_type = 'metadata';
39  }
40  }
41 
42  $join_column = $this->getJoinColumn();
43 
44  $select = Select::fromTable('entity_relationships', 'er');
45 
46  switch ($property_type) {
47  case 'attribute':
48  if (!in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
49  throw new InvalidParameterException("'$property' is not a valid attribute");
50  }
51 
52  $alias = $select->joinEntitiesTable('er', $join_column, 'inner', 'e');
53  $select->select("{$function}({$alias}.{$property}) AS calculation");
54  break;
55 
56  case 'annotation' :
57  $alias = 'n_table';
58  if (!empty($this->options->annotation_name_value_pairs) && $this->options->annotation_name_value_pairs[0]->names != $property) {
59  $alias = $select->joinAnnotationTable('er', $join_column, $property);
60  }
61  $select->select("{$function}($alias.value) AS calculation");
62  break;
63 
64  case 'metadata' :
65  $alias = $select->joinMetadataTable('er', $join_column, $property);
66  $select->select("{$function}({$alias}.value) AS calculation");
67  break;
68 
69  case 'private_setting' :
70  $alias = $select->joinPrivateSettingsTable('er', $join_column, $property);
71  $select->select("{$function}({$alias}.value) AS calculation");
72  break;
73  }
74 
75  $select = $this->buildQuery($select);
76 
77  $result = _elgg_services()->db->getDataRow($select);
78  if (empty($result)) {
79  return 0;
80  }
81 
82  return (int) $result->calculation;
83  }
84 
89  public function count() {
90  $select = Select::fromTable('entity_relationships', 'er');
91 
92  $count_expr = $this->options->distinct ? "DISTINCT er.id" : "*";
93  $select->select("COUNT({$count_expr}) AS total");
94 
95  $select = $this->buildQuery($select);
96 
97  $result = _elgg_services()->db->getDataRow($select);
98  if (empty($result)) {
99  return 0;
100  }
101 
102  return (int) $result->total;
103  }
104 
108  public function execute() {
109 
110  if ($this->options->annotation_calculation) {
111  $clauses = $this->options->annotation_name_value_pairs;
112  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
113  throw new LogicException("Annotation calculation can not be performed on multiple annotation name value pairs merged with AND");
114  }
115 
116  $clause = array_shift($clauses);
117 
118  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
119  } elseif ($this->options->metadata_calculation) {
120  $clauses = $this->options->metadata_name_value_pairs;
121  if (count($clauses) > 1 && $this->options->metadata_name_value_pairs_operator !== 'OR') {
122  throw new LogicException("Metadata calculation can not be performed on multiple metadata name value pairs merged with AND");
123  }
124 
125  $clause = array_shift($clauses);
126 
127  return $this->calculate($this->options->metadata_calculation, $clause->names, 'metadata');
128  } elseif ($this->options->count) {
129  return $this->count();
130  } elseif ($this->options->batch) {
131  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
132  } else {
133  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
134  }
135  }
136 
140  public function get($limit = null, $offset = null, $callback = null) {
141  $select = Select::fromTable('entity_relationships', 'er');
142 
143  $distinct = $this->options->distinct ? 'DISTINCT' : '';
144  $select->select("$distinct er.*");
145 
146  $this->expandInto($select, 'er');
147 
148  $select = $this->buildQuery($select);
149 
150  // Keeping things backwards compatible
151  $original_order = elgg_extract('order_by', $this->options->__original_options);
152  if (empty($original_order) && $original_order !== false) {
153  $select->addOrderBy('er.time_created', 'desc');
154  $select->addOrderBy('er.id', 'desc');
155  }
156 
157  if ($limit > 0) {
158  $select->setMaxResults((int) $limit);
159  $select->setFirstResult((int) $offset);
160  }
161 
162  $callback = $callback ? : $this->options->callback;
163  if (!isset($callback)) {
164  $callback = function ($row) {
165  return new \ElggRelationship($row);
166  };
167  }
168 
169  $results = _elgg_services()->db->getData($select, $callback);
170  if (!empty($results)) {
171  $preload = array_filter($results, function($e) {
172  return $e instanceof \ElggRelationship;
173  });
174 
175  _elgg_services()->entityPreloader->preload($preload, [
176  'guid_one',
177  'guid_two',
178  ]);
179  }
180 
181  return $results;
182  }
183 
191  protected function buildQuery(QueryBuilder $qb) {
192  $ands = [];
193 
194  foreach ($this->options->joins as $join) {
195  $join->prepare($qb, 'er');
196  }
197 
198  foreach ($this->options->wheres as $where) {
199  $ands[] = $where->prepare($qb, 'er');
200  }
201 
202  $ands[] = $this->buildEntityClause($qb);
203  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->metadata_name_value_pairs, $this->options->metadata_name_value_pairs_operator);
204  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->search_name_value_pairs, 'OR');
205  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
206  $ands[] = $this->buildPairedPrivateSettingsClause($qb, $this->options->private_setting_name_value_pairs, $this->options->private_setting_name_value_pairs_operator);
207  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
208 
209  $ands = $qb->merge($ands);
210  if (!empty($ands)) {
211  $qb->andWhere($ands);
212  }
213 
214  return $qb;
215  }
216 
225  protected function buildEntityClause(QueryBuilder $qb) {
226  $joined_alias = $qb->joinEntitiesTable('er', $this->getJoinColumn(), 'inner', 'e');
227  return EntityWhereClause::factory($this->options)->prepare($qb, $joined_alias);
228  }
229 
240  protected function buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
241  $parts = [];
242 
243  $join_column = $this->getJoinColumn();
244 
245  foreach ($clauses as $clause) {
246  if ($clause instanceof MetadataWhereClause) {
247  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
248  $joined_alias = $qb->joinMetadataTable('er', $join_column, null, 'inner', 'md');
249  } else {
250  $joined_alias = $qb->joinMetadataTable('er', $join_column, $clause->names);
251  }
252  $parts[] = $clause->prepare($qb, $joined_alias);
253  }
254  }
255 
256  return $qb->merge($parts, $boolean);
257  }
258 
269  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
270  $parts = [];
271 
272  $join_column = $this->getJoinColumn();
273 
274  foreach ($clauses as $clause) {
275  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
276  $joined_alias = $qb->joinAnnotationTable('er', $join_column, null, 'inner', 'an');
277  } else {
278  $joined_alias = $qb->joinAnnotationTable('er', $join_column, $clause->names);
279  }
280  $parts[] = $clause->prepare($qb, $joined_alias);
281  }
282 
283  return $qb->merge($parts, $boolean);
284  }
285 
296  protected function buildPairedPrivateSettingsClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
297  $parts = [];
298 
299  $join_column = $this->getJoinColumn();
300 
301  foreach ($clauses as $clause) {
302  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
303  $joined_alias = $qb->joinPrivateSettingsTable('er', $join_column, null, 'inner', 'ps');
304  } else {
305  $joined_alias = $qb->joinPrivateSettingsTable('er', $join_column, $clause->names);
306  }
307  $parts[] = $clause->prepare($qb, $joined_alias);
308  }
309 
310  return $qb->merge($parts, $boolean);
311  }
312 
322  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
323  $parts = [];
324 
325  foreach ($clauses as $clause) {
326  $parts[] = $clause->prepare($qb, 'er');
327  }
328 
329  return $qb->merge($parts, $boolean);
330  }
331 
337  protected function getJoinColumn() {
338  if (!empty($this->options->inverse_relationship)) {
339  return 'guid_two';
340  }
341 
342  return 'guid_one';
343  }
344 }
Exception thrown if an argument is not of the expected type.
batch($limit=null, $offset=null, $callback=null)
{Fetch rows as an ElggBatch.Number of rows to fetch Index of the first row Callback function to run d...
Definition: Repository.php:97
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)
Apply numeric calculation to a column.Calculation, e.g. max, min, avg Property name Property typeint|...
const PRIMARY_ATTR_NAMES
Definition: ElggEntity.php:50
Relationships repository contains methods for fetching relationships from database or performing calc...
if(!$count) $offset
Definition: pagination.php:26
Database abstraction query builder.
execute()
Apply correct execution method based on calculation, count or other criteria.mixed ...
if($pagination &&($position== 'after'||$position== 'both')) $limit
Definition: list.php:108
buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean= 'AND')
Process relationship pairs.
buildPairedPrivateSettingsClause(QueryBuilder $qb, $clauses, $boolean= 'AND')
Process private setting name value pairs Joins the private settings table on guid_one|guid_two in rel...
joinEntitiesTable($from_alias= '', $from_column= 'guid', $join_type= 'inner', $joined_alias=null)
Join entity table from alias and return joined table alias.
Abstract methods for interfacing with the database.
Definition: Repository.php:16
expandInto(QueryBuilder $qb, $table_alias=null)
Extend query builder with select, group_by, having and order_by clauses from $options.
Definition: Repository.php:181
buildQuery(QueryBuilder $qb)
Build a database query.
Exception that represents error in the program logic.
$results
Definition: content.php:22
joinAnnotationTable($from_alias= '', $from_column= 'guid', $name=null, $join_type= 'inner', $joined_alias=null)
Join annotations table from alias and return joined table alias.
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:547
static fromTable($table, $alias=null)
{}
Definition: Select.php:13
joinPrivateSettingsTable($from_alias= '', $from_column= 'guid', $name=null, $join_type= 'inner', $joined_alias=null)
Join private settings table from alias and return joined table alias.
merge($parts=null, $boolean= 'AND')
Merges multiple composite expressions with a boolean.
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:638
joinMetadataTable($from_alias= '', $from_column= 'guid', $name=null, $join_type= 'inner', $joined_alias=null)
Join metadata table from alias and return joined table alias.
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:11
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 ...