Elgg  Version 6.3
Metadata.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
5 use Doctrine\DBAL\Query\Expression\CompositeExpression;
12 
19 class Metadata extends Repository {
20 
24  public function count() {
26 
27  $count_expr = $this->options->distinct ? "DISTINCT {$qb->getTableAlias()}.id" : '*';
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  $property_type = 'metadata';
58  }
59 
61 
62  switch ($property_type) {
63  case 'attribute':
64  if (!in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
65  throw new DomainException("'{$property}' is not a valid attribute");
66  }
67 
68  $alias = $qb->joinEntitiesTable($qb->getTableAlias(), 'entity_guid', 'inner', 'e');
69  $qb->addSelect("{$function}({$alias}.{$property}) AS calculation");
70  break;
71 
72  case 'metadata':
74  if (!empty($this->options->metadata_name_value_pairs) && $this->options->metadata_name_value_pairs[0]->names != $property) {
75  $alias = $qb->joinMetadataTable($qb->getTableAlias(), 'entity_guid', $property);
76  }
77 
78  $qb->addSelect("{$function}({$alias}.value) AS calculation");
79  break;
80 
81  case 'annotation':
82  $alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'entity_guid', $property, 'inner', AnnotationsTable::DEFAULT_JOIN_ALIAS);
83  $qb->addSelect("{$function}({$alias}.value) AS calculation");
84  break;
85  }
86 
87  $qb = $this->buildQuery($qb);
88 
89  $result = _elgg_services()->db->getDataRow($qb);
90 
91  return $result ? (int) $result->calculation : 0;
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", 'asc');
117  $qb->addOrderBy("{$qb->getTableAlias()}.id", 'asc');
118  }
119 
120  if ($limit > 0) {
121  $qb->setMaxResults((int) $limit);
122  $qb->setFirstResult((int) $offset);
123  }
124 
125  $callback = $callback ?: $this->options->callback;
126  if (!isset($callback)) {
127  $callback = function ($row) {
128  return new \ElggMetadata($row);
129  };
130  }
131 
132  return _elgg_services()->db->getData($qb, $callback);
133  }
134 
141  public function execute() {
142  if ($this->options->annotation_calculation) {
143  $clauses = $this->options->annotation_name_value_pairs;
144  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
145  throw new LogicException('Annotation calculation can not be performed on multiple annotation name value pairs merged with AND');
146  }
147 
148  $clause = array_shift($clauses);
149 
150  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
151  } else if ($this->options->metadata_calculation) {
152  $clauses = $this->options->metadata_name_value_pairs;
153  if (count($clauses) > 1 && $this->options->metadata_name_value_pairs_operator !== 'OR') {
154  throw new LogicException('Metadata calculation can not be performed on multiple metadata name value pairs merged with AND');
155  }
156 
157  $clause = array_shift($clauses);
158 
159  return $this->calculate($this->options->metadata_calculation, $clause->names, 'metadata');
160  } else if ($this->options->count) {
161  return $this->count();
162  } else if ($this->options->batch) {
163  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
164  } else {
165  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
166  }
167  }
168 
176  protected function buildQuery(QueryBuilder $qb) {
177  $ands = [];
178 
179  foreach ($this->options->joins as $join) {
180  $join->prepare($qb, $qb->getTableAlias());
181  }
182 
183  foreach ($this->options->wheres as $where) {
184  $ands[] = $where->prepare($qb, $qb->getTableAlias());
185  }
186 
187  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->metadata_name_value_pairs, $this->options->metadata_name_value_pairs_operator);
188  $ands[] = $this->buildEntityWhereClause($qb);
189  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->search_name_value_pairs, 'OR');
190  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
191  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
192 
193  $ands = $qb->merge($ands);
194 
195  if (!empty($ands)) {
196  $qb->andWhere($ands);
197  }
198 
199  return $qb;
200  }
201 
211  // Even if all of these properties are empty, we want to add this clause regardless,
212  // to ensure that entity access clauses are appended to the query
213  $joined_alias = $qb->joinEntitiesTable($qb->getTableAlias(), 'entity_guid', 'inner', EntityTable::DEFAULT_JOIN_ALIAS);
214  return EntityWhereClause::factory($this->options)->prepare($qb, $joined_alias);
215  }
216 
227  protected function buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
228  $parts = [];
229 
230  foreach ($clauses as $clause) {
231  $parts[] = $clause->prepare($qb, $qb->getTableAlias());
232  }
233 
234  return $qb->merge($parts, $boolean);
235  }
236 
247  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
248  $parts = [];
249 
250  foreach ($clauses as $clause) {
251  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
252  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'entity_guid', null, 'inner', AnnotationsTable::DEFAULT_JOIN_ALIAS);
253  } else {
254  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'entity_guid', $clause->names);
255  }
256 
257  $parts[] = $clause->prepare($qb, $joined_alias);
258  }
259 
260  return $qb->merge($parts, $boolean);
261  }
262 
275  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
276  $parts = [];
277 
278  foreach ($clauses as $clause) {
279  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
280  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), 'entity_guid', null, $clause->inverse, 'inner', RelationshipsTable::DEFAULT_JOIN_ALIAS);
281  } else {
282  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), 'entity_guid', $clause->names, $clause->inverse);
283  }
284 
285  $parts[] = $clause->prepare($qb, $joined_alias);
286  }
287 
288  return $qb->merge($parts, $boolean);
289  }
290 }
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:19
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:247
calculate($function, $property, $property_type=null)
Performs a mathematical calculation on metadata or metadata entity's properties.
Definition: Metadata.php:51
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:275
buildQuery(QueryBuilder $qb)
Build a database query.
Definition: Metadata.php:176
execute()
Execute the query resolving calculation, count and/or batch options.
Definition: Metadata.php:141
count()
{Count rows.int}
Definition: Metadata.php:24
buildEntityWhereClause(QueryBuilder $qb)
Process entity attribute wheres Joins entities table on entity guid in metadata table and applies whe...
Definition: Metadata.php:210
buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process metadata name value pairs Applies where clauses to the selected metadata table.
Definition: Metadata.php:227
Database abstraction query builder.
Abstract methods for interfacing with the database.
Definition: Repository.php:15
expandInto(QueryBuilder $qb, $table_alias=null)
Extend query builder with select, group_by, having and order_by clauses from $options.
Definition: Repository.php:247
batch($limit=null, $offset=null, $callback=null)
Fetch rows as an ElggBatch.
Definition: Repository.php:120
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 that represents error in the program logic.
_elgg_services()
Get the global service provider.
Definition: elgglib.php:337
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:240
if(empty($count)) $offset
Definition: pagination.php:26
$limit
Definition: pagination.php:28
$qb
Definition: queue.php:14