Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\DomCrawler\Field;
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * ChoiceFormField represents a choice form field.
|
Chris@0
|
16 *
|
Chris@0
|
17 * It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs.
|
Chris@0
|
18 *
|
Chris@0
|
19 * @author Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
20 */
|
Chris@0
|
21 class ChoiceFormField extends FormField
|
Chris@0
|
22 {
|
Chris@0
|
23 /**
|
Chris@0
|
24 * @var string
|
Chris@0
|
25 */
|
Chris@0
|
26 private $type;
|
Chris@0
|
27 /**
|
Chris@0
|
28 * @var bool
|
Chris@0
|
29 */
|
Chris@0
|
30 private $multiple;
|
Chris@0
|
31 /**
|
Chris@0
|
32 * @var array
|
Chris@0
|
33 */
|
Chris@0
|
34 private $options;
|
Chris@0
|
35 /**
|
Chris@0
|
36 * @var bool
|
Chris@0
|
37 */
|
Chris@0
|
38 private $validationDisabled = false;
|
Chris@0
|
39
|
Chris@0
|
40 /**
|
Chris@0
|
41 * Returns true if the field should be included in the submitted values.
|
Chris@0
|
42 *
|
Chris@0
|
43 * @return bool true if the field should be included in the submitted values, false otherwise
|
Chris@0
|
44 */
|
Chris@0
|
45 public function hasValue()
|
Chris@0
|
46 {
|
Chris@0
|
47 // don't send a value for unchecked checkboxes
|
Chris@17
|
48 if (\in_array($this->type, ['checkbox', 'radio']) && null === $this->value) {
|
Chris@0
|
49 return false;
|
Chris@0
|
50 }
|
Chris@0
|
51
|
Chris@0
|
52 return true;
|
Chris@0
|
53 }
|
Chris@0
|
54
|
Chris@0
|
55 /**
|
Chris@0
|
56 * Check if the current selected option is disabled.
|
Chris@0
|
57 *
|
Chris@0
|
58 * @return bool
|
Chris@0
|
59 */
|
Chris@0
|
60 public function isDisabled()
|
Chris@0
|
61 {
|
Chris@0
|
62 if (parent::isDisabled() && 'select' === $this->type) {
|
Chris@0
|
63 return true;
|
Chris@0
|
64 }
|
Chris@0
|
65
|
Chris@0
|
66 foreach ($this->options as $option) {
|
Chris@0
|
67 if ($option['value'] == $this->value && $option['disabled']) {
|
Chris@0
|
68 return true;
|
Chris@0
|
69 }
|
Chris@0
|
70 }
|
Chris@0
|
71
|
Chris@0
|
72 return false;
|
Chris@0
|
73 }
|
Chris@0
|
74
|
Chris@0
|
75 /**
|
Chris@0
|
76 * Sets the value of the field.
|
Chris@0
|
77 *
|
Chris@17
|
78 * @param string|array $value The value of the field
|
Chris@0
|
79 */
|
Chris@0
|
80 public function select($value)
|
Chris@0
|
81 {
|
Chris@0
|
82 $this->setValue($value);
|
Chris@0
|
83 }
|
Chris@0
|
84
|
Chris@0
|
85 /**
|
Chris@0
|
86 * Ticks a checkbox.
|
Chris@0
|
87 *
|
Chris@0
|
88 * @throws \LogicException When the type provided is not correct
|
Chris@0
|
89 */
|
Chris@0
|
90 public function tick()
|
Chris@0
|
91 {
|
Chris@0
|
92 if ('checkbox' !== $this->type) {
|
Chris@0
|
93 throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
|
Chris@0
|
94 }
|
Chris@0
|
95
|
Chris@0
|
96 $this->setValue(true);
|
Chris@0
|
97 }
|
Chris@0
|
98
|
Chris@0
|
99 /**
|
Chris@13
|
100 * Unticks a checkbox.
|
Chris@0
|
101 *
|
Chris@0
|
102 * @throws \LogicException When the type provided is not correct
|
Chris@0
|
103 */
|
Chris@0
|
104 public function untick()
|
Chris@0
|
105 {
|
Chris@0
|
106 if ('checkbox' !== $this->type) {
|
Chris@13
|
107 throw new \LogicException(sprintf('You cannot untick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
|
Chris@0
|
108 }
|
Chris@0
|
109
|
Chris@0
|
110 $this->setValue(false);
|
Chris@0
|
111 }
|
Chris@0
|
112
|
Chris@0
|
113 /**
|
Chris@0
|
114 * Sets the value of the field.
|
Chris@0
|
115 *
|
Chris@13
|
116 * @param string|array $value The value of the field
|
Chris@0
|
117 *
|
Chris@0
|
118 * @throws \InvalidArgumentException When value type provided is not correct
|
Chris@0
|
119 */
|
Chris@0
|
120 public function setValue($value)
|
Chris@0
|
121 {
|
Chris@0
|
122 if ('checkbox' === $this->type && false === $value) {
|
Chris@0
|
123 // uncheck
|
Chris@0
|
124 $this->value = null;
|
Chris@0
|
125 } elseif ('checkbox' === $this->type && true === $value) {
|
Chris@0
|
126 // check
|
Chris@0
|
127 $this->value = $this->options[0]['value'];
|
Chris@0
|
128 } else {
|
Chris@17
|
129 if (\is_array($value)) {
|
Chris@0
|
130 if (!$this->multiple) {
|
Chris@0
|
131 throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name));
|
Chris@0
|
132 }
|
Chris@0
|
133
|
Chris@0
|
134 foreach ($value as $v) {
|
Chris@0
|
135 if (!$this->containsOption($v, $this->options)) {
|
Chris@0
|
136 throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues())));
|
Chris@0
|
137 }
|
Chris@0
|
138 }
|
Chris@0
|
139 } elseif (!$this->containsOption($value, $this->options)) {
|
Chris@0
|
140 throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues())));
|
Chris@0
|
141 }
|
Chris@0
|
142
|
Chris@0
|
143 if ($this->multiple) {
|
Chris@0
|
144 $value = (array) $value;
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@17
|
147 if (\is_array($value)) {
|
Chris@0
|
148 $this->value = $value;
|
Chris@0
|
149 } else {
|
Chris@0
|
150 parent::setValue($value);
|
Chris@0
|
151 }
|
Chris@0
|
152 }
|
Chris@0
|
153 }
|
Chris@0
|
154
|
Chris@0
|
155 /**
|
Chris@0
|
156 * Adds a choice to the current ones.
|
Chris@0
|
157 *
|
Chris@0
|
158 * @param \DOMElement $node
|
Chris@0
|
159 *
|
Chris@0
|
160 * @throws \LogicException When choice provided is not multiple nor radio
|
Chris@0
|
161 *
|
Chris@0
|
162 * @internal
|
Chris@0
|
163 */
|
Chris@0
|
164 public function addChoice(\DOMElement $node)
|
Chris@0
|
165 {
|
Chris@0
|
166 if (!$this->multiple && 'radio' !== $this->type) {
|
Chris@0
|
167 throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name));
|
Chris@0
|
168 }
|
Chris@0
|
169
|
Chris@0
|
170 $option = $this->buildOptionValue($node);
|
Chris@0
|
171 $this->options[] = $option;
|
Chris@0
|
172
|
Chris@0
|
173 if ($node->hasAttribute('checked')) {
|
Chris@0
|
174 $this->value = $option['value'];
|
Chris@0
|
175 }
|
Chris@0
|
176 }
|
Chris@0
|
177
|
Chris@0
|
178 /**
|
Chris@0
|
179 * Returns the type of the choice field (radio, select, or checkbox).
|
Chris@0
|
180 *
|
Chris@0
|
181 * @return string The type
|
Chris@0
|
182 */
|
Chris@0
|
183 public function getType()
|
Chris@0
|
184 {
|
Chris@0
|
185 return $this->type;
|
Chris@0
|
186 }
|
Chris@0
|
187
|
Chris@0
|
188 /**
|
Chris@0
|
189 * Returns true if the field accepts multiple values.
|
Chris@0
|
190 *
|
Chris@0
|
191 * @return bool true if the field accepts multiple values, false otherwise
|
Chris@0
|
192 */
|
Chris@0
|
193 public function isMultiple()
|
Chris@0
|
194 {
|
Chris@0
|
195 return $this->multiple;
|
Chris@0
|
196 }
|
Chris@0
|
197
|
Chris@0
|
198 /**
|
Chris@0
|
199 * Initializes the form field.
|
Chris@0
|
200 *
|
Chris@0
|
201 * @throws \LogicException When node type is incorrect
|
Chris@0
|
202 */
|
Chris@0
|
203 protected function initialize()
|
Chris@0
|
204 {
|
Chris@0
|
205 if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) {
|
Chris@0
|
206 throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName));
|
Chris@0
|
207 }
|
Chris@0
|
208
|
Chris@0
|
209 if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) {
|
Chris@0
|
210 throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type')));
|
Chris@0
|
211 }
|
Chris@0
|
212
|
Chris@0
|
213 $this->value = null;
|
Chris@17
|
214 $this->options = [];
|
Chris@0
|
215 $this->multiple = false;
|
Chris@0
|
216
|
Chris@0
|
217 if ('input' == $this->node->nodeName) {
|
Chris@0
|
218 $this->type = strtolower($this->node->getAttribute('type'));
|
Chris@0
|
219 $optionValue = $this->buildOptionValue($this->node);
|
Chris@0
|
220 $this->options[] = $optionValue;
|
Chris@0
|
221
|
Chris@0
|
222 if ($this->node->hasAttribute('checked')) {
|
Chris@0
|
223 $this->value = $optionValue['value'];
|
Chris@0
|
224 }
|
Chris@0
|
225 } else {
|
Chris@0
|
226 $this->type = 'select';
|
Chris@0
|
227 if ($this->node->hasAttribute('multiple')) {
|
Chris@0
|
228 $this->multiple = true;
|
Chris@17
|
229 $this->value = [];
|
Chris@0
|
230 $this->name = str_replace('[]', '', $this->name);
|
Chris@0
|
231 }
|
Chris@0
|
232
|
Chris@0
|
233 $found = false;
|
Chris@0
|
234 foreach ($this->xpath->query('descendant::option', $this->node) as $option) {
|
Chris@0
|
235 $optionValue = $this->buildOptionValue($option);
|
Chris@0
|
236 $this->options[] = $optionValue;
|
Chris@0
|
237
|
Chris@0
|
238 if ($option->hasAttribute('selected')) {
|
Chris@0
|
239 $found = true;
|
Chris@0
|
240 if ($this->multiple) {
|
Chris@0
|
241 $this->value[] = $optionValue['value'];
|
Chris@0
|
242 } else {
|
Chris@0
|
243 $this->value = $optionValue['value'];
|
Chris@0
|
244 }
|
Chris@0
|
245 }
|
Chris@0
|
246 }
|
Chris@0
|
247
|
Chris@0
|
248 // if no option is selected and if it is a simple select box, take the first option as the value
|
Chris@0
|
249 if (!$found && !$this->multiple && !empty($this->options)) {
|
Chris@0
|
250 $this->value = $this->options[0]['value'];
|
Chris@0
|
251 }
|
Chris@0
|
252 }
|
Chris@0
|
253 }
|
Chris@0
|
254
|
Chris@0
|
255 /**
|
Chris@0
|
256 * Returns option value with associated disabled flag.
|
Chris@0
|
257 *
|
Chris@0
|
258 * @param \DOMElement $node
|
Chris@0
|
259 *
|
Chris@0
|
260 * @return array
|
Chris@0
|
261 */
|
Chris@0
|
262 private function buildOptionValue(\DOMElement $node)
|
Chris@0
|
263 {
|
Chris@17
|
264 $option = [];
|
Chris@0
|
265
|
Chris@0
|
266 $defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on';
|
Chris@0
|
267 $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue;
|
Chris@0
|
268 $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue;
|
Chris@0
|
269 $option['disabled'] = $node->hasAttribute('disabled');
|
Chris@0
|
270
|
Chris@0
|
271 return $option;
|
Chris@0
|
272 }
|
Chris@0
|
273
|
Chris@0
|
274 /**
|
Chris@0
|
275 * Checks whether given value is in the existing options.
|
Chris@0
|
276 *
|
Chris@0
|
277 * @param string $optionValue
|
Chris@0
|
278 * @param array $options
|
Chris@0
|
279 *
|
Chris@0
|
280 * @return bool
|
Chris@0
|
281 */
|
Chris@0
|
282 public function containsOption($optionValue, $options)
|
Chris@0
|
283 {
|
Chris@0
|
284 if ($this->validationDisabled) {
|
Chris@0
|
285 return true;
|
Chris@0
|
286 }
|
Chris@0
|
287
|
Chris@0
|
288 foreach ($options as $option) {
|
Chris@0
|
289 if ($option['value'] == $optionValue) {
|
Chris@0
|
290 return true;
|
Chris@0
|
291 }
|
Chris@0
|
292 }
|
Chris@0
|
293
|
Chris@0
|
294 return false;
|
Chris@0
|
295 }
|
Chris@0
|
296
|
Chris@0
|
297 /**
|
Chris@0
|
298 * Returns list of available field options.
|
Chris@0
|
299 *
|
Chris@0
|
300 * @return array
|
Chris@0
|
301 */
|
Chris@0
|
302 public function availableOptionValues()
|
Chris@0
|
303 {
|
Chris@17
|
304 $values = [];
|
Chris@0
|
305
|
Chris@0
|
306 foreach ($this->options as $option) {
|
Chris@0
|
307 $values[] = $option['value'];
|
Chris@0
|
308 }
|
Chris@0
|
309
|
Chris@0
|
310 return $values;
|
Chris@0
|
311 }
|
Chris@0
|
312
|
Chris@0
|
313 /**
|
Chris@0
|
314 * Disables the internal validation of the field.
|
Chris@0
|
315 *
|
Chris@0
|
316 * @return self
|
Chris@0
|
317 */
|
Chris@0
|
318 public function disableValidation()
|
Chris@0
|
319 {
|
Chris@0
|
320 $this->validationDisabled = true;
|
Chris@0
|
321
|
Chris@0
|
322 return $this;
|
Chris@0
|
323 }
|
Chris@0
|
324 }
|