TDataList::applyItemStyles()   F
last analyzed

Complexity

Conditions 22
Paths 5103

Size

Total Lines 64
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 506

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 22
eloc 39
c 1
b 0
f 0
nc 5103
nop 0
dl 0
loc 64
ccs 0
cts 52
cp 0
crap 506
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * TDataList class file
5
 *
6
 * @author Qiang Xue <[email protected]>
7
 * @link https://github.com/pradosoft/prado
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 */
10
11
namespace Prado\Web\UI\WebControls;
12
13
use Prado\Exceptions\TInvalidOperationException;
14
use Prado\Prado;
15
use Prado\TPropertyValue;
16
use Prado\Exceptions\TInvalidDataValueException;
17
use Prado\Exceptions\TInvalidDataTypeException;
18
use Prado\Web\UI\ITemplate;
19
20
/**
21
 * TDataList class
22
 *
23
 * TDataList represents a data bound and updatable list control.
24
 *
25
 * Like {@see \Prado\Web\UI\WebControls\TRepeater}, TDataList displays its content repeatedly based on
26
 * the data fetched from {@see setDataSource DataSource}.
27
 * The repeated contents in TDataList are called items, which are controls and
28
 * can be accessed through {@see getItems Items}. When {@see dataBind()} is
29
 * invoked, TDataList creates an item for each row of data and binds the data
30
 * row to the item. Optionally, a TDataList can have a header, a footer and/or
31
 * separators between items.
32
 *
33
 * TDataList differs from {@see \Prado\Web\UI\WebControls\TRepeater} in that it supports tiling the items
34
 * in different manners and it maintains status of items to handle data update.
35
 *
36
 * The layout of the repeated contents are specified by inline templates.
37
 * TDataList items, header, footer, etc. are being instantiated with the corresponding
38
 * templates when data is being bound to the repeater.
39
 *
40
 * Since v3.1.0, the layout can also be by renderers. A renderer is a control class
41
 * that can be instantiated as datalist items, header, etc. A renderer can thus be viewed
42
 * as an external template (in fact, it can also be non-templated controls).
43
 *
44
 * A renderer can be any control class.
45
 * - If the class implements {@see \Prado\IDataRenderer}, the <b>Data</b>
46
 * property will be set as the data row during databinding. Many PRADO controls
47
 * implement this interface, such as {@see \Prado\Web\UI\WebControls\TLabel}, {@see \Prado\Web\UI\WebControls\TTextBox}, etc.
48
 * - If the class implements {@see \Prado\Web\UI\WebControls\IItemDataRenderer}, the <b>ItemIndex</b> property will be set
49
 * as the zero-based index of the item in the datalist item collection, and
50
 * the <b>ItemType</b> property as the item's type (such as TListItemType::Item).
51
 * {@see \Prado\Web\UI\WebControls\TDataListItemRenderer} may be used as the convenient base class which
52
 * already implements {@see IDataItemRenderer}.
53
 *
54
 * The following properties are used to specify different types of template and renderer
55
 * for a datalist:
56
 * - {@see setItemTemplate ItemTemplate}, {@see setItemRenderer ItemRenderer}:
57
 * for each repeated row of data
58
 * - {@see setAlternatingItemTemplate AlternatingItemTemplate}, {@see setAlternatingItemRenderer AlternatingItemRenderer}:
59
 * for each alternating row of data. If not set, {@see setItemTemplate ItemTemplate} or {@see setItemRenderer ItemRenderer}
60
 * will be used instead.
61
 * - {@see setHeaderTemplate HeaderTemplate}, {@see setHeaderRenderer HeaderRenderer}:
62
 * for the datalist header.
63
 * - {@see setFooterTemplate FooterTemplate}, {@see setFooterRenderer FooterRenderer}:
64
 * for the datalist footer.
65
 * - {@see setSeparatorTemplate SeparatorTemplate}, {@see setSeparatorRenderer SeparatorRenderer}:
66
 * for content to be displayed between items.
67
 * - {@see setEmptyTemplate EmptyTemplate}, {@see setEmptyRenderer EmptyRenderer}:
68
 * used when data bound to the datalist is empty.
69
 * - {@see setEditItemTemplate EditItemTemplate}, {@see setEditItemRenderer EditItemRenderer}:
70
 * for the row being editted.
71
 * - {@see setSelectedItemTemplate SelectedItemTemplate}, {@see setSelectedItemRenderer SelectedItemRenderer}:
72
 * for the row being selected.
73
 *
74
 * If a content type is defined with both a template and a renderer, the latter takes precedence.
75
 *
76
 * When {@see dataBind()} is being called, TDataList undergoes the following lifecycles for each row of data:
77
 * - create item based on templates or renderers
78
 * - set the row of data to the item
79
 * - raise {@see onItemCreated OnItemCreated}:
80
 * - add the item as a child control
81
 * - call dataBind() of the item
82
 * - raise {@see onItemDataBound OnItemDataBound}:
83
 *
84
 * TDataList raises an {@see onItemCommand OnItemCommand} whenever a button control
85
 * within some datalist item raises a <b>OnCommand</b> event. Therefore,
86
 * you can handle all sorts of <b>OnCommand</b> event in a central place by
87
 * writing an event handler for {@see onItemCommand OnItemCommand}.
88
 *
89
 * An additional event is raised if the <b>OnCommand</b> event has one of the following
90
 * command names:
91
 * - edit: user wants to edit an item. <b>OnEditCommand</b> event will be raised.
92
 * - update: user wants to save the change to an item. <b>OnUpdateCommand</b> event will be raised.
93
 * - select: user selects an item. <b>OnSelectedIndexChanged</b> event will be raised.
94
 * - delete: user deletes an item. <b>OnDeleteCommand</b> event will be raised.
95
 * - cancel: user cancels previously editting action. <b>OnCancelCommand</b> event will be raised.
96
 *
97
 * TDataList provides a few properties to support tiling the items.
98
 * The number of columns used to display the data items is specified via
99
 * {@see setRepeatColumns RepeatColumns} property, while the {@see setRepeatDirection RepeatDirection}
100
 * governs the order of the items being rendered.
101
 * The layout of the data items in the list is specified via {@see setRepeatLayout RepeatLayout},
102
 * which can take one of the following values:
103
 * - Table (default): items are organized using HTML table and cells.
104
 * When using this layout, one can set {@see setCellPadding CellPadding} and
105
 * {@see setCellSpacing CellSpacing} to adjust the cellpadding and cellpadding
106
 * of the table, and {@see setCaption Caption} and {@see setCaptionAlign CaptionAlign}
107
 * to add a table caption with the specified alignment.
108
 * - Flow: items are organized using HTML spans and breaks.
109
 * - Raw: TDataList does not generate any HTML tags to do the tiling.
110
 *
111
 * Items in TDataList can be in one of the three status: normal browsing,
112
 * being editted and being selected. To change the status of a particular
113
 * item, set {@see setSelectedItemIndex SelectedItemIndex} or
114
 * {@see setEditItemIndex EditItemIndex}. The former will change
115
 * the indicated item to selected mode, which will cause the item to
116
 * use {@see setSelectedItemTemplate SelectedItemTemplate} or
117
 * {@see setSelectedItemRenderer SelectedItemRenderer} for presentation.
118
 * The latter will change the indicated item to edit mode and to use corresponding
119
 * template or renderer.
120
 * Note, if an item is in edit mode, then selecting this item will have no effect.
121
 *
122
 * Different styles may be applied to items in different status. The style
123
 * application is performed in a hierarchical way: Style in higher hierarchy
124
 * will inherit from styles in lower hierarchy.
125
 * Starting from the lowest hierarchy, the item styles include
126
 * - item's own style
127
 * - {@see getItemStyle ItemStyle}
128
 * - {@see getAlternatingItemStyle AlternatingItemStyle}
129
 * - {@see getSelectedItemStyle SelectedItemStyle}
130
 * - {@see getEditItemStyle EditItemStyle}.
131
 * Therefore, if background color is set as red in {@see getItemStyle ItemStyle},
132
 * {@see getEditItemStyle EditItemStyle} will also have red background color
133
 * unless it is set to a different value explicitly.
134
 *
135
 * When a page containing a datalist is post back, the datalist will restore automatically
136
 * all its contents, including items, header, footer and separators.
137
 * However, the data row associated with each item will not be recovered and become null.
138
 * To access the data, use one of the following ways:
139
 * - Use {@see getDataKeys DataKeys} to obtain the data key associated with
140
 * the specified datalist item and use the key to fetch the corresponding data
141
 * from some persistent storage such as DB.
142
 * - Save the whole dataset in viewstate, which will restore the dataset automatically upon postback.
143
 * Be aware though, if the size of your dataset is big, your page size will become big. Some
144
 * complex data may also have serializing problem if saved in viewstate.
145
 *
146
 * @author Qiang Xue <[email protected]>
147
 * @since 3.0
148
 */
149
class TDataList extends TBaseDataList implements \Prado\Web\UI\INamingContainer, IRepeatInfoUser
150
{
151
	/**
152
	 * Command name that TDataList understands. They are case-insensitive.
153
	 */
154
	public const CMD_SELECT = 'Select';
155
	public const CMD_EDIT = 'Edit';
156
	public const CMD_UPDATE = 'Update';
157
	public const CMD_DELETE = 'Delete';
158
	public const CMD_CANCEL = 'Cancel';
159
160
	/**
161
	 * @var TDataListItemCollection item list
162
	 */
163
	private $_items;
164
	/**
165
	 * @var Itemplate various item templates
166
	 */
167
	private $_itemTemplate;
168
	private $_emptyTemplate;
169
	private $_alternatingItemTemplate;
170
	private $_selectedItemTemplate;
171
	private $_editItemTemplate;
172
	private $_headerTemplate;
173
	private $_footerTemplate;
174
	private $_separatorTemplate;
175
	/**
176
	 * @var \Prado\Web\UI\TControl header item
177
	 */
178
	private $_header;
179
	/**
180
	 * @var \Prado\Web\UI\TControl footer item
181
	 */
182
	private $_footer;
183
184
	/**
185
	 * @return TDataListItemCollection item list
186
	 */
187
	public function getItems()
188
	{
189
		if (!$this->_items) {
190
			$this->_items = new TDataListItemCollection();
191
		}
192
		return $this->_items;
193
	}
194
195
	/**
196
	 * @return int number of items
197
	 */
198
	public function getItemCount()
199
	{
200
		return $this->_items ? $this->_items->getCount() : 0;
201
	}
202
203
	/**
204
	 * @return string the class name for datalist items. Defaults to empty, meaning not set.
205
	 * @since 3.1.0
206
	 */
207
	public function getItemRenderer()
208
	{
209
		return $this->getViewState('ItemRenderer', '');
210
	}
211
212
	/**
213
	 * Sets the item renderer class.
214
	 *
215
	 * If not empty, the class will be used to instantiate as datalist items.
216
	 * This property takes precedence over {@see getItemTemplate ItemTemplate}.
217
	 *
218
	 * @param string $value the renderer class name in namespace format.
219
	 * @see setItemTemplate
220
	 * @since 3.1.0
221
	 */
222
	public function setItemRenderer($value)
223
	{
224
		$this->setViewState('ItemRenderer', $value, '');
225
	}
226
227
	/**
228
	 * @return string the class name for alternative datalist items. Defaults to empty, meaning not set.
229
	 * @since 3.1.0
230
	 */
231
	public function getAlternatingItemRenderer()
232
	{
233
		return $this->getViewState('AlternatingItemRenderer', '');
234
	}
235
236
	/**
237
	 * Sets the alternative item renderer class.
238
	 *
239
	 * If not empty, the class will be used to instantiate as alternative datalist items.
240
	 * This property takes precedence over {@see getAlternatingItemTemplate AlternatingItemTemplate}.
241
	 *
242
	 * @param string $value the renderer class name in namespace format.
243
	 * @see setAlternatingItemTemplate
244
	 * @since 3.1.0
245
	 */
246
	public function setAlternatingItemRenderer($value)
247
	{
248
		$this->setViewState('AlternatingItemRenderer', $value, '');
249
	}
250
251
	/**
252
	 * @return string the class name for the datalist item being editted. Defaults to empty, meaning not set.
253
	 * @since 3.1.0
254
	 */
255
	public function getEditItemRenderer()
256
	{
257
		return $this->getViewState('EditItemRenderer', '');
258
	}
259
260
	/**
261
	 * Sets the renderer class for the datalist item being editted.
262
	 *
263
	 * If not empty, the class will be used to instantiate as the datalist item.
264
	 * This property takes precedence over {@see getEditItemTemplate EditItemTemplate}.
265
	 *
266
	 * @param string $value the renderer class name in namespace format.
267
	 * @see setEditItemTemplate
268
	 * @since 3.1.0
269
	 */
270
	public function setEditItemRenderer($value)
271
	{
272
		$this->setViewState('EditItemRenderer', $value, '');
273
	}
274
275
	/**
276
	 * @return string the class name for the datalist item being selected. Defaults to empty, meaning not set.
277
	 * @since 3.1.0
278
	 */
279
	public function getSelectedItemRenderer()
280
	{
281
		return $this->getViewState('SelectedItemRenderer', '');
282
	}
283
284
	/**
285
	 * Sets the renderer class for the datalist item being selected.
286
	 *
287
	 * If not empty, the class will be used to instantiate as the datalist item.
288
	 * This property takes precedence over {@see getSelectedItemTemplate SelectedItemTemplate}.
289
	 *
290
	 * @param string $value the renderer class name in namespace format.
291
	 * @see setSelectedItemTemplate
292
	 * @since 3.1.0
293
	 */
294
	public function setSelectedItemRenderer($value)
295
	{
296
		$this->setViewState('SelectedItemRenderer', $value, '');
297
	}
298
299
	/**
300
	 * @return string the class name for datalist item separators. Defaults to empty, meaning not set.
301
	 * @since 3.1.0
302
	 */
303
	public function getSeparatorRenderer()
304
	{
305
		return $this->getViewState('SeparatorRenderer', '');
306
	}
307
308
	/**
309
	 * Sets the datalist item separator renderer class.
310
	 *
311
	 * If not empty, the class will be used to instantiate as datalist item separators.
312
	 * This property takes precedence over {@see getSeparatorTemplate SeparatorTemplate}.
313
	 *
314
	 * @param string $value the renderer class name in namespace format.
315
	 * @see setSeparatorTemplate
316
	 * @since 3.1.0
317
	 */
318
	public function setSeparatorRenderer($value)
319
	{
320
		$this->setViewState('SeparatorRenderer', $value, '');
321
	}
322
323
	/**
324
	 * @return string the class name for datalist header item. Defaults to empty, meaning not set.
325
	 * @since 3.1.0
326
	 */
327
	public function getHeaderRenderer()
328
	{
329
		return $this->getViewState('HeaderRenderer', '');
330
	}
331
332
	/**
333
	 * Sets the datalist header renderer class.
334
	 *
335
	 * If not empty, the class will be used to instantiate as datalist header item.
336
	 * This property takes precedence over {@see getHeaderTemplate HeaderTemplate}.
337
	 *
338
	 * @param string $value the renderer class name in namespace format.
339
	 * @see setHeaderTemplate
340
	 * @since 3.1.0
341
	 */
342
	public function setHeaderRenderer($value)
343
	{
344
		$this->setViewState('HeaderRenderer', $value, '');
345
	}
346
347
	/**
348
	 * @return string the class name for datalist footer item. Defaults to empty, meaning not set.
349
	 * @since 3.1.0
350
	 */
351
	public function getFooterRenderer()
352
	{
353
		return $this->getViewState('FooterRenderer', '');
354
	}
355
356
	/**
357
	 * Sets the datalist footer renderer class.
358
	 *
359
	 * If not empty, the class will be used to instantiate as datalist footer item.
360
	 * This property takes precedence over {@see getFooterTemplate FooterTemplate}.
361
	 *
362
	 * @param string $value the renderer class name in namespace format.
363
	 * @see setFooterTemplate
364
	 * @since 3.1.0
365
	 */
366
	public function setFooterRenderer($value)
367
	{
368
		$this->setViewState('FooterRenderer', $value, '');
369
	}
370
371
	/**
372
	 * @return string the class name for empty datalist item. Defaults to empty, meaning not set.
373
	 * @since 3.1.0
374
	 */
375
	public function getEmptyRenderer()
376
	{
377
		return $this->getViewState('EmptyRenderer', '');
378
	}
379
380
	/**
381
	 * Sets the datalist empty renderer class.
382
	 *
383
	 * The empty renderer is created as the child of the datalist
384
	 * if data bound to the datalist is empty.
385
	 * This property takes precedence over {@see getEmptyTemplate EmptyTemplate}.
386
	 *
387
	 * @param string $value the renderer class name in namespace format.
388
	 * @see setEmptyTemplate
389
	 * @since 3.1.0
390
	 */
391
	public function setEmptyRenderer($value)
392
	{
393
		$this->setViewState('EmptyRenderer', $value, '');
394
	}
395
396
	/**
397
	 * @return \Prado\Web\UI\ITemplate the template for item
398
	 */
399
	public function getItemTemplate()
400
	{
401
		return $this->_itemTemplate;
402
	}
403
404
	/**
405
	 * @param \Prado\Web\UI\ITemplate $value the template for item
406
	 * @throws TInvalidDataTypeException if the input is not an {@see \Prado\Web\UI\ITemplate} or not null.
407
	 */
408
	public function setItemTemplate($value)
409
	{
410
		if ($value instanceof ITemplate || $value === null) {
0 ignored issues
show
introduced by
$value is always a sub-type of Prado\Web\UI\ITemplate.
Loading history...
411
			$this->_itemTemplate = $value;
412
		} else {
413
			throw new TInvalidDataTypeException('datalist_template_required', 'ItemTemplate');
414
		}
415
	}
416
417
	/**
418
	 * @return TTableItemStyle the style for item
419
	 */
420
	public function getItemStyle()
421
	{
422
		if (($style = $this->getViewState('ItemStyle', null)) === null) {
423
			$style = new TTableItemStyle();
424
			$this->setViewState('ItemStyle', $style, null);
425
		}
426
		return $style;
427
	}
428
429
	/**
430
	 * @return \Prado\Web\UI\ITemplate the template for each alternating item
431
	 */
432
	public function getAlternatingItemTemplate()
433
	{
434
		return $this->_alternatingItemTemplate;
435
	}
436
437
	/**
438
	 * @param \Prado\Web\UI\ITemplate $value the template for each alternating item
439
	 * @throws TInvalidDataTypeException if the input is not an {@see \Prado\Web\UI\ITemplate} or not null.
440
	 */
441
	public function setAlternatingItemTemplate($value)
442
	{
443
		if ($value instanceof ITemplate || $value === null) {
0 ignored issues
show
introduced by
$value is always a sub-type of Prado\Web\UI\ITemplate.
Loading history...
444
			$this->_alternatingItemTemplate = $value;
445
		} else {
446
			throw new TInvalidDataTypeException('datalist_template_required', 'AlternatingItemType');
447
		}
448
	}
449
450
	/**
451
	 * @return TTableItemStyle the style for each alternating item
452
	 */
453
	public function getAlternatingItemStyle()
454
	{
455
		if (($style = $this->getViewState('AlternatingItemStyle', null)) === null) {
456
			$style = new TTableItemStyle();
457
			$this->setViewState('AlternatingItemStyle', $style, null);
458
		}
459
		return $style;
460
	}
461
462
	/**
463
	 * @return \Prado\Web\UI\ITemplate the selected item template
464
	 */
465
	public function getSelectedItemTemplate()
466
	{
467
		return $this->_selectedItemTemplate;
468
	}
469
470
	/**
471
	 * @param \Prado\Web\UI\ITemplate $value the selected item template
472
	 * @throws TInvalidDataTypeException if the input is not an {@see \Prado\Web\UI\ITemplate} or not null.
473
	 */
474
	public function setSelectedItemTemplate($value)
475
	{
476
		if ($value instanceof ITemplate || $value === null) {
0 ignored issues
show
introduced by
$value is always a sub-type of Prado\Web\UI\ITemplate.
Loading history...
477
			$this->_selectedItemTemplate = $value;
478
		} else {
479
			throw new TInvalidDataTypeException('datalist_template_required', 'SelectedItemTemplate');
480
		}
481
	}
482
483
	/**
484
	 * @return TTableItemStyle the style for selected item
485
	 */
486
	public function getSelectedItemStyle()
487
	{
488
		if (($style = $this->getViewState('SelectedItemStyle', null)) === null) {
489
			$style = new TTableItemStyle();
490
			$this->setViewState('SelectedItemStyle', $style, null);
491
		}
492
		return $style;
493
	}
494
495
	/**
496
	 * @return \Prado\Web\UI\ITemplate the edit item template
497
	 */
498
	public function getEditItemTemplate()
499
	{
500
		return $this->_editItemTemplate;
501
	}
502
503
	/**
504
	 * @param \Prado\Web\UI\ITemplate $value the edit item template
505
	 * @throws TInvalidDataTypeException if the input is not an {@see \Prado\Web\UI\ITemplate} or not null.
506
	 */
507
	public function setEditItemTemplate($value)
508
	{
509
		if ($value instanceof ITemplate || $value === null) {
0 ignored issues
show
introduced by
$value is always a sub-type of Prado\Web\UI\ITemplate.
Loading history...
510
			$this->_editItemTemplate = $value;
511
		} else {
512
			throw new TInvalidDataTypeException('datalist_template_required', 'EditItemTemplate');
513
		}
514
	}
515
516
	/**
517
	 * @return TTableItemStyle the style for edit item
518
	 */
519
	public function getEditItemStyle()
520
	{
521
		if (($style = $this->getViewState('EditItemStyle', null)) === null) {
522
			$style = new TTableItemStyle();
523
			$this->setViewState('EditItemStyle', $style, null);
524
		}
525
		return $style;
526
	}
527
528
	/**
529
	 * @return \Prado\Web\UI\ITemplate the header template
530
	 */
531
	public function getHeaderTemplate()
532
	{
533
		return $this->_headerTemplate;
534
	}
535
536
	/**
537
	 * @param \Prado\Web\UI\ITemplate $value the header template
538
	 * @throws TInvalidDataTypeException if the input is not an {@see \Prado\Web\UI\ITemplate} or not null.
539
	 */
540
	public function setHeaderTemplate($value)
541
	{
542
		if ($value instanceof ITemplate || $value === null) {
0 ignored issues
show
introduced by
$value is always a sub-type of Prado\Web\UI\ITemplate.
Loading history...
543
			$this->_headerTemplate = $value;
544
		} else {
545
			throw new TInvalidDataTypeException('datalist_template_required', 'HeaderTemplate');
546
		}
547
	}
548
549
	/**
550
	 * @return TTableItemStyle the style for header
551
	 */
552
	public function getHeaderStyle()
553
	{
554
		if (($style = $this->getViewState('HeaderStyle', null)) === null) {
555
			$style = new TTableItemStyle();
556
			$this->setViewState('HeaderStyle', $style, null);
557
		}
558
		return $style;
559
	}
560
561
	/**
562
	 * @return \Prado\Web\UI\TControl the header item
563
	 */
564
	public function getHeader()
565
	{
566
		return $this->_header;
567
	}
568
569
	/**
570
	 * @return \Prado\Web\UI\ITemplate the footer template
571
	 */
572
	public function getFooterTemplate()
573
	{
574
		return $this->_footerTemplate;
575
	}
576
577
	/**
578
	 * @param \Prado\Web\UI\ITemplate $value the footer template
579
	 * @throws TInvalidDataTypeException if the input is not an {@see \Prado\Web\UI\ITemplate} or not null.
580
	 */
581
	public function setFooterTemplate($value)
582
	{
583
		if ($value instanceof ITemplate || $value === null) {
0 ignored issues
show
introduced by
$value is always a sub-type of Prado\Web\UI\ITemplate.
Loading history...
584
			$this->_footerTemplate = $value;
585
		} else {
586
			throw new TInvalidDataTypeException('datalist_template_required', 'FooterTemplate');
587
		}
588
	}
589
590
	/**
591
	 * @return TTableItemStyle the style for footer
592
	 */
593
	public function getFooterStyle()
594
	{
595
		if (($style = $this->getViewState('FooterStyle', null)) === null) {
596
			$style = new TTableItemStyle();
597
			$this->setViewState('FooterStyle', $style, null);
598
		}
599
		return $style;
600
	}
601
602
	/**
603
	 * @return \Prado\Web\UI\TControl the footer item
604
	 */
605
	public function getFooter()
606
	{
607
		return $this->_footer;
608
	}
609
610
	/**
611
	 * @return \Prado\Web\UI\ITemplate the template applied when no data is bound to the datalist
612
	 */
613
	public function getEmptyTemplate()
614
	{
615
		return $this->_emptyTemplate;
616
	}
617
618
	/**
619
	 * @param \Prado\Web\UI\ITemplate $value the template applied when no data is bound to the datalist
620
	 * @throws TInvalidDataTypeException if the input is not an {@see \Prado\Web\UI\ITemplate} or not null.
621
	 */
622
	public function setEmptyTemplate($value)
623
	{
624
		if ($value instanceof ITemplate || $value === null) {
0 ignored issues
show
introduced by
$value is always a sub-type of Prado\Web\UI\ITemplate.
Loading history...
625
			$this->_emptyTemplate = $value;
626
		} else {
627
			throw new TInvalidDataTypeException('datalist_template_required', 'EmptyTemplate');
628
		}
629
	}
630
631
	/**
632
	 * @return \Prado\Web\UI\ITemplate the separator template
633
	 */
634
	public function getSeparatorTemplate()
635
	{
636
		return $this->_separatorTemplate;
637
	}
638
639
	/**
640
	 * @param \Prado\Web\UI\ITemplate $value the separator template
641
	 * @throws TInvalidDataTypeException if the input is not an {@see \Prado\Web\UI\ITemplate} or not null.
642
	 */
643
	public function setSeparatorTemplate($value)
644
	{
645
		if ($value instanceof ITemplate || $value === null) {
0 ignored issues
show
introduced by
$value is always a sub-type of Prado\Web\UI\ITemplate.
Loading history...
646
			$this->_separatorTemplate = $value;
647
		} else {
648
			throw new TInvalidDataTypeException('datalist_template_required', 'SeparatorTemplate');
649
		}
650
	}
651
652
	/**
653
	 * @return TTableItemStyle the style for separator
654
	 */
655
	public function getSeparatorStyle()
656
	{
657
		if (($style = $this->getViewState('SeparatorStyle', null)) === null) {
658
			$style = new TTableItemStyle();
659
			$this->setViewState('SeparatorStyle', $style, null);
660
		}
661
		return $style;
662
	}
663
664
	/**
665
	 * @return int the zero-based index of the selected item in {@see getItems Items}.
666
	 * A value -1 means no item selected.
667
	 */
668
	public function getSelectedItemIndex()
669
	{
670
		return $this->getViewState('SelectedItemIndex', -1);
671
	}
672
673
	/**
674
	 * Selects an item by its index in {@see getItems Items}.
675
	 * Previously selected item will be un-selected.
676
	 * If the item to be selected is already in edit mode, it will remain in edit mode.
677
	 * If the index is less than 0, any existing selection will be cleared up.
678
	 * @param int $value the selected item index
679
	 */
680
	public function setSelectedItemIndex($value)
681
	{
682
		if (($value = TPropertyValue::ensureInteger($value)) < 0) {
683
			$value = -1;
684
		}
685
		if (($current = $this->getSelectedItemIndex()) !== $value) {
686
			$this->setViewState('SelectedItemIndex', $value, -1);
687
			$items = $this->getItems();
688
			$itemCount = $items->getCount();
689
			if ($current >= 0 && $current < $itemCount) {
690
				$item = $items->itemAt($current);
691
				if (($item instanceof IItemDataRenderer) && $item->getItemType() !== TListItemType::EditItem) {
692
					$item->setItemType($current % 2 ? TListItemType::AlternatingItem : TListItemType::Item);
0 ignored issues
show
Bug introduced by
$current % 2 ? Prado\Web...ols\TListItemType::Item of type string is incompatible with the type Prado\Web\UI\WebControls\TListItemType expected by parameter $value of Prado\Web\UI\WebControls...Renderer::setItemType(). ( Ignorable by Annotation )

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

692
					$item->setItemType(/** @scrutinizer ignore-type */ $current % 2 ? TListItemType::AlternatingItem : TListItemType::Item);
Loading history...
693
				}
694
			}
695
			if ($value >= 0 && $value < $itemCount) {
696
				$item = $items->itemAt($value);
697
				if (($item instanceof IItemDataRenderer) && $item->getItemType() !== TListItemType::EditItem) {
698
					$item->setItemType(TListItemType::SelectedItem);
699
				}
700
			}
701
		}
702
	}
703
704
	/**
705
	 * @return \Prado\Web\UI\TControl the selected item, null if no item is selected.
706
	 */
707
	public function getSelectedItem()
708
	{
709
		$index = $this->getSelectedItemIndex();
710
		$items = $this->getItems();
711
		if ($index >= 0 && $index < $items->getCount()) {
712
			return $items->itemAt($index);
713
		} else {
714
			return null;
715
		}
716
	}
717
718
	/**
719
	 * @throws TInvalidOperationException if {@see getDataKeyField DataKeyField} is empty.
720
	 * @return mixed the key value of the currently selected item
721
	 */
722
	public function getSelectedDataKey()
723
	{
724
		if ($this->getDataKeyField() === '') {
725
			throw new TInvalidOperationException('datalist_datakeyfield_required');
726
		}
727
		$index = $this->getSelectedItemIndex();
728
		$dataKeys = $this->getDataKeys();
729
		if ($index >= 0 && $index < $dataKeys->getCount()) {
730
			return $dataKeys->itemAt($index);
731
		} else {
732
			return null;
733
		}
734
	}
735
736
	/**
737
	 * @return int the zero-based index of the edit item in {@see getItems Items}.
738
	 * A value -1 means no item is in edit mode.
739
	 */
740
	public function getEditItemIndex()
741
	{
742
		return $this->getViewState('EditItemIndex', -1);
743
	}
744
745
	/**
746
	 * Edits an item by its index in {@see getItems Items}.
747
	 * Previously editting item will change to normal item state.
748
	 * If the index is less than 0, any existing edit item will be cleared up.
749
	 * @param int $value the edit item index
750
	 */
751
	public function setEditItemIndex($value)
752
	{
753
		if (($value = TPropertyValue::ensureInteger($value)) < 0) {
754
			$value = -1;
755
		}
756
		if (($current = $this->getEditItemIndex()) !== $value) {
757
			$this->setViewState('EditItemIndex', $value, -1);
758
			$items = $this->getItems();
759
			$itemCount = $items->getCount();
760
			if ($current >= 0 && $current < $itemCount) {
761
				$items->itemAt($current)->setItemType($current % 2 ? TListItemType::AlternatingItem : TListItemType::Item);
762
			}
763
			if ($value >= 0 && $value < $itemCount) {
764
				$items->itemAt($value)->setItemType(TListItemType::EditItem);
765
			}
766
		}
767
	}
768
769
	/**
770
	 * @return \Prado\Web\UI\TControl the edit item
771
	 */
772
	public function getEditItem()
773
	{
774
		$index = $this->getEditItemIndex();
775
		$items = $this->getItems();
776
		if ($index >= 0 && $index < $items->getCount()) {
777
			return $items->itemAt($index);
778
		} else {
779
			return null;
780
		}
781
	}
782
783
	/**
784
	 * @return bool whether the header should be shown. Defaults to true.
785
	 */
786
	public function getShowHeader()
787
	{
788
		return $this->getViewState('ShowHeader', true);
789
	}
790
791
	/**
792
	 * @param bool $value whether to show header
793
	 */
794
	public function setShowHeader($value)
795
	{
796
		$this->setViewState('ShowHeader', TPropertyValue::ensureBoolean($value), true);
797
	}
798
799
	/**
800
	 * @return bool whether the footer should be shown. Defaults to true.
801
	 */
802
	public function getShowFooter()
803
	{
804
		return $this->getViewState('ShowFooter', true);
805
	}
806
807
	/**
808
	 * @param bool $value whether to show footer
809
	 */
810
	public function setShowFooter($value)
811
	{
812
		$this->setViewState('ShowFooter', TPropertyValue::ensureBoolean($value), true);
813
	}
814
815
	/**
816
	 * @return TRepeatInfo repeat information (primarily used by control developers)
817
	 */
818
	protected function getRepeatInfo()
819
	{
820
		if (($repeatInfo = $this->getViewState('RepeatInfo', null)) === null) {
821
			$repeatInfo = new TRepeatInfo();
822
			$this->setViewState('RepeatInfo', $repeatInfo, null);
823
		}
824
		return $repeatInfo;
825
	}
826
827
	/**
828
	 * @return string caption of the table layout
829
	 */
830
	public function getCaption()
831
	{
832
		return $this->getRepeatInfo()->getCaption();
833
	}
834
835
	/**
836
	 * @param string $value caption of the table layout
837
	 */
838
	public function setCaption($value)
839
	{
840
		$this->getRepeatInfo()->setCaption($value);
841
	}
842
843
	/**
844
	 * @return TTableCaptionAlign alignment of the caption of the table layout. Defaults to TTableCaptionAlign::NotSet.
845
	 * @deprecated use the CSS properties caption-side and text-align instead.
846
	 */
847
	public function getCaptionAlign()
848
	{
849
		return $this->getRepeatInfo()->getCaptionAlign();
850
	}
851
852
	/**
853
	 * @param TTableCaptionAlign $value alignment of the caption of the table layout.
854
	 * @deprecated use the CSS properties caption-side and text-align instead.
855
	 */
856
	public function setCaptionAlign($value)
857
	{
858
		$this->getRepeatInfo()->setCaptionAlign($value);
859
	}
860
861
	/**
862
	 * @return int the number of columns that the list should be displayed with. Defaults to 0 meaning not set.
863
	 */
864
	public function getRepeatColumns()
865
	{
866
		return $this->getRepeatInfo()->getRepeatColumns();
867
	}
868
869
	/**
870
	 * @param int $value the number of columns that the list should be displayed with.
871
	 */
872
	public function setRepeatColumns($value)
873
	{
874
		$this->getRepeatInfo()->setRepeatColumns($value);
875
	}
876
877
	/**
878
	 * @return TRepeatDirection the direction of traversing the list, defaults to TRepeatDirection::Vertical
879
	 */
880
	public function getRepeatDirection()
881
	{
882
		return $this->getRepeatInfo()->getRepeatDirection();
883
	}
884
885
	/**
886
	 * @param TRepeatDirection $value the direction of traversing the list
887
	 */
888
	public function setRepeatDirection($value)
889
	{
890
		$this->getRepeatInfo()->setRepeatDirection($value);
891
	}
892
893
	/**
894
	 * @return TRepeatLayout how the list should be displayed, using table or using line breaks. Defaults to TRepeatLayout::Table.
895
	 */
896
	public function getRepeatLayout()
897
	{
898
		return $this->getRepeatInfo()->getRepeatLayout();
899
	}
900
901
	/**
902
	 * @param TRepeatLayout $value how the list should be displayed, using table or using line breaks
903
	 */
904
	public function setRepeatLayout($value)
905
	{
906
		$this->getRepeatInfo()->setRepeatLayout($value);
907
	}
908
909
	/**
910
	 * This method overrides parent's implementation to handle
911
	 * {@see onItemCommand OnItemCommand} event which is bubbled from
912
	 * datalist items and their child controls.
913
	 * If the event parameter is {@see \Prado\Web\UI\WebControls\TDataListCommandEventParameter} and
914
	 * the command name is a recognized one, which includes 'select', 'edit',
915
	 * 'delete', 'update', and 'cancel' (case-insensitive), then a
916
	 * corresponding command event is also raised (such as {@see onEditCommand OnEditCommand}).
917
	 * This method should only be used by control developers.
918
	 * @param \Prado\Web\UI\TControl $sender the sender of the event
919
	 * @param \Prado\TEventParameter $param event parameter
920
	 * @return bool whether the event bubbling should stop here.
921
	 */
922
	public function bubbleEvent($sender, $param)
923
	{
924
		if ($param instanceof TDataListCommandEventParameter) {
925
			$this->onItemCommand($param);
926
			$command = $param->getCommandName();
927
			if (strcasecmp($command, self::CMD_SELECT) === 0) {
928
				if (($item = $param->getItem()) instanceof IItemDataRenderer) {
929
					$this->setSelectedItemIndex($item->getItemIndex());
0 ignored issues
show
Bug introduced by
The method getItemIndex() does not exist on Prado\Web\UI\TControl. 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

929
					$this->setSelectedItemIndex($item->/** @scrutinizer ignore-call */ getItemIndex());
Loading history...
930
				}
931
				$this->onSelectedIndexChanged($param);
932
				return true;
933
			} elseif (strcasecmp($command, self::CMD_EDIT) === 0) {
934
				$this->onEditCommand($param);
935
				return true;
936
			} elseif (strcasecmp($command, self::CMD_DELETE) === 0) {
937
				$this->onDeleteCommand($param);
938
				return true;
939
			} elseif (strcasecmp($command, self::CMD_UPDATE) === 0) {
940
				$this->onUpdateCommand($param);
941
				return true;
942
			} elseif (strcasecmp($command, self::CMD_CANCEL) === 0) {
943
				$this->onCancelCommand($param);
944
				return true;
945
			}
946
		}
947
		return false;
948
	}
949
950
951
	/**
952
	 * Raises <b>OnItemCreated</b> event.
953
	 * This method is invoked after a data list item is created and instantiated with
954
	 * template, but before added to the page hierarchy.
955
	 * The datalist item control responsible for the event
956
	 * can be determined from the event parameter.
957
	 * If you override this method, be sure to call parent's implementation
958
	 * so that event handlers have chance to respond to the event.
959
	 * @param TDataListItemEventParameter $param event parameter
960
	 */
961
	public function onItemCreated($param)
962
	{
963
		$this->raiseEvent('OnItemCreated', $this, $param);
964
	}
965
966
	/**
967
	 * Raises <b>OnItemDataBound</b> event.
968
	 * This method is invoked right after an item is data bound.
969
	 * The datalist item control responsible for the event
970
	 * can be determined from the event parameter.
971
	 * If you override this method, be sure to call parent's implementation
972
	 * so that event handlers have chance to respond to the event.
973
	 * @param TDataListItemEventParameter $param event parameter
974
	 */
975
	public function onItemDataBound($param)
976
	{
977
		$this->raiseEvent('OnItemDataBound', $this, $param);
978
	}
979
980
	/**
981
	 * Raises <b>OnItemCommand</b> event.
982
	 * This method is invoked when a child control of the data list
983
	 * raises an <b>OnCommand</b> event.
984
	 * @param TDataListCommandEventParameter $param event parameter
985
	 */
986
	public function onItemCommand($param)
987
	{
988
		$this->raiseEvent('OnItemCommand', $this, $param);
989
	}
990
991
	/**
992
	 * Raises <b>OnEditCommand</b> event.
993
	 * This method is invoked when a child control of the data list
994
	 * raises an <b>OnCommand</b> event and the command name is 'edit' (case-insensitive).
995
	 * @param TDataListCommandEventParameter $param event parameter
996
	 */
997
	public function onEditCommand($param)
998
	{
999
		$this->raiseEvent('OnEditCommand', $this, $param);
1000
	}
1001
1002
	/**
1003
	 * Raises <b>OnDeleteCommand</b> event.
1004
	 * This method is invoked when a child control of the data list
1005
	 * raises an <b>OnCommand</b> event and the command name is 'delete' (case-insensitive).
1006
	 * @param TDataListCommandEventParameter $param event parameter
1007
	 */
1008
	public function onDeleteCommand($param)
1009
	{
1010
		$this->raiseEvent('OnDeleteCommand', $this, $param);
1011
	}
1012
1013
	/**
1014
	 * Raises <b>OnUpdateCommand</b> event.
1015
	 * This method is invoked when a child control of the data list
1016
	 * raises an <b>OnCommand</b> event and the command name is 'update' (case-insensitive).
1017
	 * @param TDataListCommandEventParameter $param event parameter
1018
	 */
1019
	public function onUpdateCommand($param)
1020
	{
1021
		$this->raiseEvent('OnUpdateCommand', $this, $param);
1022
	}
1023
1024
	/**
1025
	 * Raises <b>OnCancelCommand</b> event.
1026
	 * This method is invoked when a child control of the data list
1027
	 * raises an <b>OnCommand</b> event and the command name is 'cancel' (case-insensitive).
1028
	 * @param TDataListCommandEventParameter $param event parameter
1029
	 */
1030
	public function onCancelCommand($param)
1031
	{
1032
		$this->raiseEvent('OnCancelCommand', $this, $param);
1033
	}
1034
1035
	/**
1036
	 * Returns a value indicating whether this control contains header item.
1037
	 * This method is required by {@see \Prado\Web\UI\WebControls\IRepeatInfoUser} interface.
1038
	 * @return bool whether the datalist has header
1039
	 */
1040
	public function getHasHeader()
1041
	{
1042
		return ($this->getShowHeader() && ($this->_headerTemplate !== null || $this->getHeaderRenderer() !== ''));
1043
	}
1044
1045
	/**
1046
	 * Returns a value indicating whether this control contains footer item.
1047
	 * This method is required by {@see \Prado\Web\UI\WebControls\IRepeatInfoUser} interface.
1048
	 * @return bool whether the datalist has footer
1049
	 */
1050
	public function getHasFooter()
1051
	{
1052
		return ($this->getShowFooter() && ($this->_footerTemplate !== null || $this->getFooterRenderer() !== ''));
1053
	}
1054
1055
	/**
1056
	 * Returns a value indicating whether this control contains separator items.
1057
	 * This method is required by {@see \Prado\Web\UI\WebControls\IRepeatInfoUser} interface.
1058
	 * @return bool always false.
1059
	 */
1060
	public function getHasSeparators()
1061
	{
1062
		return $this->_separatorTemplate !== null || $this->getSeparatorRenderer() !== '';
1063
	}
1064
1065
	/**
1066
	 * Returns a style used for rendering items.
1067
	 * This method is required by {@see \Prado\Web\UI\WebControls\IRepeatInfoUser} interface.
1068
	 * @param string $itemType item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager)
1069
	 * @param int $index index of the item being rendered
1070
	 * @return TStyle item style
1071
	 */
1072
	public function generateItemStyle($itemType, $index)
1073
	{
1074
		if (($item = $this->getItem($itemType, $index)) !== null && ($item instanceof IStyleable) && $item->getHasStyle()) {
0 ignored issues
show
Bug introduced by
$itemType of type string is incompatible with the type Prado\Web\UI\WebControls\TListItemType expected by parameter $itemType of Prado\Web\UI\WebControls\TDataList::getItem(). ( Ignorable by Annotation )

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

1074
		if (($item = $this->getItem(/** @scrutinizer ignore-type */ $itemType, $index)) !== null && ($item instanceof IStyleable) && $item->getHasStyle()) {
Loading history...
1075
			$style = $item->getStyle();
1076
			$item->clearStyle();
1077
			return $style;
1078
		} else {
1079
			return null;
1080
		}
1081
	}
1082
1083
	/**
1084
	 * Renders an item in the list.
1085
	 * This method is required by {@see \Prado\Web\UI\WebControls\IRepeatInfoUser} interface.
1086
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for rendering purpose
1087
	 * @param TRepeatInfo $repeatInfo repeat information
1088
	 * @param string $itemType item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager)
1089
	 * @param int $index zero-based index of the item in the item list
1090
	 */
1091
	public function renderItem($writer, $repeatInfo, $itemType, $index)
1092
	{
1093
		$item = $this->getItem($itemType, $index);
0 ignored issues
show
Bug introduced by
$itemType of type string is incompatible with the type Prado\Web\UI\WebControls\TListItemType expected by parameter $itemType of Prado\Web\UI\WebControls\TDataList::getItem(). ( Ignorable by Annotation )

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

1093
		$item = $this->getItem(/** @scrutinizer ignore-type */ $itemType, $index);
Loading history...
1094
		if ($repeatInfo->getRepeatLayout() === TRepeatLayout::Raw && $item::class === 'TDataListItem') {
0 ignored issues
show
introduced by
The condition $repeatInfo->getRepeatLa...rols\TRepeatLayout::Raw is always false.
Loading history...
1095
			$item->setTagName('div');
1096
		}
1097
		$item->renderControl($writer);
1098
	}
1099
1100
	/**
1101
	 * @param TListItemType $itemType item type
1102
	 * @param int $index item index
1103
	 * @return TDataListItem data list item with the specified item type and index
1104
	 */
1105
	private function getItem($itemType, $index)
1106
	{
1107
		switch ($itemType) {
1108
			case TListItemType::Item:
1109
			case TListItemType::AlternatingItem:
1110
			case TListItemType::SelectedItem:
1111
			case TListItemType::EditItem:
1112
				return $this->getItems()->itemAt($index);
1113
			case TListItemType::Header:
1114
				return $this->getControls()->itemAt(0);
1115
			case TListItemType::Footer:
1116
				return $this->getControls()->itemAt($this->getControls()->getCount() - 1);
1117
			case TListItemType::Separator:
1118
				$i = $index + $index + 1;
1119
				if ($this->_headerTemplate !== null || $this->getHeaderRenderer() !== '') {
1120
					$i++;
1121
				}
1122
				return $this->getControls()->itemAt($i);
1123
		}
1124
		return null;
1125
	}
1126
1127
	/**
1128
	 * Creates a datalist item.
1129
	 * This method invokes {@see createItem} to create a new datalist item.
1130
	 * @param int $itemIndex zero-based item index.
1131
	 * @param TListItemType $itemType item type
1132
	 * @return \Prado\Web\UI\TControl the created item, null if item is not created
1133
	 */
1134
	private function createItemInternal($itemIndex, $itemType)
1135
	{
1136
		if (($item = $this->createItem($itemIndex, $itemType)) !== null) {
1137
			$param = new TDataListItemEventParameter($item);
1138
			$this->onItemCreated($param);
1139
			$this->getControls()->add($item);
1140
			return $item;
1141
		} else {
1142
			return null;
1143
		}
1144
	}
1145
1146
	/**
1147
	 * Creates a datalist item and performs databinding.
1148
	 * This method invokes {@see createItem} to create a new datalist item.
1149
	 * @param int $itemIndex zero-based item index.
1150
	 * @param TListItemType $itemType item type
1151
	 * @param mixed $dataItem data to be associated with the item
1152
	 * @return \Prado\Web\UI\TControl the created item, null if item is not created
1153
	 */
1154
	private function createItemWithDataInternal($itemIndex, $itemType, $dataItem)
1155
	{
1156
		if (($item = $this->createItem($itemIndex, $itemType)) !== null) {
1157
			$param = new TDataListItemEventParameter($item);
1158
			if ($item instanceof \Prado\IDataRenderer) {
1159
				$item->setData($dataItem);
1160
			}
1161
			$this->onItemCreated($param);
1162
			$this->getControls()->add($item);
1163
			$item->dataBind();
1164
			$this->onItemDataBound($param);
1165
			return $item;
1166
		} else {
1167
			return null;
1168
		}
1169
	}
1170
1171
	private function getAlternatingItemDisplay()
1172
	{
1173
		if (($classPath = $this->getAlternatingItemRenderer()) === '' && $this->_alternatingItemTemplate === null) {
1174
			return [$this->getItemRenderer(), $this->_itemTemplate];
1175
		} else {
1176
			return [$classPath, $this->_alternatingItemTemplate];
1177
		}
1178
	}
1179
1180
	private function getSelectedItemDisplay($itemIndex)
1181
	{
1182
		if (($classPath = $this->getSelectedItemRenderer()) === '' && $this->_selectedItemTemplate === null) {
1183
			if ($itemIndex % 2 === 0) {
1184
				return [$this->getItemRenderer(), $this->_itemTemplate];
1185
			} else {
1186
				return $this->getAlternatingItemDisplay();
1187
			}
1188
		} else {
1189
			return [$classPath, $this->_selectedItemTemplate];
1190
		}
1191
	}
1192
1193
	private function getEditItemDisplay($itemIndex)
1194
	{
1195
		if (($classPath = $this->getEditItemRenderer()) === '' && $this->_editItemTemplate === null) {
1196
			return $this->getSelectedItemDisplay($itemIndex);
1197
		} else {
1198
			return [$classPath, $this->_editItemTemplate];
1199
		}
1200
	}
1201
1202
	/**
1203
	 * Creates a datalist item instance based on the item type and index.
1204
	 * @param int $itemIndex zero-based item index
1205
	 * @param TListItemType $itemType item type
1206
	 * @return \Prado\Web\UI\TControl created datalist item
1207
	 */
1208
	protected function createItem($itemIndex, $itemType)
1209
	{
1210
		$template = null;
1211
		$classPath = null;
1212
		switch ($itemType) {
1213
			case TListItemType::Item:
1214
				$classPath = $this->getItemRenderer();
1215
				$template = $this->_itemTemplate;
1216
				break;
1217
			case TListItemType::AlternatingItem:
1218
				[$classPath, $template] = $this->getAlternatingItemDisplay();
1219
				break;
1220
			case TListItemType::SelectedItem:
1221
				[$classPath, $template] = $this->getSelectedItemDisplay($itemIndex);
1222
				break;
1223
			case TListItemType::EditItem:
1224
				[$classPath, $template] = $this->getEditItemDisplay($itemIndex);
1225
				break;
1226
			case TListItemType::Header:
1227
				$classPath = $this->getHeaderRenderer();
1228
				$template = $this->_headerTemplate;
1229
				break;
1230
			case TListItemType::Footer:
1231
				$classPath = $this->getFooterRenderer();
1232
				$template = $this->_footerTemplate;
1233
				break;
1234
			case TListItemType::Separator:
1235
				$classPath = $this->getSeparatorRenderer();
1236
				$template = $this->_separatorTemplate;
1237
				break;
1238
			default:
1239
				throw new TInvalidDataValueException('datalist_itemtype_unknown', $itemType);
0 ignored issues
show
Bug introduced by
$itemType of type Prado\Web\UI\WebControls\TListItemType is incompatible with the type string expected by parameter $errorMessage of Prado\Exceptions\TInvali...xception::__construct(). ( Ignorable by Annotation )

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

1239
				throw new TInvalidDataValueException('datalist_itemtype_unknown', /** @scrutinizer ignore-type */ $itemType);
Loading history...
1240
		}
1241
		if ($classPath !== '') {
1242
			$item = Prado::createComponent($classPath);
1243
			if ($item instanceof IItemDataRenderer) {
1244
				$item->setItemIndex($itemIndex);
1245
				$item->setItemType($itemType);
1246
			}
1247
		} elseif ($template !== null) {
1248
			$item = new TDataListItem();
1249
			$item->setItemIndex($itemIndex);
1250
			$item->setItemType($itemType);
1251
			$template->instantiateIn($item);
1252
		} else {
1253
			$item = null;
1254
		}
1255
1256
		return $item;
1257
	}
1258
1259
	/**
1260
	 * Creates empty datalist content.
1261
	 */
1262
	protected function createEmptyContent()
1263
	{
1264
		if (($classPath = $this->getEmptyRenderer()) !== '') {
1265
			$this->getControls()->add(Prado::createComponent($classPath));
1266
		} elseif ($this->_emptyTemplate !== null) {
1267
			$this->_emptyTemplate->instantiateIn($this);
1268
		}
1269
	}
1270
1271
	/**
1272
	 * Applies styles to items, header, footer and separators.
1273
	 * Item styles are applied in a hierarchical way. Style in higher hierarchy
1274
	 * will inherit from styles in lower hierarchy.
1275
	 * Starting from the lowest hierarchy, the item styles include
1276
	 * item's own style, {@see getItemStyle ItemStyle}, {@see getAlternatingItemStyle AlternatingItemStyle},
1277
	 * {@see getSelectedItemStyle SelectedItemStyle}, and {@see getEditItemStyle EditItemStyle}.
1278
	 * Therefore, if background color is set as red in {@see getItemStyle ItemStyle},
1279
	 * {@see getEditItemStyle EditItemStyle} will also have red background color
1280
	 * unless it is set to a different value explicitly.
1281
	 */
1282
	protected function applyItemStyles()
1283
	{
1284
		$itemStyle = $this->getViewState('ItemStyle', null);
1285
1286
		$alternatingItemStyle = $this->getViewState('AlternatingItemStyle', null);
1287
		if ($itemStyle !== null) {
1288
			if ($alternatingItemStyle === null) {
1289
				$alternatingItemStyle = $itemStyle;
1290
			} else {
1291
				$alternatingItemStyle->mergeWith($itemStyle);
1292
			}
1293
		}
1294
1295
		$selectedItemStyle = $this->getViewState('SelectedItemStyle', null);
1296
1297
		$editItemStyle = $this->getViewState('EditItemStyle', null);
1298
		if ($selectedItemStyle !== null) {
1299
			if ($editItemStyle === null) {
1300
				$editItemStyle = $selectedItemStyle;
1301
			} else {
1302
				$editItemStyle->mergeWith($selectedItemStyle);
1303
			}
1304
		}
1305
1306
		// apply header style if any
1307
		if ($this->_header !== null && $this->_header instanceof IStyleable) {
1308
			if ($headerStyle = $this->getViewState('HeaderStyle', null)) {
1309
				$this->_header->getStyle()->mergeWith($headerStyle);
0 ignored issues
show
Bug introduced by
The method getStyle() does not exist on Prado\Web\UI\TControl. 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

1309
				$this->_header->/** @scrutinizer ignore-call */ 
1310
                    getStyle()->mergeWith($headerStyle);
Loading history...
1310
			}
1311
		}
1312
1313
		// apply footer style if any
1314
		if ($this->_footer !== null && $this->_footer instanceof IStyleable) {
1315
			if ($footerStyle = $this->getViewState('FooterStyle', null)) {
1316
				$this->_footer->getStyle()->mergeWith($footerStyle);
1317
			}
1318
		}
1319
1320
		$selectedIndex = $this->getSelectedItemIndex();
1321
		$editIndex = $this->getEditItemIndex();
1322
1323
		// apply item styles if any
1324
		foreach ($this->getItems() as $index => $item) {
1325
			if ($index === $editIndex) {
1326
				$style = $editItemStyle;
1327
			} elseif ($index === $selectedIndex) {
1328
				$style = $selectedItemStyle;
1329
			} elseif ($index % 2 === 0) {
1330
				$style = $itemStyle;
1331
			} else {
1332
				$style = $alternatingItemStyle;
1333
			}
1334
			if ($style && $item instanceof IStyleable) {
1335
				$item->getStyle()->mergeWith($style);
1336
			}
1337
		}
1338
1339
		// apply separator style if any
1340
		if (($separatorStyle = $this->getViewState('SeparatorStyle', null)) !== null && $this->getHasSeparators()) {
1341
			$controls = $this->getControls();
1342
			$count = $controls->getCount();
1343
			for ($i = $this->_header ? 2 : 1; $i < $count; $i += 2) {
1344
				if (($separator = $controls->itemAt($i)) instanceof IStyleable) {
1345
					$separator->getStyle()->mergeWith($separatorStyle);
1346
				}
1347
			}
1348
		}
1349
	}
1350
1351
	/**
1352
	 * Saves item count in viewstate.
1353
	 * This method is invoked right before control state is to be saved.
1354
	 */
1355
	public function saveState()
1356
	{
1357
		parent::saveState();
1358
		if ($this->_items) {
1359
			$this->setViewState('ItemCount', $this->_items->getCount(), 0);
1360
		} else {
1361
			$this->clearViewState('ItemCount');
1362
		}
1363
	}
1364
1365
	/**
1366
	 * Loads item count information from viewstate.
1367
	 * This method is invoked right after control state is loaded.
1368
	 */
1369
	public function loadState()
1370
	{
1371
		parent::loadState();
1372
		if (!$this->getIsDataBound()) {
1373
			$this->restoreItemsFromViewState();
1374
		}
1375
		$this->clearViewState('ItemCount');
1376
	}
1377
1378
	/**
1379
	 * Clears up all items in the data list.
1380
	 */
1381
	public function reset()
1382
	{
1383
		$this->getControls()->clear();
1384
		$this->getItems()->clear();
1385
		$this->_header = null;
1386
		$this->_footer = null;
1387
	}
1388
1389
	/**
1390
	 * Creates data list items based on viewstate information.
1391
	 */
1392
	protected function restoreItemsFromViewState()
1393
	{
1394
		$this->reset();
1395
		if (($itemCount = $this->getViewState('ItemCount', 0)) > 0) {
1396
			$items = $this->getItems();
1397
			$selectedIndex = $this->getSelectedItemIndex();
1398
			$editIndex = $this->getEditItemIndex();
1399
			$hasSeparator = $this->_separatorTemplate !== null || $this->getSeparatorRenderer() !== '';
1400
			$this->_header = $this->createItemInternal(-1, TListItemType::Header);
0 ignored issues
show
Bug introduced by
Prado\Web\UI\WebControls\TListItemType::Header of type string is incompatible with the type Prado\Web\UI\WebControls\TListItemType expected by parameter $itemType of Prado\Web\UI\WebControls...t::createItemInternal(). ( Ignorable by Annotation )

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

1400
			$this->_header = $this->createItemInternal(-1, /** @scrutinizer ignore-type */ TListItemType::Header);
Loading history...
1401
			for ($i = 0; $i < $itemCount; ++$i) {
1402
				if ($hasSeparator && $i > 0) {
1403
					$this->createItemInternal($i - 1, TListItemType::Separator);
1404
				}
1405
				if ($i === $editIndex) {
1406
					$itemType = TListItemType::EditItem;
1407
				} elseif ($i === $selectedIndex) {
1408
					$itemType = TListItemType::SelectedItem;
1409
				} else {
1410
					$itemType = $i % 2 ? TListItemType::AlternatingItem : TListItemType::Item;
1411
				}
1412
				$items->add($this->createItemInternal($i, $itemType));
1413
			}
1414
			$this->_footer = $this->createItemInternal(-1, TListItemType::Footer);
1415
		} else {
1416
			$this->createEmptyContent();
1417
		}
1418
		$this->clearChildState();
1419
	}
1420
1421
	/**
1422
	 * Performs databinding to populate data list items from data source.
1423
	 * This method is invoked by dataBind().
1424
	 * You may override this function to provide your own way of data population.
1425
	 * @param \Traversable $data the data
1426
	 */
1427
	protected function performDataBinding($data)
1428
	{
1429
		$this->reset();
1430
		$keys = $this->getDataKeys();
1431
		$keys->clear();
1432
		$keyField = $this->getDataKeyField();
1433
		$itemIndex = 0;
1434
		$items = $this->getItems();
1435
		$hasSeparator = $this->_separatorTemplate !== null || $this->getSeparatorRenderer() !== '';
1436
		$selectedIndex = $this->getSelectedItemIndex();
1437
		$editIndex = $this->getEditItemIndex();
1438
		foreach ($data as $key => $dataItem) {
1439
			if ($keyField !== '') {
1440
				$keys->add($this->getDataFieldValue($dataItem, $keyField));
1441
			} else {
1442
				$keys->add($key);
1443
			}
1444
			if ($itemIndex === 0) {
1445
				$this->_header = $this->createItemWithDataInternal(-1, TListItemType::Header, null);
0 ignored issues
show
Bug introduced by
Prado\Web\UI\WebControls\TListItemType::Header of type string is incompatible with the type Prado\Web\UI\WebControls\TListItemType expected by parameter $itemType of Prado\Web\UI\WebControls...eItemWithDataInternal(). ( Ignorable by Annotation )

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

1445
				$this->_header = $this->createItemWithDataInternal(-1, /** @scrutinizer ignore-type */ TListItemType::Header, null);
Loading history...
1446
			}
1447
			if ($hasSeparator && $itemIndex > 0) {
1448
				$this->createItemWithDataInternal($itemIndex - 1, TListItemType::Separator, null);
1449
			}
1450
			if ($itemIndex === $editIndex) {
1451
				$itemType = TListItemType::EditItem;
1452
			} elseif ($itemIndex === $selectedIndex) {
1453
				$itemType = TListItemType::SelectedItem;
1454
			} else {
1455
				$itemType = $itemIndex % 2 ? TListItemType::AlternatingItem : TListItemType::Item;
1456
			}
1457
			$items->add($this->createItemWithDataInternal($itemIndex, $itemType, $dataItem));
1458
			$itemIndex++;
1459
		}
1460
		if ($itemIndex > 0) {
1461
			$this->_footer = $this->createItemWithDataInternal(-1, TListItemType::Footer, null);
1462
		} else {
1463
			$this->createEmptyContent();
1464
			$this->dataBindChildren();
1465
		}
1466
		$this->setViewState('ItemCount', $itemIndex, 0);
1467
	}
1468
1469
	/**
1470
	 * Renders the data list control.
1471
	 * This method overrides the parent implementation.
1472
	 * @param \Prado\Web\UI\THtmlWriter $writer writer for rendering purpose.
1473
	 */
1474
	public function render($writer)
1475
	{
1476
		if ($this->getHasControls()) {
1477
			if ($this->getItemCount() > 0) {
1478
				$this->applyItemStyles();
1479
				$repeatInfo = $this->getRepeatInfo();
1480
				$repeatInfo->renderRepeater($writer, $this);
1481
			} elseif ($this->_emptyTemplate !== null || $this->getEmptyRenderer() !== '') {
1482
				parent::render($writer);
1483
			}
1484
		}
1485
	}
1486
}
1487