Version 1
[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    * The query pager data.
113    *
114    * @var array
115    *
116    * @see Query::pager()
117    */
118   protected $pager = [];
119
120   /**
121    * List of potential namespaces of the classes belonging to this query.
122    *
123    * @var array
124    */
125   protected $namespaces = [];
126
127   /**
128    * Constructs this object.
129    *
130    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
131    *   The entity type definition.
132    * @param string $conjunction
133    *   - AND: all of the conditions on the query need to match.
134    *   - OR: at least one of the conditions on the query need to match.
135    * @param array $namespaces
136    *   List of potential namespaces of the classes belonging to this query.
137    */
138   public function __construct(EntityTypeInterface $entity_type, $conjunction, array $namespaces) {
139     $this->entityTypeId = $entity_type->id();
140     $this->entityType = $entity_type;
141     $this->conjunction = $conjunction;
142     $this->namespaces = $namespaces;
143     $this->condition = $this->conditionGroupFactory($conjunction);
144     if ($this instanceof QueryAggregateInterface) {
145       $this->conditionAggregate = $this->conditionAggregateGroupFactory($conjunction);
146     }
147   }
148
149   /**
150    * {@inheritdoc}
151    */
152   public function getEntityTypeId() {
153     return $this->entityTypeId;
154   }
155
156   /**
157    * {@inheritdoc}
158    */
159   public function condition($property, $value = NULL, $operator = NULL, $langcode = NULL) {
160     $this->condition->condition($property, $value, $operator, $langcode);
161     return $this;
162   }
163
164   /**
165    * {@inheritdoc}
166    */
167   public function exists($property, $langcode = NULL) {
168     $this->condition->exists($property, $langcode);
169     return $this;
170   }
171
172   /**
173    * {@inheritdoc}
174    */
175   public function notExists($property, $langcode = NULL) {
176     $this->condition->notExists($property, $langcode);
177     return $this;
178   }
179
180   /**
181    * {@inheritdoc}
182    */
183   public function range($start = NULL, $length = NULL) {
184     $this->range = [
185       'start' => $start,
186       'length' => $length,
187     ];
188     return $this;
189   }
190
191   /**
192    * Creates an object holding a group of conditions.
193    *
194    * See andConditionGroup() and orConditionGroup() for more.
195    *
196    * @param string $conjunction
197    *   - AND (default): this is the equivalent of andConditionGroup().
198    *   - OR: this is the equivalent of orConditionGroup().
199    *
200    * @return \Drupal\Core\Entity\Query\ConditionInterface
201    *   An object holding a group of conditions.
202    */
203   protected function conditionGroupFactory($conjunction = 'AND') {
204     $class = static::getClass($this->namespaces, 'Condition');
205     return new $class($conjunction, $this, $this->namespaces);
206   }
207
208   /**
209    * {@inheritdoc}
210    */
211   public function andConditionGroup() {
212     return $this->conditionGroupFactory('and');
213   }
214
215   /**
216    * {@inheritdoc}
217    */
218   public function orConditionGroup() {
219     return $this->conditionGroupFactory('or');
220   }
221
222   /**
223    * {@inheritdoc}
224    */
225   public function sort($field, $direction = 'ASC', $langcode = NULL) {
226     $this->sort[] = [
227       'field' => $field,
228       'direction' => strtoupper($direction),
229       'langcode' => $langcode,
230     ];
231     return $this;
232   }
233
234   /**
235    * {@inheritdoc}
236    */
237   public function count() {
238     $this->count = TRUE;
239     return $this;
240   }
241
242   /**
243    * {@inheritdoc}
244    */
245   public function accessCheck($access_check = TRUE) {
246     $this->accessCheck = $access_check;
247     return $this;
248   }
249
250   /**
251    * {@inheritdoc}
252    */
253   public function currentRevision() {
254     $this->allRevisions = FALSE;
255     return $this;
256   }
257
258   /**
259    * {@inheritdoc}
260    */
261   public function allRevisions() {
262     $this->allRevisions = TRUE;
263     return $this;
264   }
265
266   /**
267    * {@inheritdoc}
268    */
269   public function pager($limit = 10, $element = NULL) {
270     // Even when not using SQL, storing the element PagerSelectExtender is as
271     // good as anywhere else.
272     if (!isset($element)) {
273       $element = PagerSelectExtender::$maxElement++;
274     }
275     elseif ($element >= PagerSelectExtender::$maxElement) {
276       PagerSelectExtender::$maxElement = $element + 1;
277     }
278
279     $this->pager = [
280       'limit' => $limit,
281       'element' => $element,
282     ];
283     return $this;
284   }
285
286   /**
287    * Gets the total number of results and initialize a pager for the query.
288    *
289    * The pager can be disabled by either setting the pager limit to 0, or by
290    * setting this query to be a count query.
291    */
292   protected function initializePager() {
293     if ($this->pager && !empty($this->pager['limit']) && !$this->count) {
294       $page = pager_find_page($this->pager['element']);
295       $count_query = clone $this;
296       $this->pager['total'] = $count_query->count()->execute();
297       $this->pager['start'] = $page * $this->pager['limit'];
298       pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']);
299       $this->range($this->pager['start'], $this->pager['limit']);
300     }
301   }
302
303   /**
304    * {@inheritdoc}
305    */
306   public function tableSort(&$headers) {
307     // If 'field' is not initialized, the header columns aren't clickable.
308     foreach ($headers as $key => $header) {
309       if (is_array($header) && isset($header['specifier'])) {
310         $headers[$key]['field'] = '';
311       }
312     }
313
314     $order = tablesort_get_order($headers);
315     $direction = tablesort_get_sort($headers);
316     foreach ($headers as $header) {
317       if (is_array($header) && ($header['data'] == $order['name'])) {
318         $this->sort($header['specifier'], $direction, isset($header['langcode']) ? $header['langcode'] : NULL);
319       }
320     }
321
322     return $this;
323   }
324
325   /**
326    * Makes sure that the Condition object is cloned as well.
327    */
328   public function __clone() {
329     $this->condition = clone $this->condition;
330   }
331
332   /**
333    * {@inheritdoc}
334    */
335   public function addTag($tag) {
336     $this->alterTags[$tag] = 1;
337     return $this;
338   }
339
340   /**
341    * {@inheritdoc}
342    */
343   public function hasTag($tag) {
344     return isset($this->alterTags[$tag]);
345   }
346
347   /**
348    * {@inheritdoc}
349    */
350   public function hasAllTags() {
351     return !(boolean)array_diff(func_get_args(), array_keys($this->alterTags));
352   }
353
354   /**
355    * {@inheritdoc}
356    */
357   public function hasAnyTag() {
358     return (boolean)array_intersect(func_get_args(), array_keys($this->alterTags));
359   }
360
361   /**
362    * {@inheritdoc}
363    */
364   public function addMetaData($key, $object) {
365     $this->alterMetaData[$key] = $object;
366     return $this;
367   }
368
369   /**
370    * {@inheritdoc}
371    */
372   public function getMetaData($key) {
373     return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
374   }
375
376   /**
377    * {@inheritdoc}
378    */
379   public function aggregate($field, $function, $langcode = NULL, &$alias = NULL) {
380     if (!isset($alias)) {
381       $alias = $this->getAggregationAlias($field, $function);
382     }
383
384     $this->aggregate[$alias] = [
385       'field' => $field,
386       'function' => $function,
387       'alias' => $alias,
388       'langcode' => $langcode,
389     ];
390
391     return $this;
392   }
393
394   /**
395    * {@inheritdoc}
396    */
397   public function conditionAggregate($field, $function = NULL, $value = NULL, $operator = '=', $langcode = NULL) {
398     $this->aggregate($field, $function, $langcode);
399     $this->conditionAggregate->condition($field, $function, $value, $operator, $langcode);
400
401     return $this;
402   }
403
404   /**
405    * {@inheritdoc}
406    */
407   public function sortAggregate($field, $function, $direction = 'ASC', $langcode = NULL) {
408     $alias = $this->getAggregationAlias($field, $function);
409
410     $this->sortAggregate[$alias] = [
411       'field' => $field,
412       'function' => $function,
413       'direction' => $direction,
414       'langcode' => $langcode,
415     ];
416     $this->aggregate($field, $function, $langcode, $alias);
417
418     return $this;
419   }
420
421   /**
422    * {@inheritdoc}
423    */
424   public function groupBy($field, $langcode = NULL) {
425     $this->groupBy[] = [
426       'field' => $field,
427       'langcode' => $langcode,
428     ];
429
430     return $this;
431   }
432
433   /**
434    * Generates an alias for a field and it's aggregated function.
435    *
436    * @param string $field
437    *   The field name used in the alias.
438    * @param string $function
439    *   The aggregation function used in the alias.
440    *
441    * @return string
442    *   The alias for the field.
443    */
444   protected function getAggregationAlias($field, $function) {
445     return strtolower($field . '_' . $function);
446   }
447
448   /**
449    * Gets a list of namespaces of the ancestors of a class.
450    *
451    * @param $object
452    *   An object within a namespace.
453    *
454    * @return array
455    *   A list containing the namespace of the class, the namespace of the
456    *   parent of the class and so on and so on.
457    */
458   public static function getNamespaces($object) {
459     $namespaces = [];
460     for ($class = get_class($object); $class; $class = get_parent_class($class)) {
461       $namespaces[] = substr($class, 0, strrpos($class, '\\'));
462     }
463     return $namespaces;
464   }
465
466   /**
467    * Finds a class in a list of namespaces.
468    *
469    * @param array $namespaces
470    *   A list of namespaces.
471    * @param string $short_class_name
472    *   A class name without namespace.
473    *
474    * @return string
475    *   The fully qualified name of the class.
476    */
477   public static function getClass(array $namespaces, $short_class_name) {
478     foreach ($namespaces as $namespace) {
479       $class = $namespace . '\\' . $short_class_name;
480       if (class_exists($class)) {
481         return $class;
482       }
483     }
484   }
485
486 }