3 namespace Drupal\Core\Entity\Query;
5 use Drupal\Core\Database\Query\PagerSelectExtender;
6 use Drupal\Core\Entity\EntityTypeInterface;
9 * The base entity query class.
11 abstract class QueryBase implements QueryInterface {
14 * The entity type this query runs against.
18 protected $entityTypeId;
21 * Information about the entity type.
23 * @var \Drupal\Core\Entity\EntityTypeInterface
25 protected $entityType;
35 * TRUE if this is a count query, FALSE if it isn't.
39 protected $count = FALSE;
44 * @var \Drupal\Core\Entity\Query\ConditionInterface
49 * The list of aggregate expressions.
53 protected $aggregate = [];
56 * The list of columns to group on.
60 protected $groupBy = [];
63 * Aggregate Conditions
65 * @var \Drupal\Core\Entity\Query\ConditionAggregateInterface
67 protected $conditionAggregate;
70 * The list of sorts over the aggregate results.
74 protected $sortAggregate = [];
81 protected $range = [];
84 * The query metadata for alter purposes.
88 protected $alterMetaData;
98 * Whether access check is requested or not. Defaults to TRUE.
102 protected $accessCheck = TRUE;
105 * Flag indicating whether to query the current revision or all revisions.
109 protected $allRevisions = FALSE;
112 * Flag indicating whether to query the latest revision.
116 protected $latestRevision = FALSE;
119 * The query pager data.
123 * @see Query::pager()
125 protected $pager = [];
128 * List of potential namespaces of the classes belonging to this query.
132 protected $namespaces = [];
135 * Constructs this object.
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.
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);
159 public function getEntityTypeId() {
160 return $this->entityTypeId;
166 public function condition($property, $value = NULL, $operator = NULL, $langcode = NULL) {
167 $this->condition->condition($property, $value, $operator, $langcode);
174 public function exists($property, $langcode = NULL) {
175 $this->condition->exists($property, $langcode);
182 public function notExists($property, $langcode = NULL) {
183 $this->condition->notExists($property, $langcode);
190 public function range($start = NULL, $length = NULL) {
199 * Creates an object holding a group of conditions.
201 * See andConditionGroup() and orConditionGroup() for more.
203 * @param string $conjunction
204 * - AND (default): this is the equivalent of andConditionGroup().
205 * - OR: this is the equivalent of orConditionGroup().
207 * @return \Drupal\Core\Entity\Query\ConditionInterface
208 * An object holding a group of conditions.
210 protected function conditionGroupFactory($conjunction = 'AND') {
211 $class = static::getClass($this->namespaces, 'Condition');
212 return new $class($conjunction, $this, $this->namespaces);
218 public function andConditionGroup() {
219 return $this->conditionGroupFactory('and');
225 public function orConditionGroup() {
226 return $this->conditionGroupFactory('or');
232 public function sort($field, $direction = 'ASC', $langcode = NULL) {
235 'direction' => strtoupper($direction),
236 'langcode' => $langcode,
244 public function count() {
252 public function accessCheck($access_check = TRUE) {
253 $this->accessCheck = $access_check;
260 public function currentRevision() {
261 $this->allRevisions = FALSE;
262 $this->latestRevision = FALSE;
269 public function latestRevision() {
270 $this->allRevisions = TRUE;
271 $this->latestRevision = TRUE;
278 public function allRevisions() {
279 $this->allRevisions = TRUE;
280 $this->latestRevision = FALSE;
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++;
293 elseif ($element >= PagerSelectExtender::$maxElement) {
294 PagerSelectExtender::$maxElement = $element + 1;
299 'element' => $element,
305 * Gets the total number of results and initialize a pager for the query.
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.
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']);
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'] = '';
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);
344 * Makes sure that the Condition object is cloned as well.
346 public function __clone() {
347 $this->condition = clone $this->condition;
353 public function addTag($tag) {
354 $this->alterTags[$tag] = 1;
361 public function hasTag($tag) {
362 return isset($this->alterTags[$tag]);
368 public function hasAllTags() {
369 return !(boolean) array_diff(func_get_args(), array_keys($this->alterTags));
375 public function hasAnyTag() {
376 return (boolean) array_intersect(func_get_args(), array_keys($this->alterTags));
382 public function addMetaData($key, $object) {
383 $this->alterMetaData[$key] = $object;
390 public function getMetaData($key) {
391 return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
397 public function aggregate($field, $function, $langcode = NULL, &$alias = NULL) {
398 if (!isset($alias)) {
399 $alias = $this->getAggregationAlias($field, $function);
402 $this->aggregate[$alias] = [
404 'function' => $function,
406 'langcode' => $langcode,
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);
425 public function sortAggregate($field, $function, $direction = 'ASC', $langcode = NULL) {
426 $alias = $this->getAggregationAlias($field, $function);
428 $this->sortAggregate[$alias] = [
430 'function' => $function,
431 'direction' => $direction,
432 'langcode' => $langcode,
434 $this->aggregate($field, $function, $langcode, $alias);
442 public function groupBy($field, $langcode = NULL) {
445 'langcode' => $langcode,
452 * Generates an alias for a field and its aggregated function.
454 * @param string $field
455 * The field name used in the alias.
456 * @param string $function
457 * The aggregation function used in the alias.
460 * The alias for the field.
462 protected function getAggregationAlias($field, $function) {
463 return strtolower($field . '_' . $function);
467 * Gets a list of namespaces of the ancestors of a class.
470 * An object within a namespace.
473 * A list containing the namespace of the class, the namespace of the
474 * parent of the class and so on and so on.
476 public static function getNamespaces($object) {
478 for ($class = get_class($object); $class; $class = get_parent_class($class)) {
479 $namespaces[] = substr($class, 0, strrpos($class, '\\'));
485 * Finds a class in a list of namespaces.
487 * @param array $namespaces
488 * A list of namespaces.
489 * @param string $short_class_name
490 * A class name without namespace.
493 * The fully qualified name of the class.
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)) {