Elgg  Version master
Annotations.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
5 use Doctrine\DBAL\Query\Expression\CompositeExpression;
13 
20 class Annotations extends Repository {
21 
25  public function count() {
27 
28  $count_expr = $this->options->distinct ? "DISTINCT {$qb->getTableAlias()}.id" : '*';
29  $qb->select("COUNT({$count_expr}) AS total");
30 
31  $qb = $this->buildQuery($qb);
32 
33  $result = _elgg_services()->db->getDataRow($qb);
34 
35  if (empty($result)) {
36  return 0;
37  }
38 
39  return (int) $result->total;
40  }
41 
52  public function calculate($function, $property, $property_type = null) {
53  if (!in_array(strtolower($function), QueryBuilder::CALCULATIONS)) {
54  throw new DomainException("'{$function}' is not a valid numeric function");
55  }
56 
57  if (!isset($property_type)) {
58  $property_type = 'annotation';
59  }
60 
62 
63  switch ($property_type) {
64  case 'attribute':
65  if (!in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
66  throw new DomainException("'{$property}' is not a valid attribute");
67  }
68 
69  $alias = $qb->joinEntitiesTable($qb->getTableAlias(), 'entity_guid', 'inner', EntityTable::DEFAULT_JOIN_ALIAS);
70  $qb->select("{$function}({$alias}.{$property}) AS calculation");
71  break;
72 
73  case 'annotation':
75  if (!empty($this->options->annotation_name_value_pairs) && $this->options->annotation_name_value_pairs[0]->names != $property) {
76  $alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'entity_guid', $property);
77  }
78 
79  $qb->select("{$function}({$alias}.value) AS calculation");
80  break;
81 
82  case 'metadata':
83  $alias = $qb->joinMetadataTable($qb->getTableAlias(), 'entity_guid', $property);
84  $qb->select("{$function}({$alias}.value) AS calculation");
85  break;
86  }
87 
88  $qb = $this->buildQuery($qb);
89 
90  $result = _elgg_services()->db->getDataRow($qb);
91 
92  return $result ? (int) $result->calculation : 0;
93  }
94 
104  public function get($limit = null, $offset = null, $callback = null) {
106 
107  $distinct = $this->options->distinct ? 'DISTINCT ' : '';
108  $qb->select("{$distinct}{$qb->getTableAlias()}.*");
109 
110  $this->expandInto($qb, $qb->getTableAlias());
111 
112  $qb = $this->buildQuery($qb);
113 
114  // add default ordering
115  $original_order = elgg_extract('order_by', $this->options->__original_options);
116  if (empty($this->options->order_by) && $original_order !== false) {
117  $qb->addOrderBy("{$qb->getTableAlias()}.time_created", 'asc');
118  $qb->addOrderBy("{$qb->getTableAlias()}.id", 'asc');
119  }
120 
121  if ($limit > 0) {
122  $qb->setMaxResults((int) $limit);
123  $qb->setFirstResult((int) $offset);
124  }
125 
126  $callback = $callback ?: $this->options->callback;
127  if (!isset($callback)) {
128  $callback = function ($row) {
129  return new \ElggAnnotation($row);
130  };
131  }
132 
133  $results = _elgg_services()->db->getData($qb, $callback);
134  if (!empty($results) && $this->options->preload_owners) {
135  _elgg_services()->entityPreloader->preload($results, ['owner_guid']);
136  }
137 
138  return $results;
139  }
140 
147  public function execute() {
148  if ($this->options->annotation_calculation) {
149  $clauses = $this->options->annotation_name_value_pairs;
150  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
151  throw new LogicException('Annotation calculation can not be performed on multiple annotation name value pairs merged with AND');
152  }
153 
154  $clause = array_shift($clauses);
155 
156  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
157  } elseif ($this->options->metadata_calculation) {
158  $clauses = $this->options->metadata_name_value_pairs;
159  if (count($clauses) > 1 && $this->options->metadata_name_value_pairs_operator !== 'OR') {
160  throw new LogicException('Metadata calculation can not be performed on multiple metadata name value pairs merged with AND');
161  }
162 
163  $clause = array_shift($clauses);
164 
165  return $this->calculate($this->options->metadata_calculation, $clause->names, 'metadata');
166  } elseif ($this->options->count) {
167  return $this->count();
168  } elseif ($this->options->batch) {
169  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
170  } else {
171  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
172  }
173  }
174 
182  protected function buildQuery(QueryBuilder $qb) {
183  $ands = [];
184 
185  foreach ($this->options->joins as $join) {
186  $join->prepare($qb, $qb->getTableAlias());
187  }
188 
189  foreach ($this->options->wheres as $where) {
190  $ands[] = $where->prepare($qb, $qb->getTableAlias());
191  }
192 
193  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
194  $ands[] = $this->buildEntityWhereClause($qb);
195  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->metadata_name_value_pairs, $this->options->metadata_name_value_pairs_operator);
196  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->search_name_value_pairs, 'OR');
197  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
198 
199  $ands = $qb->merge($ands);
200 
201  if (!empty($ands)) {
202  $qb->andWhere($ands);
203  }
204 
205  return $qb;
206  }
207 
217  $joined_alias = $qb->joinEntitiesTable($qb->getTableAlias(), 'entity_guid', 'inner', EntityTable::DEFAULT_JOIN_ALIAS);
218  return EntityWhereClause::factory($this->options)->prepare($qb, $joined_alias);
219  }
220 
231  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
232  $parts = [];
233 
234  if (empty($clauses)) {
235  // We need to make sure that enabled and access clauses are appended to the query
236  $clauses[] = new AnnotationWhereClause();
237  }
238 
239  foreach ($clauses as $clause) {
240  $parts[] = $clause->prepare($qb, $qb->getTableAlias());
241  }
242 
243  return $qb->merge($parts, $boolean);
244  }
245 
256  protected function buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
257  $parts = [];
258 
259  foreach ($clauses as $clause) {
260  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
261  $joined_alias = $qb->joinMetadataTable($qb->getTableAlias(), 'entity_guid', null, 'inner', MetadataTable::DEFAULT_JOIN_ALIAS);
262  } else {
263  $joined_alias = $qb->joinMetadataTable($qb->getTableAlias(), 'entity_guid', $clause->names);
264  }
265 
266  $parts[] = $clause->prepare($qb, $joined_alias);
267  }
268 
269  return $qb->merge($parts, $boolean);
270  }
271 
282  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
283  $parts = [];
284 
285  foreach ($clauses as $clause) {
286  $join_on = $clause->join_on == 'guid' ? 'entity_guid' : $clause->join_on;
287  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
288  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), $join_on, null, $clause->inverse, 'inner', RelationshipsTable::DEFAULT_JOIN_ALIAS);
289  } else {
290  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), $join_on, $clause->names, $clause->inverse);
291  }
292 
293  $parts[] = $clause->prepare($qb, $joined_alias);
294  }
295 
296  return $qb->merge($parts, $boolean);
297  }
298 }
const PRIMARY_ATTR_NAMES
Definition: ElggEntity.php:61
Annotation repository contains methods for fetching annotations from database or performing calculati...
Definition: Annotations.php:20
buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process metadata name value pairs Joins metadata table on entity_guid in the annotations table and ap...
count()
{Count rows.int}
Definition: Annotations.php:25
buildQuery(QueryBuilder $qb)
Build a database query.
buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process relationship name value pairs Joins relationship table on entity_guid in the annotations tabl...
execute()
Execute the query resolving calculation, count and/or batch options.
buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process annotation name value pairs Applies where clauses to the selected annotation table.
calculate($function, $property, $property_type=null)
Performs a mathematical calculation on metadata or metadata entity's properties.
Definition: Annotations.php:52
buildEntityWhereClause(QueryBuilder $qb)
Process entity attribute wheres Joins entities table on entity guid in annotations table and applies ...
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.
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:250
batch($limit=null, $offset=null, $callback=null)
Fetch rows as an ElggBatch.
Definition: Repository.php:123
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 thrown if an argument is not of the expected type.
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:353
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:256
if(empty($count)) $offset
Definition: pagination.php:26
$limit
Definition: pagination.php:28
$qb
Definition: queue.php:12
$results
Definition: content.php:22