Elgg  Version 6.2
Metadata.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 Metadata 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 = 'metadata';
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', 'e');
70  $qb->addSelect("{$function}({$alias}.{$property}) AS calculation");
71  break;
72 
73  case 'metadata':
75  if (!empty($this->options->metadata_name_value_pairs) && $this->options->metadata_name_value_pairs[0]->names != $property) {
76  $alias = $qb->joinMetadataTable($qb->getTableAlias(), 'entity_guid', $property);
77  }
78 
79  $qb->addSelect("{$function}({$alias}.value) AS calculation");
80  break;
81 
82  case 'annotation':
83  $alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'entity_guid', $property, 'inner', AnnotationsTable::DEFAULT_JOIN_ALIAS);
84  $qb->addSelect("{$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 \ElggMetadata($row);
130  };
131  }
132 
133  return _elgg_services()->db->getData($qb, $callback);
134  }
135 
142  public function execute() {
143  if ($this->options->annotation_calculation) {
144  $clauses = $this->options->annotation_name_value_pairs;
145  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
146  throw new LogicException('Annotation calculation can not be performed on multiple annotation name value pairs merged with AND');
147  }
148 
149  $clause = array_shift($clauses);
150 
151  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
152  } else if ($this->options->metadata_calculation) {
153  $clauses = $this->options->metadata_name_value_pairs;
154  if (count($clauses) > 1 && $this->options->metadata_name_value_pairs_operator !== 'OR') {
155  throw new LogicException('Metadata calculation can not be performed on multiple metadata name value pairs merged with AND');
156  }
157 
158  $clause = array_shift($clauses);
159 
160  return $this->calculate($this->options->metadata_calculation, $clause->names, 'metadata');
161  } else if ($this->options->count) {
162  return $this->count();
163  } else if ($this->options->batch) {
164  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
165  } else {
166  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
167  }
168  }
169 
177  protected function buildQuery(QueryBuilder $qb) {
178  $ands = [];
179 
180  foreach ($this->options->joins as $join) {
181  $join->prepare($qb, $qb->getTableAlias());
182  }
183 
184  foreach ($this->options->wheres as $where) {
185  $ands[] = $where->prepare($qb, $qb->getTableAlias());
186  }
187 
188  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->metadata_name_value_pairs, $this->options->metadata_name_value_pairs_operator);
189  $ands[] = $this->buildEntityWhereClause($qb);
190  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->search_name_value_pairs, 'OR');
191  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
192  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
193 
194  $ands = $qb->merge($ands);
195 
196  if (!empty($ands)) {
197  $qb->andWhere($ands);
198  }
199 
200  return $qb;
201  }
202 
212  // Even if all of these properties are empty, we want to add this clause regardless,
213  // to ensure that entity access clauses are appended to the query
214  $joined_alias = $qb->joinEntitiesTable($qb->getTableAlias(), 'entity_guid', 'inner', EntityTable::DEFAULT_JOIN_ALIAS);
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  foreach ($clauses as $clause) {
232  $parts[] = $clause->prepare($qb, $qb->getTableAlias());
233  }
234 
235  return $qb->merge($parts, $boolean);
236  }
237 
248  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
249  $parts = [];
250 
251  foreach ($clauses as $clause) {
252  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
253  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'entity_guid', null, 'inner', AnnotationsTable::DEFAULT_JOIN_ALIAS);
254  } else {
255  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'entity_guid', $clause->names);
256  }
257 
258  $parts[] = $clause->prepare($qb, $joined_alias);
259  }
260 
261  return $qb->merge($parts, $boolean);
262  }
263 
276  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
277  $parts = [];
278 
279  foreach ($clauses as $clause) {
280  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
281  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), 'entity_guid', null, $clause->inverse, 'inner', RelationshipsTable::DEFAULT_JOIN_ALIAS);
282  } else {
283  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), 'entity_guid', $clause->names, $clause->inverse);
284  }
285 
286  $parts[] = $clause->prepare($qb, $joined_alias);
287  }
288 
289  return $qb->merge($parts, $boolean);
290  }
291 }
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.
Metadata repository contains methods for fetching metadata from database or performing calculations o...
Definition: Metadata.php:20
buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process annotation name value pairs Joins annotation table on entity_guid in the metadata table and a...
Definition: Metadata.php:248
calculate($function, $property, $property_type=null)
Performs a mathematical calculation on metadata or metadata entity's properties.
Definition: Metadata.php:52
buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process relationship name value pairs Joins relationship table on entity_guid in the metadata table a...
Definition: Metadata.php:276
buildQuery(QueryBuilder $qb)
Build a database query.
Definition: Metadata.php:177
execute()
Execute the query resolving calculation, count and/or batch options.
Definition: Metadata.php:142
count()
{Count rows.int}
Definition: Metadata.php:25
buildEntityWhereClause(QueryBuilder $qb)
Process entity attribute wheres Joins entities table on entity guid in metadata table and applies whe...
Definition: Metadata.php:211
buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process metadata name value pairs Applies where clauses to the selected metadata table.
Definition: Metadata.php:228
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.
_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