3 namespace Drupal\Core\Config\Entity\Query;
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\Core\Entity\Query\ConditionBase;
7 use Drupal\Core\Entity\Query\ConditionInterface;
8 use Drupal\Core\Entity\Query\QueryException;
11 * Defines the condition class for the config entity query.
13 * @see \Drupal\Core\Config\Entity\Query\Query
15 class Condition extends ConditionBase {
20 public function compile($configs) {
21 $and = strtoupper($this->conjunction) == 'AND';
22 $single_conditions = [];
23 $condition_groups = [];
24 foreach ($this->conditions as $condition) {
25 if ($condition['field'] instanceof ConditionInterface) {
26 $condition_groups[] = $condition;
29 if (!isset($condition['operator'])) {
30 $condition['operator'] = is_array($condition['value']) ? 'IN' : '=';
33 // Lowercase condition value(s) for case-insensitive matches.
34 if (is_array($condition['value'])) {
35 $condition['value'] = array_map('Drupal\Component\Utility\Unicode::strtolower', $condition['value']);
37 elseif (!is_bool($condition['value'])) {
38 $condition['value'] = Unicode::strtolower($condition['value']);
41 $single_conditions[] = $condition;
45 if ($single_conditions) {
46 foreach ($configs as $config_name => $config) {
47 foreach ($single_conditions as $condition) {
48 $match = $this->matchArray($condition, $config, explode('.', $condition['field']));
49 // If AND and it's not matching, then the rest of conditions do not
50 // matter and this config object does not match.
51 // If OR and it is matching, then the rest of conditions do not
52 // matter and this config object does match.
58 $return[$config_name] = $config;
62 elseif (!$condition_groups || $and) {
63 // If there were no single conditions then either:
64 // - Complex conditions, OR: need to start from no entities.
65 // - Complex conditions, AND: need to start from all entities.
66 // - No complex conditions (AND/OR doesn't matter): need to return all
70 foreach ($condition_groups as $condition) {
71 $group_entities = $condition['field']->compile($configs);
73 $return = array_intersect_key($return, $group_entities);
76 $return = $return + $group_entities;
86 public function exists($field, $langcode = NULL) {
87 return $this->condition($field, NULL, 'IS NOT NULL', $langcode);
93 public function notExists($field, $langcode = NULL) {
94 return $this->condition($field, NULL, 'IS NULL', $langcode);
98 * Matches for an array representing one or more config paths.
100 * @param array $condition
101 * The condition array as created by the condition() method.
103 * The config array or part of it.
104 * @param array $needs_matching
105 * The list of config array keys needing a match. Can contain config keys
106 * and the * wildcard.
107 * @param array $parents
108 * The current list of parents.
111 * TRUE when the condition matched to the data else FALSE.
113 protected function matchArray(array $condition, array $data, array $needs_matching, array $parents = []) {
114 $parent = array_shift($needs_matching);
115 if ($parent === '*') {
116 $candidates = array_keys($data);
119 // Avoid a notice when calling match() later.
120 if (!isset($data[$parent])) {
121 $data[$parent] = NULL;
123 $candidates = [$parent];
125 foreach ($candidates as $key) {
126 if ($needs_matching) {
127 if (is_array($data[$key])) {
128 $new_parents = $parents;
129 $new_parents[] = $key;
130 if ($this->matchArray($condition, $data[$key], $needs_matching, $new_parents)) {
135 // Only try to match a scalar if there are no remaining keys in
136 // $needs_matching as this indicates that we are looking for a specific
137 // subkey and a scalar can never match that.
138 elseif ($this->match($condition, $data[$key])) {
146 * Perform the actual matching.
148 * @param array $condition
149 * The condition array as created by the condition() method.
150 * @param string $value
151 * The value to match against.
154 * TRUE when matches else FALSE.
156 protected function match(array $condition, $value) {
157 // "IS NULL" and "IS NOT NULL" conditions can also deal with array values,
158 // so we return early for them to avoid problems.
159 if (in_array($condition['operator'], ['IS NULL', 'IS NOT NULL'], TRUE)) {
160 $should_be_set = $condition['operator'] === 'IS NOT NULL';
161 return $should_be_set === isset($value);
165 // We always want a case-insensitive match.
166 if (!is_bool($value)) {
167 $value = Unicode::strtolower($value);
170 switch ($condition['operator']) {
172 return $value == $condition['value'];
174 return $value > $condition['value'];
176 return $value < $condition['value'];
178 return $value >= $condition['value'];
180 return $value <= $condition['value'];
182 return $value != $condition['value'];
184 return array_search($value, $condition['value']) !== FALSE;
186 return array_search($value, $condition['value']) === FALSE;
188 return strpos($value, $condition['value']) === 0;
190 return strpos($value, $condition['value']) !== FALSE;
192 return substr($value, -strlen($condition['value'])) === (string) $condition['value'];
194 throw new QueryException('Invalid condition operator.');