Further Drupal 8.6.4 changes. Some core files were not committed before a commit...
[yaffs-website] / web / core / lib / Drupal / Core / Entity / Query / QueryBase.php
1 <?php
2
3 namespace Drupal\Core\Entity\Query;
4
5 use Drupal\Core\Database\Query\PagerSelectExtender;
6 use Drupal\Core\Entity\EntityTypeInterface;
7
8 /**
9  * The base entity query class.
10  */
11 abstract class QueryBase implements QueryInterface {
12
13   /**
14    * The entity type this query runs against.
15    *
16    * @var string
17    */
18   protected $entityTypeId;
19
20   /**
21    * Information about the entity type.
22    *
23    * @var \Drupal\Core\Entity\EntityTypeInterface
24    */
25   protected $entityType;
26
27   /**
28    * The list of sorts.
29    *
30    * @var array
31    */
32   protected $sort = [];
33
34   /**
35    * TRUE if this is a count query, FALSE if it isn't.
36    *
37    * @var bool
38    */
39   protected $count = FALSE;
40
41   /**
42    * Conditions.
43    *
44    * @var \Drupal\Core\Entity\Query\ConditionInterface
45    */
46   protected $condition;
47
48   /**
49    * The list of aggregate expressions.
50    *
51    * @var array
52    */
53   protected $aggregate = [];
54
55   /**
56    * The list of columns to group on.
57    *
58    * @var array
59    */
60   protected $groupBy = [];
61
62   /**
63    * Aggregate Conditions
64    *
65    * @var \Drupal\Core\Entity\Query\ConditionAggregateInterface
66    */
67   protected $conditionAggregate;
68
69   /**
70    * The list of sorts over the aggregate results.
71    *
72    * @var array
73    */
74   protected $sortAggregate = [];
75
76   /**
77    * The query range.
78    *
79    * @var array
80    */
81   protected $range = [];
82
83   /**
84    * The query metadata for alter purposes.
85    *
86    * @var array
87    */
88   protected $alterMetaData;
89
90   /**
91    * The query tags.
92    *
93    * @var array
94    */
95   protected $alterTags;
96
97   /**
98    * Whether access check is requested or not. Defaults to TRUE.
99    *
100    * @var bool
101    */
102   protected $accessCheck = TRUE;
103
104   /**
105    * Flag indicating whether to query the current revision or all revisions.
106    *
107    * @var bool
108    */
109   protected $allRevisions = FALSE;
110
111   /**
112    * Flag indicating whether to query the latest revision.
113    *
114    * @var bool
115    */
116   protected $latestRevision = FALSE;
117
118   /**
119    * The query pager data.
120    *
121    * @var array
122    *
123    * @see Query::pager()
124    */
125   protected $pager = [];
126
127   /**
128    * List of potential namespaces of the classes belonging to this query.
129    *
130    * @var array
131    */
132   protected $namespaces = [];
133
134   /**
135    * Constructs this object.
136    *
137    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
138    *   The entity type definition.
139    * @param string $conjunction
140    *   - AND: all of the conditions on the query need to match.
141    *   - OR: at least one of the conditions on the query need to match.
142    * @param array $namespaces
143    *   List of potential namespaces of the classes belonging to this query.
144    */
145   public function __construct(EntityTypeInterface $entity_type, $conjunction, array $namespaces) {
146     $this->entityTypeId = $entity_type->id();
147     $this->entityType = $entity_type;
148     $this->conjunction = $conjunction;
149     $this->namespaces = $namespaces;
150     $this->condition = $this->conditionGroupFactory($conjunction);
151     if ($this instanceof QueryAggregateInterface) {
152       $this->conditionAggregate = $this->conditionAggregateGroupFactory($conjunction);
153     }
154   }
155
156   /**
157    * {@inheritdoc}
158    */
159   public function getEntityTypeId() {
160     return $this->entityTypeId;
161   }
162
163   /**
164    * {@inheritdoc}
165    */
166   public function condition($property, $value = NULL, $operator = NULL, $langcode = NULL) {
167     $this->condition->condition($property, $value, $operator, $langcode);
168     return $this;
169   }
170
171   /**
172    * {@inheritdoc}
173    */
174   public function exists($property, $langcode = NULL) {
175     $this->condition->exists($property, $langcode);
176     return $this;
177   }
178
179   /**
180    * {@inheritdoc}
181    */
182   public function notExists($property, $langcode = NULL) {
183     $this->condition->notExists($property, $langcode);
184     return $this;
185   }
186
187   /**
188    * {@inheritdoc}
189    */
190   public function range($start = NULL, $length = NULL) {
191     $this->range = [
192       'start' => $start,
193       'length' => $length,
194     ];
195     return $this;
196   }
197
198   /**
199    * Creates an object holding a group of conditions.
200    *
201    * See andConditionGroup() and orConditionGroup() for more.
202    *
203    * @param string $conjunction
204    *   - AND (default): this is the equivalent of andConditionGroup().
205    *   - OR: this is the equivalent of orConditionGroup().
206    *
207    * @return \Drupal\Core\Entity\Query\ConditionInterface
208    *   An object holding a group of conditions.
209    */
210   protected function conditionGroupFactory($conjunction = 'AND') {
211     $class = static::getClass($this->namespaces, 'Condition');
212     return new $class($conjunction, $this, $this->namespaces);
213   }
214
215   /**
216    * {@inheritdoc}
217    */
218   public function andConditionGroup() {
219     return $this->conditionGroupFactory('and');
220   }
221
222   /**
223    * {@inheritdoc}
224    */
225   public function orConditionGroup() {
226     return $this->conditionGroupFactory('or');
227   }
228
229   /**
230    * {@inheritdoc}
231    */
232   public function sort($field, $direction = 'ASC', $langcode = NULL) {
233     $this->sort[] = [
234       'field' => $field,
235       'direction' => strtoupper($direction),
236       'langcode' => $langcode,
237     ];
238     return $this;
239   }
240
241   /**
242    * {@inheritdoc}
243    */
244   public function count() {
245     $this->count = TRUE;
246     return $this;
247   }
248
249   /**
250    * {@inheritdoc}
251    */
252   public function accessCheck($access_check = TRUE) {
253     $this->accessCheck = $access_check;
254     return $this;
255   }
256
257   /**
258    * {@inheritdoc}
259    */
260   public function currentRevision() {
261     $this->allRevisions = FALSE;
262     $this->latestRevision = FALSE;
263     return $this;
264   }
265
266   /**
267    * {@inheritdoc}
268    */
269   public function latestRevision() {
270     $this->allRevisions = TRUE;
271     $this->latestRevision = TRUE;
272     return $this;
273   }
274
275   /**
276    * {@inheritdoc}
277    */
278   public function allRevisions() {
279     $this->allRevisions = TRUE;
280     $this->latestRevision = FALSE;
281     return $this;
282   }
283
284   /**
285    * {@inheritdoc}
286    */
287   public function pager($limit = 10, $element = NULL) {
288     // Even when not using SQL, storing the element PagerSelectExtender is as
289     // good as anywhere else.
290     if (!isset($element)) {
291       $element = PagerSelectExtender::$maxElement++;
292     }
293     elseif ($element >= PagerSelectExtender::$maxElement) {
294       PagerSelectExtender::$maxElement = $element + 1;
295     }
296
297     $this->pager = [
298       'limit' => $limit,
299       'element' => $element,
300     ];
301     return $this;
302   }
303
304   /**
305    * Gets the total number of results and initialize a pager for the query.
306    *
307    * The pager can be disabled by either setting the pager limit to 0, or by
308    * setting this query to be a count query.
309    */
310   protected function initializePager() {
311     if ($this->pager && !empty($this->pager['limit']) && !$this->count) {
312       $page = pager_find_page($this->pager['element']);
313       $count_query = clone $this;
314       $this->pager['total'] = $count_query->count()->execute();
315       $this->pager['start'] = $page * $this->pager['limit'];
316       pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']);
317       $this->range($this->pager['start'], $this->pager['limit']);
318     }
319   }
320
321   /**
322    * {@inheritdoc}
323    */
324   public function tableSort(&$headers) {
325     // If 'field' is not initialized, the header columns aren't clickable.
326     foreach ($headers as $key => $header) {
327       if (is_array($header) && isset($header['specifier'])) {
328         $headers[$key]['field'] = '';
329       }
330     }
331
332     $order = tablesort_get_order($headers);
333     $direction = tablesort_get_sort($headers);
334     foreach ($headers as $header) {
335       if (is_array($header) && ($header['data'] == $order['name'])) {
336         $this->sort($header['specifier'], $direction, isset($header['langcode']) ? $header['langcode'] : NULL);
337       }
338     }
339
340     return $this;
341   }
342
343   /**
344    * Makes sure that the Condition object is cloned as well.
345    */
346   public function __clone() {
347     $this->condition = clone $this->condition;
348   }
349
350   /**
351    * {@inheritdoc}
352    */
353   public function addTag($tag) {
354     $this->alterTags[$tag] = 1;
355     return $this;
356   }
357
358   /**
359    * {@inheritdoc}
360    */
361   public function hasTag($tag) {
362     return isset($this->alterTags[$tag]);
363   }
364
365   /**
366    * {@inheritdoc}
367    */
368   public function hasAllTags() {
369     return !(boolean) array_diff(func_get_args(), array_keys($this->alterTags));
370   }
371
372   /**
373    * {@inheritdoc}
374    */
375   public function hasAnyTag() {
376     return (boolean) array_intersect(func_get_args(), array_keys($this->alterTags));
377   }
378
379   /**
380    * {@inheritdoc}
381    */
382   public function addMetaData($key, $object) {
383     $this->alterMetaData[$key] = $object;
384     return $this;
385   }
386
387   /**
388    * {@inheritdoc}
389    */
390   public function getMetaData($key) {
391     return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
392   }
393
394   /**
395    * {@inheritdoc}
396    */
397   public function aggregate($field, $function, $langcode = NULL, &$alias = NULL) {
398     if (!isset($alias)) {
399       $alias = $this->getAggregationAlias($field, $function);
400     }
401
402     $this->aggregate[$alias] = [
403       'field' => $field,
404       'function' => $function,
405       'alias' => $alias,
406       'langcode' => $langcode,
407     ];
408
409     return $this;
410   }
411
412   /**
413    * {@inheritdoc}
414    */
415   public function conditionAggregate($field, $function = NULL, $value = NULL, $operator = '=', $langcode = NULL) {
416     $this->aggregate($field, $function, $langcode);
417     $this->conditionAggregate->condition($field, $function, $value, $operator, $langcode);
418
419     return $this;
420   }
421
422   /**
423    * {@inheritdoc}
424    */
425   public function sortAggregate($field, $function, $direction = 'ASC', $langcode = NULL) {
426     $alias = $this->getAggregationAlias($field, $function);
427
428     $this->sortAggregate[$alias] = [
429       'field' => $field,
430       'function' => $function,
431       'direction' => $direction,
432       'langcode' => $langcode,
433     ];
434     $this->aggregate($field, $function, $langcode, $alias);
435
436     return $this;
437   }
438
439   /**
440    * {@inheritdoc}
441    */
442   public function groupBy($field, $langcode = NULL) {
443     $this->groupBy[] = [
444       'field' => $field,
445       'langcode' => $langcode,
446     ];
447
448     return $this;
449   }
450
451   /**
452    * Generates an alias for a field and its aggregated function.
453    *
454    * @param string $field
455    *   The field name used in the alias.
456    * @param string $function
457    *   The aggregation function used in the alias.
458    *
459    * @return string
460    *   The alias for the field.
461    */
462   protected function getAggregationAlias($field, $function) {
463     return strtolower($field . '_' . $function);
464   }
465
466   /**
467    * Gets a list of namespaces of the ancestors of a class.
468    *
469    * @param $object
470    *   An object within a namespace.
471    *
472    * @return array
473    *   A list containing the namespace of the class, the namespace of the
474    *   parent of the class and so on and so on.
475    */
476   public static function getNamespaces($object) {
477     $namespaces = [];
478     for ($class = get_class($object); $class; $class = get_parent_class($class)) {
479       $namespaces[] = substr($class, 0, strrpos($class, '\\'));
480     }
481     return $namespaces;
482   }
483
484   /**
485    * Finds a class in a list of namespaces.
486    *
487    * @param array $namespaces
488    *   A list of namespaces.
489    * @param string $short_class_name
490    *   A class name without namespace.
491    *
492    * @return string
493    *   The fully qualified name of the class.
494    */
495   public static function getClass(array $namespaces, $short_class_name) {
496     foreach ($namespaces as $namespace) {
497       $class = $namespace . '\\' . $short_class_name;
498       if (class_exists($class)) {
499         return $class;
500       }
501     }
502   }
503
504 }