Elgg  Version master
Metadata.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
13 
20 class Metadata extends Repository {
21 
25  public function count() {
26  $qb = Select::fromTable('metadata', 'n_table');
27 
28  $count_expr = $this->options->distinct ? "DISTINCT n_table.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 
54  if (!in_array(strtolower($function), QueryBuilder::$calculations)) {
55  throw new DomainException("'{$function}' is not a valid numeric function");
56  }
57 
58  if (!isset($property_type)) {
59  $property_type = 'metadata';
60  }
61 
62  $qb = Select::fromTable('metadata', 'n_table');
63 
64  switch ($property_type) {
65  case 'attribute':
66  if (!in_array($property, \ElggEntity::PRIMARY_ATTR_NAMES)) {
67  throw new DomainException("'{$property}' is not a valid attribute");
68  }
69 
73  $alias = $qb->joinEntitiesTable('n_table', 'entity_guid', 'inner', 'e');
74  $qb->addSelect("{$function}({$alias}.{$property}) AS calculation");
75  break;
76 
77  case 'metadata' :
78  $alias = 'n_table';
79  if (!empty($this->options->metadata_name_value_pairs) && $this->options->metadata_name_value_pairs[0]->names != $property) {
80  $alias = $qb->joinMetadataTable('n_table', 'entity_guid', $property);
81  }
82  $qb->addSelect("{$function}($alias.value) AS calculation");
83  break;
84 
85  case 'annotation' :
86  $alias = $qb->joinAnnotationTable('n_table', 'entity_guid', $property);
87  $qb->addSelect("{$function}({$alias}.value) AS calculation");
88  break;
89  }
90 
91  $qb = $this->buildQuery($qb);
92 
93  $result = _elgg_services()->db->getDataRow($qb);
94 
95  if (empty($result)) {
96  return 0;
97  }
98 
99  return (int) $result->calculation;
100  }
101 
111  public function get($limit = null, $offset = null, $callback = null) {
112 
113  $qb = Select::fromTable('metadata', 'n_table');
114 
115  $distinct = $this->options->distinct ? "DISTINCT" : "";
116  $qb->select("$distinct n_table.*");
117 
118  $this->expandInto($qb, 'n_table');
119 
120  $qb = $this->buildQuery($qb);
121 
122  // Keeping things backwards compatible
123  $original_order = elgg_extract('order_by', $this->options->__original_options);
124  if (empty($original_order) && $original_order !== false) {
125  $qb->addOrderBy('n_table.time_created', 'asc');
126  $qb->addOrderBy('n_table.id', 'asc');
127  }
128 
129  if ($limit > 0) {
130  $qb->setMaxResults((int) $limit);
131  $qb->setFirstResult((int) $offset);
132  }
133 
134  $callback = $callback ? : $this->options->callback;
135  if (!isset($callback)) {
136  $callback = function ($row) {
137  return new \ElggMetadata($row);
138  };
139  }
140 
141  return _elgg_services()->db->getData($qb, $callback);
142  }
143 
150  public function execute() {
151 
152  if ($this->options->annotation_calculation) {
153  $clauses = $this->options->annotation_name_value_pairs;
154  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
155  throw new LogicException("Annotation calculation can not be performed on multiple annotation name value pairs merged with AND");
156  }
157 
158  $clause = array_shift($clauses);
159 
160  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
161  } else if ($this->options->metadata_calculation) {
162  $clauses = $this->options->metadata_name_value_pairs;
163  if (count($clauses) > 1 && $this->options->metadata_name_value_pairs_operator !== 'OR') {
164  throw new LogicException("Metadata calculation can not be performed on multiple metadata name value pairs merged with AND");
165  }
166 
167  $clause = array_shift($clauses);
168 
169  return $this->calculate($this->options->metadata_calculation, $clause->names, 'metadata');
170  } else if ($this->options->count) {
171  return $this->count();
172  } else if ($this->options->batch) {
173  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
174  } else {
175  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
176  }
177  }
178 
186  protected function buildQuery(QueryBuilder $qb) {
187 
188  $ands = [];
189 
190  foreach ($this->options->joins as $join) {
191  $join->prepare($qb, 'n_table');
192  }
193 
194  foreach ($this->options->wheres as $where) {
195  $ands[] = $where->prepare($qb, 'n_table');
196  }
197 
198  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->metadata_name_value_pairs, $this->options->metadata_name_value_pairs_operator);
199  $ands[] = $this->buildEntityWhereClause($qb);
200  $ands[] = $this->buildPairedMetadataClause($qb, $this->options->search_name_value_pairs, 'OR');
201  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
202  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
203 
204  $ands = $qb->merge($ands);
205 
206  if (!empty($ands)) {
207  $qb->andWhere($ands);
208  }
209 
210  return $qb;
211  }
212 
222 
223  // Even if all of these properties are empty, we want to add this clause regardless,
224  // to ensure that entity access clauses are appended to the query
225 
226  $joined_alias = $qb->joinEntitiesTable('n_table', 'entity_guid', '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 
244  foreach ($clauses as $clause) {
245  $parts[] = $clause->prepare($qb, 'n_table');
246  }
247 
248  return $qb->merge($parts, $boolean);
249  }
250 
261  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
262  $parts = [];
263 
264  foreach ($clauses as $clause) {
265  if (strtoupper($boolean) === 'OR' || count($clauses) > 1) {
266  $joined_alias = $qb->joinAnnotationTable('n_table', 'entity_guid');
267  } else {
268  $joined_alias = $qb->joinAnnotationTable('n_table', 'entity_guid', $clause->names);
269  }
270  $parts[] = $clause->prepare($qb, $joined_alias);
271  }
272 
273  return $qb->merge($parts, $boolean);
274  }
275 
288  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
289  $parts = [];
290 
291  foreach ($clauses as $clause) {
292  if (strtoupper($boolean) == 'OR' || count($clauses) > 1) {
293  $joined_alias = $qb->joinRelationshipTable('n_table', 'entity_guid', null, $clause->inverse);
294  } else {
295  $joined_alias = $qb->joinRelationshipTable('n_table', 'entity_guid', $clause->names, $clause->inverse);
296  }
297  $parts[] = $clause->prepare($qb, $joined_alias);
298  }
299 
300  return $qb->merge($parts, $boolean);
301  }
302 }
buildQuery(QueryBuilder $qb)
Build a database query.
Definition: Metadata.php:186
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
const PRIMARY_ATTR_NAMES
Definition: ElggEntity.php:51
calculate($function, $property, $property_type=null)
Performs a mathematical calculation on metadata or metadata entity&#39;s properties.
Definition: Metadata.php:52
count()
{Count rows.int}
Definition: Metadata.php:25
Exception thrown if a value does not adhere to a defined valid data domain.
if(!$count) $offset
Definition: pagination.php:26
buildEntityWhereClause(QueryBuilder $qb)
Process entity attribute wheres Joins entities table on entity guid in metadata table and applies whe...
Definition: Metadata.php:221
Database abstraction query builder.
if($pagination &&($position== 'after'||$position== 'both')) $limit
Definition: list.php:108
joinEntitiesTable($from_alias= '', $from_column= 'guid', $join_type= 'inner', $joined_alias=null)
Join entity table from alias and return joined table alias.
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
Metadata repository contains methods for fetching metadata from database or performing calculations o...
Definition: Metadata.php:20
Abstract methods for interfacing with the database.
Definition: Repository.php:16
execute()
Execute the query resolving calculation, count and/or batch options.
Definition: Metadata.php:150
expandInto(QueryBuilder $qb, $table_alias=null)
Extend query builder with select, group_by, having and order_by clauses from $options.
Definition: Repository.php:181
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:261
Exception that represents error in the program logic.
joinAnnotationTable($from_alias= '', $from_column= 'guid', $name=null, $join_type= 'inner', $joined_alias=null)
Join annotations table from alias and return joined table alias.
static fromTable($table, $alias=null)
{}
Definition: Select.php:13
merge($parts=null, $boolean= 'AND')
Merges multiple composite expressions with a boolean.
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:288
_elgg_services()
Get the global service provider.
Definition: elgglib.php:347
joinRelationshipTable($from_alias= '', $from_column= 'guid', $name=null, $inverse=false, $join_type= 'inner', $joined_alias=null)
Join relationship table from alias and return joined table alias.
$qb
Definition: queue.php:11
buildPairedMetadataClause(QueryBuilder $qb, $clauses, $boolean= 'AND')
Process metadata name value pairs Applies where clauses to the selected metadata table.
Definition: Metadata.php:240