Elgg  Version 4.3
River.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Database;
4 
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  'posted_time_lower' => null,
47  'posted_time_upper' => 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() {
76  $qb = Select::fromTable('river', 'rv');
77 
78  $count_expr = $this->options->distinct ? "DISTINCT rv.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  if (empty($result)) {
86  return 0;
87  }
88 
89  return (int) $result->total;
90  }
91 
102  public function calculate($function, $property, $property_type = 'annotation') {
103 
104  if (!in_array(strtolower($function), QueryBuilder::$calculations)) {
105  throw new InvalidArgumentException("'{$function}' is not a valid numeric function");
106  }
107 
108  $qb = Select::fromTable('river', 'rv');
109 
110  $alias = 'n_table';
111  if (!empty($this->options->annotation_name_value_pairs) && $this->options->annotation_name_value_pairs[0]->names != $property) {
112  $alias = $qb->getNextJoinAlias();
113 
115  $annotation->names = $property;
116  $qb->addClause($annotation, $alias);
117  }
118 
119  $qb->join('rv', 'annotations', $alias, "rv.annotation_id = $alias.id");
120  $qb->select("{$function}(n_table.value) AS calculation");
121 
122  $qb = $this->buildQuery($qb);
123 
124  $result = _elgg_services()->db->getDataRow($qb);
125 
126  if (empty($result)) {
127  return 0;
128  }
129 
130  return (int) $result->calculation;
131  }
132 
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  $where->annotation_ids = $this->options->river_annotation_ids;
265 
266  return $where->prepare($qb, 'rv');
267  }
268 
277  public function buildEntityClauses($qb) {
278 
279  $use_access_clause = !_elgg_services()->userCapabilities->canBypassPermissionsCheck();
280 
281  $ands = [];
282 
283  if (!empty($this->options->subject_guids) || $use_access_clause) {
284  $qb->joinEntitiesTable('rv', 'subject_guid', 'inner', 'se');
285  $subject = new EntityWhereClause();
286  $subject->guids = $this->options->subject_guids;
287  $ands[] = $subject->prepare($qb, 'se');
288  }
289 
290  if (!empty($this->options->object_guids) || $use_access_clause || !empty($this->options->type_subtype_pairs)) {
291  $qb->joinEntitiesTable('rv', 'object_guid', 'inner', 'oe');
292  $object = new EntityWhereClause();
293  $object->guids = $this->options->object_guids;
294  $object->type_subtype_pairs = $this->options->type_subtype_pairs;
295  $ands[] = $object->prepare($qb, 'oe');
296  }
297 
298  if (!empty($this->options->target_guids) || $use_access_clause) {
299  $target_ors = [];
300  $qb->joinEntitiesTable('rv', 'target_guid', 'left', 'te');
301  $target = new EntityWhereClause();
302  $target->guids = $this->options->target_guids;
303  $target_ors[] = $target->prepare($qb, 'te');
304  // Note the LEFT JOIN
305  $target_ors[] = $qb->compare('te.guid', 'IS NULL');
306  $ands[] = $qb->merge($target_ors, 'OR');
307  }
308 
309  return $qb->merge($ands);
310  }
311 
322  protected function buildPairedAnnotationClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
323  $parts = [];
324 
325  foreach ($clauses as $clause) {
326  if (strtoupper($boolean) === 'OR' || count($clauses) === 1) {
327  $joined_alias = 'n_table';
328  } else {
329  $joined_alias = $qb->getNextJoinAlias();
330  }
331  $joins = $qb->getQueryPart('join');
332  $is_joined = false;
333  if (!empty($joins['rv'])) {
334  foreach ($joins['rv'] as $join) {
335  if ($join['joinAlias'] === $joined_alias) {
336  $is_joined = true;
337  }
338  }
339  }
340 
341  if (!$is_joined) {
342  $qb->join('rv', 'annotations', $joined_alias, "$joined_alias.id = rv.annotation_id");
343  }
344 
345  $parts[] = $clause->prepare($qb, $joined_alias);
346  }
347 
348  return $qb->merge($parts, $boolean);
349  }
350 
360  protected function buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean = 'AND') {
361  $parts = [];
362 
363  foreach ($clauses as $clause) {
364  $join_on = $clause->join_on === 'guid' ? 'subject_guid' : $clause->join_on;
365  if (strtoupper($boolean) == 'OR' || count($clauses) === 1) {
366  $joined_alias = $qb->joinRelationshipTable('rv', $join_on, null, $clause->inverse, 'inner', 'r');
367  } else {
368  $joined_alias = $qb->joinRelationshipTable('rv', $join_on, $clause->names, $clause->inverse);
369  }
370  $parts[] = $clause->prepare($qb, $joined_alias);
371  }
372 
373  return $qb->merge($parts, $boolean);
374  }
375 }
if(!$comment instanceof\ElggComment||!$comment->canEdit()) $target
Definition: edit.php:13
Exception thrown if an argument is not of the expected type.
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
calculate($function, $property, $property_type= 'annotation')
Performs a mathematical calculation on river annotations.
Definition: River.php:102
if($id< 1) $annotation
Definition: delete.php:11
$defaults
buildEntityClauses($qb)
Add subject, object and target clauses Make sure all three are accessible by the user.
Definition: River.php:277
if(!$count) $offset
Definition: pagination.php:26
Database abstraction query builder.
buildQuery(QueryBuilder $qb)
Build a database query.
Definition: River.php:221
if($pagination &&($position== 'after'||$position== 'both')) $limit
Definition: list.php:108
Builds queries for matching annotations against their properties.
execute()
Execute the query resolving calculation, count and/or batch options.
Definition: River.php:194
$items
Definition: delete.php:8
buildPairedRelationshipClause(QueryBuilder $qb, $clauses, $boolean= 'AND')
Process relationship pairs.
Definition: River.php:360
River repository contains methods for fetching/counting river items.
Definition: River.php:18
Abstract methods for interfacing with the database.
Definition: Repository.php:16
Builds queries for matching river items against their properties.
count()
{Count rows.int}
Definition: River.php:75
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:181
Exception that represents error in the program logic.
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:322
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:547
getNextJoinAlias()
Get an index of the next available join alias.
static fromTable($table, $alias=null)
{}
Definition: Select.php:13
if($email instanceof\Elgg\Email) $object
Definition: body.php:24
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:638
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:68
__construct(array $options=[])
{}
Definition: River.php:23
$qb
Definition: queue.php:11
$subject
Definition: useradd.php:58