3 namespace Drupal\entity\QueryAccess;
5 use Drupal\Core\Cache\Cache;
6 use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
7 use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
10 * Represents a group of query access conditions.
12 * Used by query access handlers for filtering lists of entities based on
13 * granted permissions.
17 * // Filter by node type and uid.
18 * $condition_group = new ConditionGroup();
19 * $condition_group->addCondition('type', ['article', 'page']);
20 * $condition_group->addCondition('uid', '1');
22 * // Filter by node type or status.
23 * $condition_group = new ConditionGroup('OR');
24 * $condition_group->addCondition('type', ['article', 'page']);
25 * $condition_group->addCondition('status', '1', '<>');
27 * // Nested condition groups: node type AND (uid OR status).
28 * $condition_group = new ConditionGroup();
29 * $condition_group->addCondition('type', ['article', 'page']);
30 * $condition_group->addCondition((new ConditionGroup('OR'))
31 * ->addCondition('uid', 1)
32 * ->addCondition('status', '1')
36 final class ConditionGroup implements \Countable, RefinableCacheableDependencyInterface {
38 use RefinableCacheableDependencyTrait;
45 protected $conjunction;
50 * @var \Drupal\entity\QueryAccess\Condition[]|\Drupal\entity\QueryAccess\ConditionGroup[]
52 protected $conditions = [];
55 * Whether the condition group is always FALSE.
59 protected $alwaysFalse = FALSE;
62 * Constructs a new ConditionGroup object.
64 * @param string $conjunction
67 public function __construct($conjunction = 'AND') {
68 $this->conjunction = $conjunction;
72 * Gets the conjunction.
75 * The conjunction. Possible values: AND, OR.
77 public function getConjunction() {
78 return $this->conjunction;
82 * Gets all conditions and nested condition groups.
84 * @return \Drupal\entity\QueryAccess\Condition[]|\Drupal\entity\QueryAccess\ConditionGroup[]
85 * The conditions, where each one is either a Condition or a nested
86 * ConditionGroup. Returned by reference, to allow callers to replace
87 * or remove conditions.
89 public function &getConditions() {
90 return $this->conditions;
96 * @param string|\Drupal\entity\QueryAccess\ConditionGroup $field
97 * Either a condition group (for nested AND/OR conditions), or a
98 * field name with an optional column name. E.g: 'uid', 'address.locality'.
101 * @param string $operator
103 * Possible values: =, <>, <, <=, >, >=, BETWEEN, NOT BETWEEN,
104 * IN, NOT IN, IS NULL, IS NOT NULL.
108 public function addCondition($field, $value = NULL, $operator = NULL) {
109 if ($field instanceof ConditionGroup) {
110 if ($field->count() === 1) {
111 // The condition group only has a single condition, merge it.
112 $this->conditions[] = reset($field->getConditions());
113 $this->addCacheTags($field->getCacheTags());
114 $this->addCacheContexts($field->getCacheContexts());
115 $this->mergeCacheMaxAge($field->getCacheMaxAge());
117 elseif ($field->count() > 1) {
118 $this->conditions[] = $field;
122 $this->conditions[] = new Condition($field, $value, $operator);
129 * Gets whether the condition group is always FALSE.
131 * Used when the user doesn't have access to any entities, to ensure that a
132 * query returns no results.
135 * Whether the condition group is always FALSE.
137 public function isAlwaysFalse() {
138 return $this->alwaysFalse;
142 * Sets whether the condition group should always be FALSE.
144 * @param bool $always_false
145 * Whether the condition group should always be FALSE.
149 public function alwaysFalse($always_false = TRUE) {
150 $this->alwaysFalse = $always_false;
155 * Clones the contained conditions when the condition group is cloned.
157 public function __clone() {
158 foreach ($this->conditions as $i => $condition) {
159 $this->conditions[$i] = clone $condition;
164 * Gets the string representation of the condition group.
167 * The string representation of the condition group.
169 public function __toString() {
170 // Special case for a single, nested condition group:
171 if (count($this->conditions) == 1) {
172 return (string) reset($this->conditions);
175 foreach ($this->conditions as $condition) {
176 $lines[] = str_replace("\n", "\n ", (string) $condition);
178 return $lines ? "(\n " . implode("\n {$this->conjunction}\n ", $lines) . "\n)" : '';
184 public function count() {
185 return count($this->conditions);
191 public function getCacheTags() {
192 $tags = $this->cacheTags;
193 foreach ($this->conditions as $condition) {
194 if ($condition instanceof ConditionGroup) {
195 $tags = array_merge($tags, $condition->getCacheTags());
198 return Cache::mergeTags($tags, []);
204 public function getCacheContexts() {
205 $cache_contexts = $this->cacheContexts;
206 foreach ($this->conditions as $condition) {
207 if ($condition instanceof ConditionGroup) {
208 $cache_contexts = array_merge($cache_contexts, $condition->getCacheContexts());
211 return Cache::mergeContexts($cache_contexts);
217 public function getCacheMaxAge() {
218 $max_age = $this->cacheMaxAge;
219 foreach ($this->conditions as $condition) {
220 if ($condition instanceof ConditionGroup) {
221 $max_age = Cache::mergeMaxAges($max_age, $condition->getCacheMaxAge());