Elgg  Version master
River.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
5 use Doctrine\DBAL\Query\Expression\CompositeExpression;
12 
18 class River extends Repository {
19 
23  public function __construct(array $options = []) {
24  $singulars = [
25  'id',
26  'subject_guid',
27  'object_guid',
28  'target_guid',
29  'annotation_id',
30  'action_type',
31  'type',
32  'subtype',
33  'view',
34  ];
35 
36  $options = QueryOptions::normalizePluralOptions($options, $singulars);
37 
38  $defaults = [
39  'ids' => null,
40  'subject_guids' => null,
41  'object_guids' => null,
42  'target_guids' => null,
43  'annotation_ids' => null,
44  'views' => null,
45  'action_types' => null,
46  'created_after' => null,
47  'created_before' => null,
48  'limit' => 20,
49  'offset' => 0,
50  ];
51 
52  $options = array_merge($defaults, $options);
53 
54  // prevent conflicts with annotation ids for annotation where clause
55  $options['river_annotation_ids'] = elgg_extract('river_annotation_ids', $options, $options['annotation_ids']);
56  unset($options['annotation_ids']);
57 
58  parent::__construct($options);
59  }
60 
68  public static function find(array $options = []) {
69  return parent::find($options);
70  }
71 
75  public function count() {
77 
78  $count_expr = $this->options->distinct ? "DISTINCT {$qb->getTableAlias()}.id" : '*';
79  $qb->select("COUNT({$count_expr}) AS total");
80 
81  $qb = $this->buildQuery($qb);
82 
83  $result = _elgg_services()->db->getDataRow($qb);
84 
85  return $result ? (int) $result->total : 0;
86  }
87 
98  public function calculate($function, $property, $property_type = 'annotation') {
99  if (!in_array(strtolower($function), QueryBuilder::CALCULATIONS)) {
100  throw new DomainException("'{$function}' is not a valid numeric function");
101  }
102 
104 
106  if (!empty($this->options->annotation_name_value_pairs) && $this->options->annotation_name_value_pairs[0]->names != $property) {
107  $alias = $qb->getNextJoinAlias();
108 
109  $annotation = AnnotationWhereClause::factory(['names' => $property]);
110  $qb->addClause($annotation, $alias);
111  }
112 
113  $qb->joinAnnotationTable($qb->getTableAlias(), 'annotation_id', null, 'inner', $alias);
114  $qb->select("{$function}({$alias}.value) AS calculation");
115 
116  $qb = $this->buildQuery($qb);
117 
118  $result = _elgg_services()->db->getDataRow($qb);
119 
120  return $result ? (int) $result->calculation : 0;
121  }
122 
132  public function get($limit = null, $offset = null, $callback = null) {
134 
135  $distinct = $this->options->distinct ? 'DISTINCT ' : '';
136  $qb->select("{$distinct}{$qb->getTableAlias()}.*");
137 
138  $this->expandInto($qb, $qb->getTableAlias());
139 
140  $qb = $this->buildQuery($qb);
141 
142  // add default ordering
143  $original_order = elgg_extract('order_by', $this->options->__original_options);
144  if (empty($this->options->order_by) && $original_order !== false) {
145  $qb->addOrderBy("{$qb->getTableAlias()}.posted", 'desc');
146  }
147 
148  if ($limit > 0) {
149  $qb->setMaxResults((int) $limit);
150  $qb->setFirstResult((int) $offset);
151  }
152 
153  $callback = $callback ?: $this->options->callback;
154  if (!isset($callback)) {
155  $callback = function ($row) {
156  return new \ElggRiverItem($row);
157  };
158  }
159 
160  $items = _elgg_services()->db->getData($qb, $callback);
161 
162  if (!empty($items)) {
163  $preload = array_filter($items, function($e) {
164  return $e instanceof \ElggRiverItem;
165  });
166 
167  _elgg_services()->entityPreloader->preload($preload, [
168  'subject_guid',
169  'object_guid',
170  'target_guid',
171  ]);
172  }
173 
174  return $items;
175  }
176 
183  public function execute() {
184  if ($this->options->annotation_calculation) {
185  $clauses = $this->options->annotation_name_value_pairs;
186  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
187  throw new LogicException('Annotation calculation can not be performed on multiple annotation name value pairs merged with AND');
188  }
189 
190  $clause = array_shift($clauses);
191 
192  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
193  } elseif ($this->options->count) {
194  return $this->count();
195  } elseif ($this->options->batch) {
196  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
197  } else {
198  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
199  }
200  }
201 
209  protected function buildQuery(QueryBuilder $qb) {
210  $ands = [];
211 
212  foreach ($this->options->joins as $join) {
213  $join->prepare($qb, $qb->getTableAlias());
214  }
215 
216  foreach ($this->options->wheres as $where) {
217  $ands[] = $where->prepare($qb, $qb->getTableAlias());
218  }
219 
220  $ands[] = $this->buildRiverClause($qb);
221  $ands[] = $this->buildEntityClauses($qb);
222  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
223  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
224 
225  $ands = $qb->merge($ands);
226 
227  if (!empty($ands)) {
228  $qb->andWhere($ands);
229  }
230 
231  return $qb;
232  }
233 
241  protected function buildRiverClause(QueryBuilder $qb) {
242  $where = new RiverWhereClause();
243  $where->ids = $this->options->ids;
244  $where->views = $this->options->views;
245  $where->action_types = $this->options->action_types;
246  $where->subject_guids = $this->options->subject_guids;
247  $where->object_guids = $this->options->object_guids;
248  $where->target_guids = $this->options->target_guids;
249  $where->created_after = $this->options->created_after;
250  $where->created_before = $this->options->created_before;
251  $where->annotation_ids = $this->options->river_annotation_ids;
252 
253  return $where->prepare($qb, $qb->getTableAlias());
254  }
255 
264  public function buildEntityClauses($qb) {
265  $use_access_clause = !_elgg_services()->userCapabilities->canBypassPermissionsCheck();
266 
267  $ands = [];
268 
269  if (!empty($this->options->subject_guids) || $use_access_clause) {
270  $qb->joinEntitiesTable($qb->getTableAlias(), 'subject_guid', 'inner', 'se');
271  $subject = new EntityWhereClause();
272  $subject->guids = $this->options->subject_guids;
273  $ands[] = $subject->prepare($qb, 'se');
274  }
275 
276  if (!empty($this->options->object_guids) || $use_access_clause || !empty($this->options->type_subtype_pairs)) {
277  $qb->joinEntitiesTable($qb->getTableAlias(), 'object_guid', 'inner', 'oe');
278  $object = new EntityWhereClause();
279  $object->guids = $this->options->object_guids;
280  $object->type_subtype_pairs = $this->options->type_subtype_pairs;
281  $ands[] = $object->prepare($qb, 'oe');
282  }
283 
284  if (!empty($this->options->target_guids) || $use_access_clause) {
285  $target_ors = [];
286  $qb->joinEntitiesTable($qb->getTableAlias(), 'target_guid', 'left', 'te');
287  $target = new EntityWhereClause();
288  $target->guids = $this->options->target_guids;
289  $target_ors[] = $target->prepare($qb, 'te');
290  // Note the LEFT JOIN
291  $target_ors[] = $qb->compare('te.guid', 'IS NULL');
292  $ands[] = $qb->merge($target_ors, 'OR');
293  }
294 
295  return $qb->merge($ands);
296  }
297 
308  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
309  $parts = [];
310 
311  foreach ($clauses as $clause) {
312  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
313  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'annotation_id');
314  } else {
315  $joined_alias = $qb->joinAnnotationTable($qb->getTableAlias(), 'annotation_id', $clause->names);
316  }
317 
318  $parts[] = $clause->prepare($qb, $joined_alias);
319  }
320 
321  return $qb->merge($parts, $boolean);
322  }
323 
333  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
334  $parts = [];
335 
336  foreach ($clauses as $clause) {
337  $join_on = $clause->join_on === 'guid' ? 'subject_guid' : $clause->join_on;
338  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
339  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), $join_on, null, $clause->inverse, 'inner', RelationshipsTable::DEFAULT_JOIN_ALIAS);
340  } else {
341  $joined_alias = $qb->joinRelationshipTable($qb->getTableAlias(), $join_on, $clause->names, $clause->inverse);
342  }
343 
344  $parts[] = $clause->prepare($qb, $joined_alias);
345  }
346 
347  return $qb->merge($parts, $boolean);
348  }
349 }
if($id< 1) $annotation
Definition: delete.php:11
$items
Definition: delete.php:8
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 their properties in entity_relationships table.
Builds queries for matching river items against their properties.
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
River repository contains methods for fetching/counting river items.
Definition: River.php:18
buildEntityClauses($qb)
Add subject, object and target clauses Make sure all three are accessible by the user.
Definition: River.php:264
buildQuery(QueryBuilder $qb)
Build a database query.
Definition: River.php:209
buildRiverClause(QueryBuilder $qb)
Process river properties.
Definition: River.php:241
calculate($function, $property, $property_type='annotation')
Performs a mathematical calculation on river annotations.
Definition: River.php:98
count()
{Count rows.int}
Definition: River.php:75
buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process annotation name value pairs Joins the annotation table on entity guid in the entities table a...
Definition: River.php:308
__construct(array $options=[])
{Constructor.ege* options}
Definition: River.php:23
static find(array $options=[])
Build and execute a new query from an array of legacy options.
Definition: River.php:68
execute()
Execute the query resolving calculation, count and/or batch options.
Definition: River.php:183
buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean='AND')
Process relationship pairs.
Definition: River.php:333
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.
if($email instanceof \Elgg\Email) $object
Definition: body.php:24
$subject
HTML body of an email.
Definition: body.php:11
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: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
$defaults
Generic entity header upload helper.
Definition: header.php:6
$target
Definition: create.php:17
if(empty($count)) $offset
Definition: pagination.php:26
$limit
Definition: pagination.php:28
$qb
Definition: queue.php:14