Elgg  Version 3.0
River.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
10 use ElggEntity;
11 use ElggRiverItem;
14 
22 class River extends Repository {
23 
27  public function __construct(array $options = []) {
28  $singulars = [
29  'id',
30  'subject_guid',
31  'object_guid',
32  'target_guid',
33  'annotation_id',
34  'action_type',
35  'type',
36  'subtype',
37  'view',
38  ];
39 
40  $options = LegacyQueryOptionsAdapter::normalizePluralOptions($options, $singulars);
41 
42  $defaults = [
43  'ids' => null,
44  'subject_guids' => null,
45  'object_guids' => null,
46  'target_guids' => null,
47  'annotation_ids' => null,
48  'views' => null,
49  'action_types' => null,
50  'posted_time_lower' => null,
51  'posted_time_upper' => null,
52  'limit' => 20,
53  'offset' => 0,
54  ];
55 
56  $options = array_merge($defaults, $options);
57  parent::__construct($options);
58  }
59 
67  public static function find(array $options = []) {
68  return parent::find($options);
69  }
70 
74  public function count() {
75  $qb = Select::fromTable('river', 'rv');
76 
77  $count_expr = $this->options->distinct ? "DISTINCT rv.id" : "*";
78  $qb->select("COUNT({$count_expr}) AS total");
79 
80  $qb = $this->buildQuery($qb);
81 
82  $result = _elgg_services()->db->getDataRow($qb);
83 
84  if (empty($result)) {
85  return 0;
86  }
87 
88  return (int) $result->total;
89  }
90 
101  public function calculate($function, $property, $property_type = 'annotation') {
102 
103  if (!in_array(strtolower($function), QueryBuilder::$calculations)) {
104  throw new InvalidArgumentException("'$function' is not a valid numeric function");
105  }
106 
107  $qb = Select::fromTable('river', 'rv');
108 
109  $alias = 'n_table';
110  if (!empty($this->options->annotation_name_value_pairs) && $this->options->annotation_name_value_pairs[0]->names != $property) {
111  $alias = $qb->getNextJoinAlias();
112 
114  $annotation->names = $property;
115  $qb->addClause($annotation, $alias);
116  }
117 
118  $qb->join('rv', 'annotations', $alias, "rv.annotation_id = $alias.id");
119  $qb->select("{$function}(n_table.value) AS calculation");
120 
121  $qb = $this->buildQuery($qb);
122 
123  $result = _elgg_services()->db->getDataRow($qb);
124 
125  if (empty($result)) {
126  return 0;
127  }
128 
129  return (int) $result->calculation;
130  }
131 
142  public function get($limit = null, $offset = null, $callback = null) {
143 
144  $qb = Select::fromTable('river', 'rv');
145 
146  $distinct = $this->options->distinct ? "DISTINCT" : "";
147  $qb->select("$distinct rv.*");
148 
149  $this->expandInto($qb, 'rv');
150 
151  $qb = $this->buildQuery($qb);
152 
153  // Keeping things backwards compatible
154  $original_order = elgg_extract('order_by', $this->options->__original_options);
155  if (empty($original_order) && $original_order !== false) {
156  $qb->addOrderBy('rv.posted', 'desc');
157  }
158 
159  if ($limit > 0) {
160  $qb->setMaxResults((int) $limit);
161  $qb->setFirstResult((int) $offset);
162  }
163 
164  $callback = $callback ? : $this->options->callback;
165  if (!isset($callback)) {
166  $callback = function ($row) {
167  return new ElggRiverItem($row);
168  };
169  }
170 
171  $items = _elgg_services()->db->getData($qb, $callback);
172 
173  if (!empty($items)) {
174  $preload = array_filter($items, function($e) {
175  return $e instanceof ElggRiverItem;
176  });
177 
178  _elgg_services()->entityPreloader->preload($preload, [
179  'subject_guid',
180  'object_guid',
181  'target_guid',
182  ]);
183  }
184 
185  return $items;
186  }
187 
194  public function execute() {
195 
196  if ($this->options->annotation_calculation) {
197  $clauses = $this->options->annotation_name_value_pairs;
198  if (count($clauses) > 1 && $this->options->annotation_name_value_pairs_operator !== 'OR') {
199  throw new \LogicException("Annotation calculation can not be performed on multiple annotation name value pairs merged with AND");
200  }
201 
202  $clause = array_shift($clauses);
203 
204  return $this->calculate($this->options->annotation_calculation, $clause->names, 'annotation');
205  } else if ($this->options->count) {
206  return $this->count();
207  } else if ($this->options->batch) {
208  return $this->batch($this->options->limit, $this->options->offset, $this->options->callback);
209  } else {
210  return $this->get($this->options->limit, $this->options->offset, $this->options->callback);
211  }
212  }
213 
221  protected function buildQuery(QueryBuilder $qb) {
222 
223  $ands = [];
224 
225  foreach ($this->options->joins as $join) {
226  $join->prepare($qb, 'rv');
227  }
228 
229  foreach ($this->options->wheres as $where) {
230  $ands[] = $where->prepare($qb, 'rv');
231  }
232 
233  $ands[] = $this->buildRiverClause($qb);
234  $ands[] = $this->buildEntityClauses($qb);
235  $ands[] = $this->buildPairedAnnotationClause($qb, $this->options->annotation_name_value_pairs, $this->options->annotation_name_value_pairs_operator);
236  $ands[] = $this->buildPairedRelationshipClause($qb, $this->options->relationship_pairs);
237 
238  $ands = $qb->merge($ands);
239 
240  if (!empty($ands)) {
241  $qb->andWhere($ands);
242  }
243 
244  return $qb;
245  }
246 
254  protected function buildRiverClause(QueryBuilder $qb) {
255  $where = new RiverWhereClause();
256  $where->ids = $this->options->ids;
257  $where->views = $this->options->views;
258  $where->action_types = $this->options->action_types;
259  $where->subject_guids = $this->options->subject_guids;
260  $where->object_guids = $this->options->object_guids;
261  $where->target_guids = $this->options->target_guids;
262  $where->created_after = $this->options->created_after;
263  $where->created_before = $this->options->created_before;
264 
265  return $where->prepare($qb, 'rv');
266  }
267 
276  public function buildEntityClauses($qb) {
277 
278  $use_access_clause = !_elgg_services()->userCapabilities->canBypassPermissionsCheck();
279 
280  $ands = [];
281 
282  if (!empty($this->options->subject_guids) || $use_access_clause) {
283  $qb->joinEntitiesTable('rv', 'subject_guid', 'inner', 'se');
284  $subject = new EntityWhereClause();
285  $subject->guids = $this->options->subject_guids;
286  $ands[] = $subject->prepare($qb, 'se');
287  }
288 
289  if (!empty($this->options->object_guids) || $use_access_clause || !empty($this->options->type_subtype_pairs)) {
290  $qb->joinEntitiesTable('rv', 'object_guid', 'inner', 'oe');
291  $object = new EntityWhereClause();
292  $object->guids = $this->options->object_guids;
293  $object->type_subtype_pairs = $this->options->type_subtype_pairs;
294  $ands[] = $object->prepare($qb, 'oe');
295  }
296 
297  if (!empty($this->options->target_guids) || $use_access_clause) {
298  $target_ors = [];
299  $qb->joinEntitiesTable('rv', 'target_guid', 'left', 'te');
300  $target = new EntityWhereClause();
301  $target->guids = $this->options->target_guids;
302  $target_ors[] = $target->prepare($qb, 'te');
303  // Note the LEFT JOIN
304  $target_ors[] = $qb->compare('te.guid', 'IS NULL');
305  $ands[] = $qb->merge($target_ors, 'OR');
306  }
307 
308  return $qb->merge($ands);
309  }
310 
321  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
322  $parts = [];
323 
324  foreach ($clauses as $clause) {
325  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
326  $joined_alias = 'n_table';
327  } else {
328  $joined_alias = $qb->getNextJoinAlias();
329  }
330  $joins = $qb->getQueryPart('join');
331  $is_joined = false;
332  if (!empty($joins['rv'])) {
333  foreach ($joins['rv'] as $join) {
334  if ($join['joinAlias'] === $joined_alias) {
335  $is_joined = true;
336  }
337  }
338  }
339 
340  if (!$is_joined) {
341  $qb->join('rv', 'annotations', $joined_alias, "$joined_alias.id = rv.annotation_id");
342  }
343 
344  $parts[] = $clause->prepare($qb, $joined_alias);
345  }
346 
347  return $qb->merge($parts, $boolean);
348  }
349 
359  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
360  $parts = [];
361 
362  foreach ($clauses as $clause) {
363  $join_on = $clause->join_on === 'guid' ? 'subject_guid' : $clause->join_on;
364  if (strtoupper($boolean) == 'OR' || count($clauses) === 1) {
365  $joined_alias = $qb->joinRelationshipTable('rv', $join_on, null, $clause->inverse, 'inner', 'r');
366  } else {
367  $joined_alias = $qb->joinRelationshipTable('rv', $join_on, $clause->names, $clause->inverse);
368  }
369  $parts[] = $clause->prepare($qb, $joined_alias);
370  }
371 
372  return $qb->merge($parts, $boolean);
373  }
374 }
if(!$item instanceof ElggRiverItem) $object
Definition: responses.php:23
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:96
$annotation
Elgg default annotation view.
Definition: default.php:10
calculate($function, $property, $property_type= 'annotation')
Performs a mathematical calculation on river annotations.
Definition: River.php:101
$defaults
buildEntityClauses($qb)
Add subject, object and target clauses Make sure all three are accessible by the user.
Definition: River.php:276
if(!$count) $offset
Definition: pagination.php:26
Database abstraction query builder.
$items
Definition: delete.php:8
buildQuery(QueryBuilder $qb)
Build a database query.
Definition: River.php:221
Builds queries for matching annotations against their properties.
execute()
Execute the query resolving calculation, count and/or batch options.
Definition: River.php:194
buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean= 'AND')
Process relationship pairs.
Definition: River.php:359
River repository contains methods for fetching/counting river items.
Definition: River.php:22
Abstract methods for interfacing with the database.
Definition: Repository.php:15
$limit
Definition: userpicker.php:52
Builds queries for matching river items against their properties.
count()
{Count rows.int}
Definition: River.php:74
Builds queries for filtering entities by their properties in the entities table.
join($fromAlias, $join, $alias, $condition=null)
{}
expandInto(QueryBuilder $qb, $table_alias=null)
Extend query builder with select, group_by, having and order_by clauses from $options.
Definition: Repository.php:180
if(!($comment instanceof\ElggComment)||!$comment->canEdit()) $target
Definition: edit.php:17
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:321
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:1131
getNextJoinAlias()
Get an index of the next available join alias.
static fromTable($table, $alias=null)
{}
Definition: Select.php:13
merge($parts=null, $boolean= 'AND')
Merges multiple composite expressions with a boolean.
buildRiverClause(QueryBuilder $qb)
Process river properties.
Definition: River.php:254
_elgg_services()
Get the global service provider.
Definition: elgglib.php:1292
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.
static find(array $options=[])
Build and execute a new query from an array of legacy options.
Definition: River.php:67
__construct(array $options=[])
{}
Definition: River.php:27
elgg ElggEntity
Definition: ElggEntity.js:15
$subject
Definition: useradd.php:59