Elgg  Version master
Entities.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
12 
19 class Entities extends Repository {
20 
24  public function count() {
26 
27  $count_expr = $this->options->distinct ? "DISTINCT {$qb->getTableAlias()}.guid" : '*';
28  $qb->select("COUNT({$count_expr}) AS total");
29 
30  $qb = $this->buildQuery($qb);
31 
32  $result = _elgg_services()->db->getDataRow($qb);
33 
34  if (empty($result)) {
35  return 0;
36  }
37 
38  return (int) $result->total;
39  }
40 
51  public function calculate($function, $property, $property_type = null) {
52  if (!in_array(strtolower($function), QueryBuilder::CALCULATIONS)) {
53  throw new DomainException("'{$function}' is not a valid numeric function");
54  }
55 
56  if (!isset($property_type)) {
57  if (in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
58  $property_type = 'attribute';
59  } else {
60  $property_type = 'metadata';
61  }
62  }
63 
65 
66  switch ($property_type) {
67  case 'attribute':
68  if (!in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
69  throw new DomainException("'{$property}' is not a valid attribute");
70  }
71 
72  $qb->addSelect("{$function}({$qb->getTableAlias()}.{$property}) AS calculation");
73  break;
74 
75  case 'metadata':
76  $alias = $qb->joinMetadataTable($qb->getTableAlias(), 'guid', $property, 'inner', MetadataTable::DEFAULT_JOIN_ALIAS);
77  $qb->addSelect("{$function}({$alias}.value) AS calculation");
78  break;
79 
80  case 'annotation':
81  $alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'guid', $property, 'inner', AnnotationsTable::DEFAULT_JOIN_ALIAS);
82  $qb->addSelect("{$function}({$alias}.value) AS calculation");
83  break;
84  }
85 
86  $qb = $this->buildQuery($qb);
87 
88  $result = _elgg_services()->db->getDataRow($qb);
89 
90  // do not cast data as calculations could be used as int, float or string
91  return $result->calculation;
92  }
93 
103  public function get($limit = null, $offset = null, $callback = null) {
105 
106  $distinct = $this->options->distinct ? 'DISTINCT ' : '';
107  $qb->select("{$distinct}{$qb->getTableAlias()}.*");
108 
109  $this->expandInto($qb, $qb->getTableAlias());
110 
111  $qb = $this->buildQuery($qb);
112 
113  // add default ordering
114  $original_order = elgg_extract('order_by', $this->options->__original_options);
115  if (empty($this->options->order_by) && $original_order !== false) {
116  $qb->addOrderBy("{$qb->getTableAlias()}.time_created", 'desc');
117  // also add order by guid, to rely less on internals of MySQL fallback ordering
118  $qb->addOrderBy("{$qb->getTableAlias()}.guid", 'desc');
119  }
120 
121  if ($limit > 0) {
122  $qb->setMaxResults((int) $limit);
123  $qb->setFirstResult((int) $offset);
124  }
125 
126  $options = $this->options->getArrayCopy();
127 
128  $options['limit'] = (int) $limit;
129  $options['offset'] = (int) $offset;
130  $options['callback'] = $callback ?: $this->options->callback;
131  if (!isset($options['callback'])) {
132  $options['callback'] = [_elgg_services()->entityTable, 'rowToElggStar'];
133  }
134 
135  unset($options['count']);
136 
137  return _elgg_services()->entityTable->fetch($qb, $options);
138  }
139 
149  public function getDates(): array {
151 
152  $qb->select("DISTINCT EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME({$qb->getTableAlias()}.time_created)) AS yearmonth");
153 
154  $this->expandInto($qb, $qb->getTableAlias());
155 
156  $qb = $this->buildQuery($qb);
157 
158  $options = $this->options->getArrayCopy();
159  unset($options['count']);
160 
161  $options['callback'] = false;
162 
163  $results = _elgg_services()->entityTable->fetch($qb, $options);
164 
165  if (empty($results)) {
166  return [];
167  }
168 
169  return array_map(function ($e) {
170  return $e->yearmonth;
171  }, $results);
172  }
173 
180  public function execute() {
181  if ($this->options->annotation_calculation) {
182  $clauses = $this->options->annotation_name_value_pairs;
183  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
184  throw new LogicException('Annotation calculation can not be performed on multiple annotation name value pairs merged with AND');
185  }
186 
187  $clause = array_shift($clauses);
188 
189  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
190  } else if ($this->options->metadata_calculation) {
191  $clauses = $this->options->metadata_name_value_pairs;
192  if (count($clauses) > 1 && $this->options->metadata_name_value_pairs_operator !== 'OR') {
193  throw new LogicException('Metadata calculation can not be performed on multiple metadata name value pairs merged with AND');
194  }
195 
196  $clause = array_shift($clauses);
197 
198  return $this->calculate($this->options->metadata_calculation, $clause->names, 'metadata');
199  } else if ($this->options->count) {
200  return $this->count();
201  } else if ($this->options->batch) {
202  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
203  } else {
204  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
205  }
206  }
207 
215  protected function buildQuery(QueryBuilder $qb) {
216  $ands = [];
217 
218  foreach ($this->options->joins as $join) {
219  $join->prepare($qb, $qb->getTableAlias());
220  }
221 
222  foreach ($this->options->wheres as $where) {
223  $ands[] = $where->prepare($qb, $qb->getTableAlias());
224  }
225 
226  $ands[] = $this->buildEntityClause($qb);
227  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->metadata_name_value_pairs, $this->options->metadata_name_value_pairs_operator);
228  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->search_name_value_pairs, 'OR');
229  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
230  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
231 
232  $ands = $qb->merge($ands);
233 
234  if (!empty($ands)) {
235  $qb->andWhere($ands);
236  }
237 
238  return $qb;
239  }
240 
249  protected function buildEntityClause(QueryBuilder $qb) {
250  return EntityWhereClause::factory($this->options)->prepare($qb, $qb->getTableAlias());
251  }
252 
263  protected function buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
264  $parts = [];
265 
266  foreach ($clauses as $clause) {
267  if ($clause instanceof MetadataWhereClause) {
268  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
269  $joined_alias = $qb->joinMetadataTable($qb->getTableAlias(), 'guid', null, 'inner', MetadataTable::DEFAULT_JOIN_ALIAS);
270  } else {
271  $joined_alias = $qb->joinMetadataTable($qb->getTableAlias(), 'guid', $clause->names);
272  }
273 
274  $parts[] = $clause->prepare($qb, $joined_alias);
275  }
276  }
277 
278  return $qb->merge($parts, $boolean);
279  }
280 
291  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
292  $parts = [];
293 
294  foreach ($clauses as $clause) {
295  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
296  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'guid', null, 'inner', AnnotationsTable::DEFAULT_JOIN_ALIAS);
297  } else {
298  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'guid', $clause->names);
299  }
300 
301  $parts[] = $clause->prepare($qb, $joined_alias);
302  }
303 
304  return $qb->merge($parts, $boolean);
305  }
306 
316  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
317  $parts = [];
318 
319  foreach ($clauses as $clause) {
320  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
321  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), $clause->join_on, null, $clause->inverse, 'inner', RelationshipsTable::DEFAULT_JOIN_ALIAS);
322  } else {
323  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), $clause->join_on, $clause->names, $clause->inverse);
324  }
325 
326  $parts[] = $clause->prepare($qb, $joined_alias);
327  }
328 
329  return $qb->merge($parts, $boolean);
330  }
331 }
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.
buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean= 'AND')
Process annotation name value pairs Joins the annotation table on entity guid in the entities table a...
Definition: Entities.php:291
execute()
Execute the query resolving calculation, count and/or batch options.
Definition: Entities.php:180
batch($limit=null, $offset=null, $callback=null)
Fetch rows as an ElggBatch.
Definition: Repository.php:123
const PRIMARY_ATTR_NAMES
Definition: ElggEntity.php:53
buildQuery(QueryBuilder $qb)
Build a database query.
Definition: Entities.php:215
calculate($function, $property, $property_type=null)
Performs a mathematical calculation on a set of entity properties.
Definition: Entities.php:51
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.
Entities repository contains methods for fetching entities from database or performing calculations o...
Definition: Entities.php:19
buildEntityClause(QueryBuilder $qb)
Process entity attribute wheres Applies entity attribute constrains on the selected entities table...
Definition: Entities.php:249
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
buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean= 'AND')
Process metadata name value pairs Joins the metadata table on entity guid in the entities table and a...
Definition: Entities.php:263
expandInto(QueryBuilder $qb, $table_alias=null)
Extend query builder with select, group_by, having and order_by clauses from $options.
Definition: Repository.php:250
Exception that represents error in the program logic.
buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean= 'AND')
Process relationship pairs.
Definition: Entities.php:316
joinRelationshipTable(string $from_alias= '', string $from_column= 'guid', $name=null, bool $inverse=false,?string $join_type= 'inner', string $joined_alias=null)
Join relationship 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
getDates()
Returns a list of months in which entities were updated or created.
Definition: Entities.php:149
$qb
Definition: queue.php:12
Builds clauses for filtering entities by properties in metadata table.