comparison core/modules/jsonapi/src/Query/EntityCondition.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents
children
comparison
equal deleted inserted replaced
4:a9cd425dd02b 5:12f9dff5fda9
1 <?php
2
3 namespace Drupal\jsonapi\Query;
4
5 use Drupal\Core\Cache\CacheableMetadata;
6 use Drupal\Core\Http\Exception\CacheableBadRequestHttpException;
7
8 /**
9 * A condition object for the EntityQuery.
10 *
11 * @internal JSON:API maintains no PHP API since its API is the HTTP API. This
12 * class may change at any time and this will break any dependencies on it.
13 *
14 * @see https://www.drupal.org/project/jsonapi/issues/3032787
15 * @see jsonapi.api.php
16 */
17 class EntityCondition {
18
19 /**
20 * The field key in the filter condition: filter[lorem][condition][<field>].
21 *
22 * @var string
23 */
24 const PATH_KEY = 'path';
25
26 /**
27 * The value key in the filter condition: filter[lorem][condition][<value>].
28 *
29 * @var string
30 */
31 const VALUE_KEY = 'value';
32
33 /**
34 * The operator key in the condition: filter[lorem][condition][<operator>].
35 *
36 * @var string
37 */
38 const OPERATOR_KEY = 'operator';
39
40 /**
41 * The allowed condition operators.
42 *
43 * @var string[]
44 */
45 public static $allowedOperators = [
46 '=', '<>',
47 '>', '>=', '<', '<=',
48 'STARTS_WITH', 'CONTAINS', 'ENDS_WITH',
49 'IN', 'NOT IN',
50 'BETWEEN', 'NOT BETWEEN',
51 'IS NULL', 'IS NOT NULL',
52 ];
53
54 /**
55 * The field to be evaluated.
56 *
57 * @var string
58 */
59 protected $field;
60
61 /**
62 * The condition operator.
63 *
64 * @var string
65 */
66 protected $operator;
67
68 /**
69 * The value against which the field should be evaluated.
70 *
71 * @var mixed
72 */
73 protected $value;
74
75 /**
76 * Constructs a new EntityCondition object.
77 */
78 public function __construct($field, $value, $operator = NULL) {
79 $this->field = $field;
80 $this->value = $value;
81 $this->operator = ($operator) ? $operator : '=';
82 }
83
84 /**
85 * The field to be evaluated.
86 *
87 * @return string
88 * The field upon which to evaluate the condition.
89 */
90 public function field() {
91 return $this->field;
92 }
93
94 /**
95 * The comparison operator to use for the evaluation.
96 *
97 * For a list of allowed operators:
98 *
99 * @see \Drupal\jsonapi\Query\EntityCondition::allowedOperators
100 *
101 * @return string
102 * The condition operator.
103 */
104 public function operator() {
105 return $this->operator;
106 }
107
108 /**
109 * The value against which the condition should be evaluated.
110 *
111 * @return mixed
112 * The condition comparison value.
113 */
114 public function value() {
115 return $this->value;
116 }
117
118 /**
119 * Creates an EntityCondition object from a query parameter.
120 *
121 * @param mixed $parameter
122 * The `filter[condition]` query parameter from the request.
123 *
124 * @return self
125 * An EntityCondition object with defaults.
126 */
127 public static function createFromQueryParameter($parameter) {
128 static::validate($parameter);
129 $field = $parameter[static::PATH_KEY];
130 $value = (isset($parameter[static::VALUE_KEY])) ? $parameter[static::VALUE_KEY] : NULL;
131 $operator = (isset($parameter[static::OPERATOR_KEY])) ? $parameter[static::OPERATOR_KEY] : NULL;
132 return new static($field, $value, $operator);
133 }
134
135 /**
136 * Validates the filter has the required fields.
137 */
138 protected static function validate($parameter) {
139 $valid_key_combinations = [
140 [static::PATH_KEY, static::VALUE_KEY],
141 [static::PATH_KEY, static::OPERATOR_KEY],
142 [static::PATH_KEY, static::VALUE_KEY, static::OPERATOR_KEY],
143 ];
144
145 $given_keys = array_keys($parameter);
146 $valid_key_set = array_reduce($valid_key_combinations, function ($valid, $set) use ($given_keys) {
147 return ($valid) ? $valid : count(array_diff($set, $given_keys)) === 0;
148 }, FALSE);
149
150 $has_operator_key = isset($parameter[static::OPERATOR_KEY]);
151 $has_path_key = isset($parameter[static::PATH_KEY]);
152 $has_value_key = isset($parameter[static::VALUE_KEY]);
153
154 $cacheability = (new CacheableMetadata())->addCacheContexts(['url.query_args:filter']);
155 if (!$valid_key_set) {
156 // Try to provide a more specific exception is a key is missing.
157 if (!$has_operator_key) {
158 if (!$has_path_key) {
159 throw new CacheableBadRequestHttpException($cacheability, "Filter parameter is missing a '" . static::PATH_KEY . "' key.");
160 }
161 if (!$has_value_key) {
162 throw new CacheableBadRequestHttpException($cacheability, "Filter parameter is missing a '" . static::VALUE_KEY . "' key.");
163 }
164 }
165
166 // Catchall exception.
167 $reason = "You must provide a valid filter condition. Check that you have set the required keys for your filter.";
168 throw new CacheableBadRequestHttpException($cacheability, $reason);
169 }
170
171 if ($has_operator_key) {
172 $operator = $parameter[static::OPERATOR_KEY];
173 if (!in_array($operator, static::$allowedOperators)) {
174 $reason = "The '" . $operator . "' operator is not allowed in a filter parameter.";
175 throw new CacheableBadRequestHttpException($cacheability, $reason);
176 }
177
178 if (in_array($operator, ['IS NULL', 'IS NOT NULL']) && $has_value_key) {
179 $reason = "Filters using the '" . $operator . "' operator should not provide a value.";
180 throw new CacheableBadRequestHttpException($cacheability, $reason);
181 }
182 }
183 }
184
185 }