TListControl::getValidationGroup()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 1
cp 0
crap 2
rs 10
1
<?php
2
3
/**
4
 * TListControl class file
5
 *
6
 * @author Robin J. Rogge <[email protected]>
7
 * @author Qiang Xue <[email protected]>
8
 * @link https://github.com/pradosoft/prado
9
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
10
 */
11
12
namespace Prado\Web\UI\WebControls;
13
14
use Exception;
15
use Prado\Exceptions\TNotSupportedException;
16
use Prado\TPropertyValue;
17
use Prado\Util\TDataFieldAccessor;
18
use Prado\Web\THttpUtility;
19
use Prado\Exceptions\TInvalidDataValueException;
20
21
/**
22
 * TListControl class
23
 *
24
 * TListControl is a base class for list controls, such as {@see \Prado\Web\UI\WebControls\TListBox},
25
 * {@see \Prado\Web\UI\WebControls\TDropDownList}, {@see \Prado\Web\UI\WebControls\TCheckBoxList}, etc.
26
 * It manages the items and their status in a list control.
27
 * It also implements how the items can be populated from template and
28
 * data source.
29
 *
30
 * The property {@see getItems} returns a list of the items in the control.
31
 * To specify or determine which item is selected, use the
32
 * {@see getSelectedIndex SelectedIndex} property that indicates the zero-based
33
 * index of the selected item in the item list. You may also use
34
 * {@see getSelectedItem SelectedItem} and {@see getSelectedValue SelectedValue}
35
 * to get the selected item and its value. For multiple selection lists
36
 * (such as {@see \Prado\Web\UI\WebControls\TCheckBoxList} and {@see \Prado\Web\UI\WebControls\TListBox}), property
37
 * {@see getSelectedIndices SelectedIndices} is useful.
38
 *
39
 * TListControl implements {@see setAutoPostBack AutoPostBack} which allows
40
 * a list control to postback the page if the selections of the list items are changed.
41
 * The {@see setCausesValidation CausesValidation} and {@see setValidationGroup ValidationGroup}
42
 * properties may be used to specify that validation be performed when auto postback occurs.
43
 *
44
 * There are three ways to populate the items in a list control: from template,
45
 * using {@see setDataSource DataSource} and using {@see setDataSourceID DataSourceID}.
46
 * The latter two are covered in {@see \Prado\Web\UI\WebControls\TDataBoundControl}. To specify items via
47
 * template, using the following template syntax:
48
 * ```php
49
 * <com:TListControl>
50
 *   <com:TListItem Value="xxx" Text="yyy" >
51
 *   <com:TListItem Value="xxx" Text="yyy" Selected="true" >
52
 *   <com:TListItem Value="xxx" Text="yyy" >
53
 * </com:TListControl>
54
 * ```
55
 *
56
 * When {@see setDataSource DataSource} or {@see setDataSourceID DataSourceID}
57
 * is used to populate list items, the {@see setDataTextField DataTextField} and
58
 * {@see setDataValueField DataValueField} properties are used to specify which
59
 * columns of the data will be used to populate the text and value of the items.
60
 * For example, if a data source is as follows,
61
 * ```php
62
 * $dataSource=array(
63
 *    array('name'=>'John', 'age'=>31),
64
 *    array('name'=>'Cary', 'age'=>28),
65
 *    array('name'=>'Rose', 'age'=>35),
66
 * );
67
 * ```
68
 * setting {@see setDataTextField DataTextField} and {@see setDataValueField DataValueField}
69
 * to 'name' and 'age' will make the first item's text be 'John', value be 31,
70
 * the second item's text be 'Cary', value be 28, and so on.
71
 * The {@see setDataTextFormatString DataTextFormatString} property may be further
72
 * used to format how the item should be displayed. See {@see formatDataValue()}
73
 * for an explanation of the format string.
74
 *
75
 * The {@see setPromptText PromptText} and {@see setPromptValue PromptValue} properties can
76
 * be used to add a dummy list item that will be rendered first.
77
 *
78
 * @author Qiang Xue <[email protected]>
79
 * @since 3.0
80
 */
81
abstract class TListControl extends TDataBoundControl implements \Prado\IDataRenderer
82
{
83
	/**
84
	 * @var \Prado\Collections\TListItemCollection item list
85
	 */
86
	private $_items;
87
	/**
88
	 * @var bool whether items are restored from viewstate
89
	 */
90
	private $_stateLoaded = false;
91
	/**
92
	 * @var mixed the following selection variables are used
93
	 * to keep selections when Items are not available
94
	 */
95
	private $_cachedSelectedIndex = -1;
96
	private $_cachedSelectedValue;
97
	private $_cachedSelectedIndices;
98
	private $_cachedSelectedValues;
99
100
	/**
101
	 * @return string tag name of the list control
102
	 */
103
	protected function getTagName()
104
	{
105
		return 'select';
106
	}
107
108
	/**
109
	 * @return bool whether to render javascript.
110
	 */
111
	public function getEnableClientScript()
112
	{
113
		return $this->getViewState('EnableClientScript', true);
114
	}
115
116
	/**
117
	 * @param bool $value whether to render javascript.
118
	 */
119
	public function setEnableClientScript($value)
120
	{
121
		$this->setViewState('EnableClientScript', TPropertyValue::ensureBoolean($value), true);
122
	}
123
124
	/**
125
	 * Adds attributes to renderer.
126
	 * @param \Prado\Web\UI\THtmlWriter $writer the renderer
127
	 */
128
	protected function addAttributesToRender($writer)
129
	{
130
		$page = $this->getPage();
131
		$page->ensureRenderInForm($this);
132
		if ($this->getIsMultiSelect()) {
133
			$writer->addAttribute('multiple', 'multiple');
134
		}
135
		if ($this->getEnabled(true)) {
136
			if ($this->getAutoPostBack()
137
				&& $this->getEnableClientScript()
138
				&& $page->getClientSupportsJavaScript()) {
139
				$this->renderClientControlScript($writer);
140
			}
141
		} elseif ($this->getEnabled()) {
142
			$writer->addAttribute('disabled', 'disabled');
143
		}
144
		parent::addAttributesToRender($writer);
145
	}
146
147
	/**
148
	 * Renders the javascript for list control.
149
	 * @param mixed $writer
150
	 */
151
	protected function renderClientControlScript($writer)
152
	{
153
		$writer->addAttribute('id', $this->getClientID());
154
		$this->getPage()->getClientScript()->registerPostBackControl($this->getClientClassName(), $this->getPostBackOptions());
155
	}
156
157
	/**
158
	 * Gets the name of the javascript class responsible for performing postback for this control.
159
	 * Derived classes may override this method and return customized js class names.
160
	 * @return string the javascript class name
161
	 */
162
	protected function getClientClassName()
163
	{
164
		return 'Prado.WebUI.TListControl';
165
	}
166
167
	/**
168
	 * @return array postback options for JS postback code
169
	 */
170
	protected function getPostBackOptions()
171
	{
172
		$options['ID'] = $this->getClientID();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.
Loading history...
173
		$options['CausesValidation'] = $this->getCausesValidation();
174
		$options['ValidationGroup'] = $this->getValidationGroup();
175
		$options['EventTarget'] = $this->getUniqueID();
176
		return $options;
177
	}
178
179
	/**
180
	 * Adds object parsed from template to the control.
181
	 * This method adds only {@see \Prado\Web\UI\WebControls\TListItem} objects into the {@see getItems Items} collection.
182
	 * All other objects are ignored.
183
	 * @param mixed $object object parsed from template
184
	 */
185
	public function addParsedObject($object)
186
	{
187
		// Do not add items from template if items are loaded from viewstate
188
		if (!$this->_stateLoaded && ($object instanceof TListItem)) {
189
			$index = $this->getItems()->add($object);
190
			if (($this->_cachedSelectedValue !== null && $this->_cachedSelectedValue === $object->getValue()) || ($this->_cachedSelectedIndex === $index)) {
191
				$object->setSelected(true);
192
				$this->_cachedSelectedValue = null;
193
				$this->_cachedSelectedIndex = -1;
194
			}
195
		}
196
	}
197
198
	/**
199
	 * Performs databinding to populate list items from data source.
200
	 * This method is invoked by dataBind().
201
	 * You may override this function to provide your own way of data population.
202
	 * @param \Traversable $data the data
203
	 */
204
	protected function performDataBinding($data)
205 1
	{
206
		$items = $this->getItems();
207 1
		if (!$this->getAppendDataBoundItems()) {
208 1
			$items->clear();
209 1
		}
210
		$textField = $this->getDataTextField();
211 1
		if ($textField === '') {
212 1
			$textField = 0;
213 1
		}
214
		$valueField = $this->getDataValueField();
215 1
		if ($valueField === '') {
216 1
			$valueField = 1;
217 1
		}
218
		$textFormat = $this->getDataTextFormatString();
219 1
		$groupField = $this->getDataGroupField();
220 1
		foreach ($data as $key => $object) {
221 1
			$item = $items->createListItem();
222 1
			if (is_array($object) || is_object($object)) {
223 1
				$text = TDataFieldAccessor::getDataFieldValue($object, $textField);
224
				$value = TDataFieldAccessor::getDataFieldValue($object, $valueField);
225
				$item->setValue($value);
226
				if ($groupField !== '') {
227
					$item->setAttribute('Group', TDataFieldAccessor::getDataFieldValue($object, $groupField));
228
				}
229
			} else {
230
				$text = $object;
231 1
				$item->setValue("$key");
232 1
			}
233
			$item->setText($this->formatDataValue($textFormat, $text));
234 1
		}
235
		// SelectedValue or SelectedIndex may be set before databinding
236
		// so we make them be effective now
237
		if ($this->_cachedSelectedValue !== null) {
238 1
			$this->setSelectedValue($this->_cachedSelectedValue);
239
			$this->resetCachedSelections();
240
		} elseif ($this->_cachedSelectedIndex !== -1) {
241 1
			$this->setSelectedIndex($this->_cachedSelectedIndex);
242
			$this->resetCachedSelections();
243
		} elseif ($this->_cachedSelectedValues !== null) {
244 1
			$this->setSelectedValues($this->_cachedSelectedValues);
245
			$this->resetCachedSelections();
246
		} elseif ($this->_cachedSelectedIndices !== null) {
247 1
			$this->setSelectedIndices($this->_cachedSelectedIndices);
248
			$this->resetCachedSelections();
249
		}
250
	}
251 1
252
	private function resetCachedSelections()
253
	{
254
		$this->_cachedSelectedValue = null;
255
		$this->_cachedSelectedIndex = -1;
256
		$this->_cachedSelectedValues = null;
257
		$this->_cachedSelectedIndices = null;
258
	}
259
260
	/**
261
	 * Creates a collection object to hold list items.
262
	 * This method may be overriden to create a customized collection.
263
	 * @return \Prado\Collections\TListItemCollection the collection object
264
	 */
265
	protected function createListItemCollection()
266 1
	{
267
		return new \Prado\Collections\TListItemCollection();
268 1
	}
269
270
	/**
271
	 * Saves items into viewstate.
272
	 * This method is invoked right before control state is to be saved.
273
	 */
274
	public function saveState()
275
	{
276
		parent::saveState();
277
		if ($this->_items) {
278
			$this->setViewState('Items', $this->_items->saveState(), null);
279
		} else {
280
			$this->clearViewState('Items');
281
		}
282
	}
283
284
	/**
285
	 * Loads items from viewstate.
286
	 * This method is invoked right after control state is loaded.
287
	 */
288
	public function loadState()
289
	{
290
		parent::loadState();
291
		$this->_stateLoaded = true;
292
		if (!$this->getIsDataBound()) {
293
			$this->_items = $this->createListItemCollection();
294
			$this->_items->loadState($this->getViewState('Items', null));
295
		}
296
		$this->clearViewState('Items');
297
	}
298
299
	/**
300
	 * @return bool whether this is a multiselect control. Defaults to false.
301
	 */
302
	protected function getIsMultiSelect()
303
	{
304
		return false;
305
	}
306
307
	/**
308
	 * @return bool whether performing databind should append items or clear the existing ones. Defaults to false.
309
	 */
310
	public function getAppendDataBoundItems()
311 1
	{
312
		return $this->getViewState('AppendDataBoundItems', false);
313 1
	}
314
315
	/**
316
	 * @param bool $value whether performing databind should append items or clear the existing ones.
317
	 */
318
	public function setAppendDataBoundItems($value)
319
	{
320
		$this->setViewState('AppendDataBoundItems', TPropertyValue::ensureBoolean($value), false);
321
	}
322
323
	/**
324
	 * @return bool a value indicating whether an automatic postback to the server
325
	 * will occur whenever the user makes change to the list control and then tabs out of it.
326
	 * Defaults to false.
327
	 */
328
	public function getAutoPostBack()
329
	{
330
		return $this->getViewState('AutoPostBack', false);
331
	}
332
333
	/**
334
	 * Sets the value indicating if postback automatically.
335
	 * An automatic postback to the server will occur whenever the user
336
	 * makes change to the list control and then tabs out of it.
337
	 * @param bool $value the value indicating if postback automatically
338
	 */
339
	public function setAutoPostBack($value)
340
	{
341
		$this->setViewState('AutoPostBack', TPropertyValue::ensureBoolean($value), false);
342
	}
343
344
	/**
345
	 * @return bool whether postback event trigger by this list control will cause input validation, default is true.
346
	 */
347
	public function getCausesValidation()
348
	{
349
		return $this->getViewState('CausesValidation', true);
350
	}
351
352
	/**
353
	 * @param bool $value whether postback event trigger by this list control will cause input validation.
354
	 */
355
	public function setCausesValidation($value)
356
	{
357
		$this->setViewState('CausesValidation', TPropertyValue::ensureBoolean($value), true);
358
	}
359
360
	/**
361
	 * @return string the field of the data source that provides the text content of the list items.
362
	 */
363
	public function getDataTextField()
364 1
	{
365
		return $this->getViewState('DataTextField', '');
366 1
	}
367
368
	/**
369
	 * @param string $value the field of the data source that provides the text content of the list items.
370
	 */
371
	public function setDataTextField($value)
372
	{
373
		$this->setViewState('DataTextField', $value, '');
374
	}
375
376
	/**
377
	 * @return string the formatting string used to control how data bound to the list control is displayed.
378
	 */
379
	public function getDataTextFormatString()
380 1
	{
381
		return $this->getViewState('DataTextFormatString', '');
382 1
	}
383
384
	/**
385
	 * Sets data text format string.
386
	 * The format string is used in {@see TDataValueFormatter::format()} to format the Text property value
387
	 * of each item in the list control.
388
	 * @param string $value the formatting string used to control how data bound to the list control is displayed.
389
	 * @see TDataValueFormatter::format()
390
	 */
391
	public function setDataTextFormatString($value)
392
	{
393
		$this->setViewState('DataTextFormatString', $value, '');
394
	}
395
396
	/**
397
	 * @return string the field of the data source that provides the value of each list item.
398
	 */
399
	public function getDataValueField()
400 1
	{
401
		return $this->getViewState('DataValueField', '');
402 1
	}
403
404
	/**
405
	 * @param string $value the field of the data source that provides the value of each list item.
406
	 */
407
	public function setDataValueField($value)
408
	{
409
		$this->setViewState('DataValueField', $value, '');
410
	}
411
412
	/**
413
	 * @return string the field of the data source that provides the label of the list item groups
414
	 */
415
	public function getDataGroupField()
416 1
	{
417
		return $this->getViewState('DataGroupField', '');
418 1
	}
419
420
	/**
421
	 * @param string $value the field of the data source that provides the label of the list item groups
422
	 */
423
	public function setDataGroupField($value)
424
	{
425
		$this->setViewState('DataGroupField', $value, '');
426
	}
427
428
	/**
429
	 * @return int the number of items in the list control
430
	 */
431
	public function getItemCount()
432
	{
433
		return $this->_items ? $this->_items->getCount() : 0;
434
	}
435
436
	/**
437
	 * @return bool whether the list control contains any items.
438
	 */
439
	public function getHasItems()
440
	{
441
		return ($this->_items && $this->_items->getCount() > 0);
442
	}
443
444
	/**
445
	 * @return \Prado\Collections\TListItemCollection the item collection
446
	 */
447
	public function getItems()
448 1
	{
449
		if (!$this->_items) {
450 1
			$this->_items = $this->createListItemCollection();
451 1
		}
452
		return $this->_items;
453 1
	}
454
455
	/**
456
	 * @return int the index (zero-based) of the item being selected, -1 if no item is selected.
457
	 */
458
	public function getSelectedIndex()
459
	{
460
		if ($this->_items) {
461
			$n = $this->_items->getCount();
462
			for ($i = 0; $i < $n; ++$i) {
463
				if ($this->_items->itemAt($i)->getSelected()) {
464
					return $i;
465
				}
466
			}
467
		}
468
		return -1;
469
	}
470
471
	/**
472
	 * @param int $index the index (zero-based) of the item to be selected
473
	 */
474
	public function setSelectedIndex($index)
475
	{
476
		if (($index = TPropertyValue::ensureInteger($index)) < 0) {
477
			$index = -1;
478
		}
479
		if ($this->_items) {
480
			$this->clearSelection();
481
			if ($index >= 0 && $index < $this->_items->getCount()) {
482
				$this->_items->itemAt($index)->setSelected(true);
483
			}
484
		}
485
		$this->_cachedSelectedIndex = $index;
486
		if ($this->getAdapter() instanceof IListControlAdapter) {
487
			$this->getAdapter()->setSelectedIndex($index);
0 ignored issues
show
Bug introduced by
The method setSelectedIndex() does not exist on Prado\Web\UI\TControlAdapter. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

487
			$this->getAdapter()->/** @scrutinizer ignore-call */ setSelectedIndex($index);
Loading history...
488
		}
489
	}
490
491
	/**
492
	 * @return array list of index of items that are selected
493
	 */
494
	public function getSelectedIndices()
495
	{
496
		$selections = [];
497
		if ($this->_items) {
498
			$n = $this->_items->getCount();
499
			for ($i = 0; $i < $n; ++$i) {
500
				if ($this->_items->itemAt($i)->getSelected()) {
501
					$selections[] = $i;
502
				}
503
			}
504
		}
505
		return $selections;
506
	}
507
508
	/**
509
	 * @param array $indices list of index of items to be selected
510
	 */
511
	public function setSelectedIndices($indices)
512
	{
513
		if ($this->getIsMultiSelect()) {
514
			if ($this->_items) {
515
				$this->clearSelection();
516
				$n = $this->_items->getCount();
517
				foreach ($indices as $index) {
518
					if ($index >= 0 && $index < $n) {
519
						$this->_items->itemAt($index)->setSelected(true);
520
					}
521
				}
522
			}
523
			$this->_cachedSelectedIndices = $indices;
524
		} else {
525
			throw new TNotSupportedException('listcontrol_multiselect_unsupported', $this::class);
526
		}
527
528
		if ($this->getAdapter() instanceof IListControlAdapter) {
529
			$this->getAdapter()->setSelectedIndices($indices);
0 ignored issues
show
Bug introduced by
The method setSelectedIndices() does not exist on Prado\Web\UI\TControlAdapter. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

529
			$this->getAdapter()->/** @scrutinizer ignore-call */ setSelectedIndices($indices);
Loading history...
530
		}
531
	}
532
533
	/**
534
	 * @return null|TListItem the selected item with the lowest cardinal index, null if no item is selected.
535
	 */
536
	public function getSelectedItem()
537
	{
538
		if (($index = $this->getSelectedIndex()) >= 0) {
539
			return $this->_items->itemAt($index);
540
		} else {
541
			return null;
542
		}
543
	}
544
545
	/**
546
	 * Returns the value of the selected item with the lowest cardinal index.
547
	 * This method is required by {@see \Prado\IDataRenderer}.
548
	 * It is the same as {@see getSelectedValue()}.
549
	 * @return string the value of the selected item with the lowest cardinal index, empty if no selection.
550
	 * @see getSelectedValue
551
	 * @since 3.1.0
552
	 */
553
	public function getData()
554
	{
555
		return $this->getSelectedValue();
556
	}
557
558
	/**
559
	 * Selects an item by the specified value.
560
	 * This method is required by {@see \Prado\IDataRenderer}.
561
	 * It is the same as {@see setSelectedValue()}.
562
	 * @param string $value the value of the item to be selected.
563
	 * @see setSelectedValue
564
	 * @since 3.1.0
565
	 */
566
	public function setData($value)
567
	{
568
		$this->setSelectedValue($value);
569
	}
570
571
	/**
572
	 * @return string the value of the selected item with the lowest cardinal index, empty if no selection
573
	 */
574
	public function getSelectedValue()
575
	{
576
		$index = $this->getSelectedIndex();
577
		return $index >= 0 ? $this->getItems()->itemAt($index)->getValue() : $this->getPromptValue();
578
	}
579
580
	/**
581
	 * Sets selection by item value.
582
	 * Existing selections will be cleared if the item value is found in the item collection.
583
	 * Note, if the value is null, existing selections will also be cleared.
584
	 * @param string $value the value of the item to be selected.
585
	 */
586
	public function setSelectedValue($value)
587
	{
588
		if ($this->_items) {
589
			if ($value === null) {
0 ignored issues
show
introduced by
The condition $value === null is always false.
Loading history...
590
				$this->clearSelection();
591
			} elseif (($item = $this->_items->findItemByValue($value)) !== null) {
592
				$this->clearSelection();
593
				$item->setSelected(true);
594
			} else {
595
				$this->clearSelection();
596
			}
597
		}
598
		$this->_cachedSelectedValue = $value;
599
		if ($this->getAdapter() instanceof IListControlAdapter) {
600
			$this->getAdapter()->setSelectedValue($value);
0 ignored issues
show
Bug introduced by
The method setSelectedValue() does not exist on Prado\Web\UI\TControlAdapter. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

600
			$this->getAdapter()->/** @scrutinizer ignore-call */ setSelectedValue($value);
Loading history...
601
		}
602
	}
603
604
605
	/**
606
	 * @return array list of the selected item values (strings)
607
	 */
608
	public function getSelectedValues()
609
	{
610
		$values = [];
611
		if ($this->_items) {
612
			foreach ($this->_items as $item) {
613
				if ($item->getSelected()) {
614
					$values[] = $item->getValue();
615
				}
616
			}
617
		}
618
		return $values;
619
	}
620
621
	/**
622
	 * @param array $values list of the selected item values
623
	 */
624
	public function setSelectedValues($values)
625
	{
626
		if ($this->getIsMultiSelect()) {
627
			if ($this->_items) {
628
				$this->clearSelection();
629
				$lookup = [];
630
				foreach ($this->_items as $item) {
631
					$lookup[$item->getValue()] = $item;
632
				}
633
				foreach ($values as $value) {
634
					if (isset($lookup["$value"])) {
635
						$lookup["$value"]->setSelected(true);
636
					}
637
				}
638
			}
639
			$this->_cachedSelectedValues = $values;
640
		} else {
641
			throw new TNotSupportedException('listcontrol_multiselect_unsupported', $this::class);
642
		}
643
644
		if ($this->getAdapter() instanceof IListControlAdapter) {
645
			$this->getAdapter()->setSelectedValues($values);
0 ignored issues
show
Bug introduced by
The method setSelectedValues() does not exist on Prado\Web\UI\TControlAdapter. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

645
			$this->getAdapter()->/** @scrutinizer ignore-call */ setSelectedValues($values);
Loading history...
646
		}
647
	}
648
649
	/**
650
	 * @return string selected value
651
	 */
652
	public function getText()
653
	{
654
		return $this->getSelectedValue();
655
	}
656
657
	/**
658
	 * @param string $value value to be selected
659
	 */
660
	public function setText($value)
661
	{
662
		$this->setSelectedValue($value);
663
	}
664
665
	/**
666
	 * Clears all existing selections.
667
	 */
668
	public function clearSelection()
669
	{
670
		if ($this->_items) {
671
			foreach ($this->_items as $item) {
672
				$item->setSelected(false);
673
			}
674
		}
675
676
		if ($this->getAdapter() instanceof IListControlAdapter) {
677
			$this->getAdapter()->clearSelection();
0 ignored issues
show
Bug introduced by
The method clearSelection() does not exist on Prado\Web\UI\TControlAdapter. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

677
			$this->getAdapter()->/** @scrutinizer ignore-call */ clearSelection();
Loading history...
678
		}
679
	}
680
681
	/**
682
	 * @return string the group of validators which the list control causes validation upon postback
683
	 */
684
	public function getValidationGroup()
685
	{
686
		return $this->getViewState('ValidationGroup', '');
687
	}
688
689
	/**
690
	 * @param string $value the group of validators which the list control causes validation upon postback
691
	 */
692
	public function setValidationGroup($value)
693
	{
694
		$this->setViewState('ValidationGroup', $value, '');
695
	}
696
697
	/**
698
	 * @return string the prompt text which is to be displayed as the first list item.
699
	 * @since 3.1.1
700
	 */
701
	public function getPromptText()
702
	{
703
		return $this->getViewState('PromptText', '');
704
	}
705
706
	/**
707
	 * @param string $value the prompt text which is to be displayed as the first list item.
708
	 * @since 3.1.1
709
	 */
710
	public function setPromptText($value)
711
	{
712
		$this->setViewState('PromptText', $value, '');
713
	}
714
715
	/**
716
	 * @return string the prompt selection value.
717
	 * @see getPromptText
718
	 * @since 3.1.1
719
	 */
720
	public function getPromptValue()
721
	{
722
		return $this->getViewState('PromptValue', '');
723
	}
724
725
	/**
726
	 * @param string $value the prompt selection value.
727
	 * @see setPromptText
728
	 * @since 3.1.1
729
	 */
730
	public function setPromptValue($value)
731
	{
732
		$this->setViewState('PromptValue', (string) $value, '');
733
	}
734
735
	/**
736
	 * Raises OnSelectedIndexChanged event when selection is changed.
737
	 * This method is invoked when the list control has its selection changed
738
	 * by end-users.
739
	 * @param \Prado\TEventParameter $param event parameter
740
	 */
741
	public function onSelectedIndexChanged($param)
742
	{
743
		$this->raiseEvent('OnSelectedIndexChanged', $this, $param);
744
		$this->onTextChanged($param);
745
	}
746
747
	/**
748
	 * Raises OnTextChanged event when selection is changed.
749
	 * This method is invoked when the list control has its selection changed
750
	 * by end-users.
751
	 * @param \Prado\TEventParameter $param event parameter
752
	 */
753
	public function onTextChanged($param)
754
	{
755
		$this->raiseEvent('OnTextChanged', $this, $param);
756
	}
757
758
	/**
759
	 * Renders the prompt text, if any.
760
	 * @param \Prado\Web\UI\THtmlWriter $writer writer
761
	 * @since 3.1.1
762
	 */
763
	protected function renderPrompt($writer)
764
	{
765
		$text = $this->getPromptText();
766
		$value = $this->getPromptValue();
767
		if ($value !== '' || $text !== '') {
768
			$writer->addAttribute('value', $value);
769
			$writer->renderBeginTag('option');
770
			$writer->write(THttpUtility::htmlEncode($text));
771
			$writer->renderEndTag();
772
			$writer->writeLine();
773
		}
774
	}
775
776
	/**
777
	 * Renders body content of the list control.
778
	 * This method renders items contained in the list control as the body content.
779
	 * @param \Prado\Web\UI\THtmlWriter $writer writer
780
	 */
781
	public function renderContents($writer)
782
	{
783
		$this->renderPrompt($writer);
784
785
		if ($this->_items) {
786
			$writer->writeLine();
787
			$previousGroup = null;
788
			foreach ($this->_items as $item) {
789
				if ($item->getEnabled()) {
790
					if ($item->getHasAttributes()) {
791
						$group = $item->getAttributes()->remove('Group');
792
						if ($group !== $previousGroup) {
793
							if ($previousGroup !== null) {
794
								$writer->renderEndTag();
795
								$writer->writeLine();
796
								$previousGroup = null;
797
							}
798
							if ($group !== null) {
799
								$writer->addAttribute('label', $group);
800
								$writer->renderBeginTag('optgroup');
801
								$writer->writeLine();
802
								$previousGroup = $group;
803
							}
804
						}
805
						foreach ($item->getAttributes() as $name => $value) {
806
							$writer->addAttribute($name, $value);
807
						}
808
					} elseif ($previousGroup !== null) {
809
						$writer->renderEndTag();
810
						$writer->writeLine();
811
						$previousGroup = null;
812
					}
813
					if ($item->getSelected()) {
814
						$writer->addAttribute('selected', 'selected');
815
					}
816
					$writer->addAttribute('value', $item->getValue());
817
					$writer->renderBeginTag('option');
818
					$writer->write(THttpUtility::htmlEncode($item->getText()));
819
					$writer->renderEndTag();
820
					$writer->writeLine();
821
				}
822
			}
823
			if ($previousGroup !== null) {
824
				$writer->renderEndTag();
825
				$writer->writeLine();
826
			}
827
		}
828
	}
829
830
	/**
831
	 * Formats the text value according to a format string.
832
	 * If the format string is empty, the original value is converted into
833
	 * a string and returned.
834
	 * If the format string starts with '#', the string is treated as a PHP expression
835
	 * within which the token '{0}' is translated with the data value to be formated.
836
	 * Otherwise, the format string and the data value are passed
837
	 * as the first and second parameters in {@see sprintf}.
838
	 * @param string $formatString format string
839
	 * @param mixed $value the data to be formatted
840
	 * @return string the formatted result
841
	 */
842
	protected function formatDataValue($formatString, $value)
843 1
	{
844
		if ($formatString === '') {
845 1
			return TPropertyValue::ensureString($value);
846 1
		} elseif ($formatString[0] === '#') {
847
			$expression = strtr(substr($formatString, 1), ['{0}' => '$value']);
848
			try {
849
				return eval("return $expression;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
850
			} catch (Exception $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Exception $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
851
				throw new TInvalidDataValueException('listcontrol_expression_invalid', $this::class, $expression, $e->getMessage());
852
			}
853
		} else {
854
			return sprintf($formatString, $value);
855
		}
856
	}
857
}
858