Test Failed
Branch master (206474)
by Fabio
18:24
created

TDataGrid::addParsedObject()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * TDataGrid related class files.
4
 * This file contains the definition of the following classes:
5
 * TDataGrid, TDataGridItem, TDataGridItemCollection, TDataGridColumnCollection,
6
 * TDataGridPagerStyle, TDataGridItemEventParameter,
7
 * TDataGridCommandEventParameter, TDataGridSortCommandEventParameter,
8
 * TDataGridPageChangedEventParameter
9
 *
10
 * @author Qiang Xue <[email protected]>
11
 * @link https://github.com/pradosoft/prado
12
 * @copyright Copyright &copy; 2005-2016 The PRADO Group
13
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
14
 * @package Prado\Web\UI\WebControls
15
 */
16
17
namespace Prado\Web\UI\WebControls;
18
19
use Prado\Collections\TList;
20
use Prado\Collections\TPagedDataSource;
21
use Prado\TPropertyValue;
22
use Prado\Exceptions\TInvalidDataTypeException;
23
use Prado\Web\UI\ITemplate;
24
25
/**
26
 * TDataGrid class
27
 *
28
 * TDataGrid represents a data bound and updatable grid control.
29
 *
30
 * To populate data into the datagrid, sets its {@link setDataSource DataSource}
31
 * to a tabular data source and call {@link dataBind()}.
32
 * Each row of data will be represented by an item in the {@link getItems Items}
33
 * collection of the datagrid.
34
 *
35
 * An item can be at one of three states: browsing, selected and edit.
36
 * The state determines how the item will be displayed. For example, if an item
37
 * is in edit state, it may be displayed as a table row with input text boxes
38
 * if the columns are of type {@link TBoundColumn}; and if in browsing state,
39
 * they are displayed as static text.
40
 *
41
 * To change the state of an item, set {@link setEditItemIndex EditItemIndex}
42
 * or {@link setSelectedItemIndex SelectedItemIndex} property.
43
 *
44
 * Each datagrid item has a {@link TDataGridItem::getItemType type}
45
 * which tells the position and state of the item in the datalist. An item in the header
46
 * of the repeater is of type Header. A body item may be of either
47
 * Item, AlternatingItem, SelectedItem or EditItem, depending whether the item
48
 * index is odd or even, whether it is being selected or edited.
49
 *
50
 * A datagrid is specified with a list of columns. Each column specifies how the corresponding
51
 * table column will be displayed. For example, the header/footer text of that column,
52
 * the cells in that column, and so on. The following column types are currently
53
 * provided by the framework,
54
 * - {@link TBoundColumn}, associated with a specific field in datasource and displays the corresponding data.
55
 * - {@link TEditCommandColumn}, displaying edit/update/cancel command buttons
56
 * - {@link TButtonColumn}, displaying generic command buttons that may be bound to specific field in datasource.
57
 * - {@link TDropDownListColumn}, displaying a dropdown list when the item is in edit state
58
 * - {@link THyperLinkColumn}, displaying a hyperlink that may be bound to specific field in datasource.
59
 * - {@link TCheckBoxColumn}, displaying a checkbox that may be bound to specific field in datasource.
60
 * - {@link TTemplateColumn}, displaying content based on templates.
61
 *
62
 * There are three ways to specify columns for a datagrid.
63
 * <ul>
64
 *  <li>Automatically generated based on data source.
65
 *  By setting {@link setAutoGenerateColumns AutoGenerateColumns} to true,
66
 *  a list of columns will be automatically generated based on the schema of the data source.
67
 *  Each column corresponds to a column of the data.</li>
68
 *  <li>Specified in template. For example,
69
 *    <code>
70
 *     <com:TDataGrid ...>
71
 *        <com:TBoundColumn .../>
72
 *        <com:TEditCommandColumn .../>
73
 *     </com:TDataGrid>
74
 *    </code>
75
 *  </li>
76
 *  <li>Manually created in code. Columns can be manipulated via
77
 *  the {@link setColumns Columns} property of the datagrid. For example,
78
 *  <code>
79
 *    $column=new TBoundColumn;
80
 *    $datagrid->Columns[]=$column;
81
 *  </code>
82
 *  </li>
83
 * </ul>
84
 * Note, automatically generated columns cannot be accessed via
85
 * the {@link getColumns Columns} property.
86
 *
87
 * TDataGrid supports sorting. If the {@link setAllowSorting AllowSorting}
88
 * is set to true, a column with nonempty {@link setSortExpression SortExpression}
89
 * will have its header text displayed as a clickable link button.
90
 * Clicking on the link button will raise {@link onSortCommand OnSortCommand}
91
 * event. You can respond to this event, sort the data source according
92
 * to the event parameter, and then invoke {@link databind()} on the datagrid
93
 * to show to end users the sorted data.
94
 *
95
 * TDataGrid supports paging. If the {@link setAllowPaging AllowPaging}
96
 * is set to true, a pager will be displayed on top and/or bottom of the table.
97
 * How the pager will be displayed is determined by the {@link getPagerStyle PagerStyle}
98
 * property. Clicking on a pager button will raise an {@link onPageIndexChanged OnPageIndexChanged}
99
 * event. You can respond to this event, specify the page to be displayed by
100
 * setting {@link setCurrentPageIndex CurrentPageIndex}</b> property,
101
 * and then invoke {@link databind()} on the datagrid to show to end users
102
 * a new page of data.
103
 *
104
 * TDataGrid supports two kinds of paging. The first one is based on the number of data items in
105
 * datasource. The number of pages {@link getPageCount PageCount} is calculated based
106
 * the item number and the {@link setPageSize PageSize} property.
107
 * The datagrid will manage which section of the data source to be displayed
108
 * based on the {@link setCurrentPageIndex CurrentPageIndex} property.
109
 * The second approach calculates the page number based on the
110
 * {@link setVirtualItemCount VirtualItemCount} property and
111
 * the {@link setPageSize PageSize} property. The datagrid will always
112
 * display from the beginning of the datasource up to the number of
113
 * {@link setPageSize PageSize} data items. This approach is especially
114
 * useful when the datasource may contain too many data items to be managed by
115
 * the datagrid efficiently.
116
 *
117
 * When the datagrid contains a button control that raises an {@link onCommand OnCommand}
118
 * event, the event will be bubbled up to the datagrid control.
119
 * If the event's command name is recognizable by the datagrid control,
120
 * a corresponding item event will be raised. The following item events will be
121
 * raised upon a specific command:
122
 * - OnEditCommand, if CommandName=edit
123
 * - OnCancelCommand, if CommandName=cancel
124
 * - OnSelectCommand, if CommandName=select
125
 * - OnDeleteCommand, if CommandName=delete
126
 * - OnUpdateCommand, if CommandName=update
127
 * - onPageIndexChanged, if CommandName=page
128
 * - OnSortCommand, if CommandName=sort
129
 * Note, an {@link onItemCommand OnItemCommand} event is raised in addition to
130
 * the above specific command events.
131
 *
132
 * TDataGrid also raises an {@link onItemCreated OnItemCreated} event for
133
 * every newly created datagrid item. You can respond to this event to customize
134
 * the content or style of the newly created item.
135
 *
136
 * Note, the data bound to the datagrid are reset to null after databinding.
137
 * There are several ways to access the data associated with a datagrid row:
138
 * - Access the data in {@link onItemDataBound OnItemDataBound} event
139
 * - Use {@link getDataKeys DataKeys} to obtain the data key associated with
140
 * the specified datagrid row and use the key to fetch the corresponding data
141
 * from some persistent storage such as DB.
142
 * - Save the data in viewstate and get it back during postbacks.
143
 *
144
 * @author Qiang Xue <[email protected]>
145
 * @package Prado\Web\UI\WebControls
146
 * @since 3.0
147
 */
148
class TDataGrid extends TBaseDataList implements \Prado\Web\UI\INamingContainer
149
{
150
	/**
151
	 * Command name that TDataGrid understands.
152
	 */
153
	const CMD_SELECT = 'Select';
154
	const CMD_EDIT = 'Edit';
155
	const CMD_UPDATE = 'Update';
156
	const CMD_DELETE = 'Delete';
157
	const CMD_CANCEL = 'Cancel';
158
	const CMD_SORT = 'Sort';
159
	const CMD_PAGE = 'Page';
160
	const CMD_PAGE_NEXT = 'Next';
161
	const CMD_PAGE_PREV = 'Previous';
162
	const CMD_PAGE_FIRST = 'First';
163
	const CMD_PAGE_LAST = 'Last';
164
165
	/**
166
	 * @var TDataGridColumnCollection manually created column collection
167
	 */
168
	private $_columns;
169
	/**
170
	 * @var TDataGridColumnCollection automatically created column collection
171
	 */
172
	private $_autoColumns;
173
	/**
174
	 * @var TList all columns including both manually and automatically created columns
175
	 */
176
	private $_allColumns;
177
	/**
178
	 * @var TDataGridItemCollection datagrid item collection
179
	 */
180
	private $_items;
181
	/**
182
	 * @var TDataGridItem header item
183
	 */
184
	private $_header;
185
	/**
186
	 * @var TDataGridItem footer item
187
	 */
188
	private $_footer;
189
	/**
190
	 * @var TPagedDataSource paged data source object
191
	 */
192
	private $_pagedDataSource;
0 ignored issues
show
Unused Code introduced by
The property $_pagedDataSource is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
193
	private $_topPager;
194
	private $_bottomPager;
195
	/**
196
	 * @var ITemplate template used when empty data is bounded
197
	 */
198
	private $_emptyTemplate;
199
	/**
200
	 * @var bool whether empty template is effective
201
	 */
202
	private $_useEmptyTemplate = false;
203
204
	/**
205
	 * @return string tag name (table) of the datagrid
206
	 */
207
	protected function getTagName()
208
	{
209
		return 'table';
210
	}
211
212
	/**
213
	 * @return string Name of the class used in AutoGenerateColumns mode
214
	 */
215
	protected function getAutoGenerateColumnName()
216
	{
217
		return 'TBoundColumn';
218
	}
219
220
	/**
221
	 * Adds objects parsed in template to datagrid.
222
	 * Datagrid columns are added into {@link getColumns Columns} collection.
223
	 * @param mixed $object object parsed in template
224
	 */
225
	public function addParsedObject($object)
226
	{
227
		if ($object instanceof TDataGridColumn) {
228
			$this->getColumns()->add($object);
229
		} else {
230
			parent::addParsedObject($object);
231
		}  // this is needed by EmptyTemplate
232
	}
233
234
	/**
235
	 * @return TDataGridColumnCollection manually specified datagrid columns
236
	 */
237
	public function getColumns()
238
	{
239
		if (!$this->_columns) {
240
			$this->_columns = new TDataGridColumnCollection($this);
241
		}
242
		return $this->_columns;
243
	}
244
245
	/**
246
	 * @return TDataGridColumnCollection automatically generated datagrid columns
247
	 */
248
	public function getAutoColumns()
249
	{
250
		if (!$this->_autoColumns) {
251
			$this->_autoColumns = new TDataGridColumnCollection($this);
252
		}
253
		return $this->_autoColumns;
254
	}
255
256
	/**
257
	 * @return TDataGridItemCollection datagrid item collection
258
	 */
259
	public function getItems()
260
	{
261
		if (!$this->_items) {
262
			$this->_items = new TDataGridItemCollection;
263
		}
264
		return $this->_items;
265
	}
266
267
	/**
268
	 * @return int number of items
269
	 */
270
	public function getItemCount()
271
	{
272
		return $this->_items ? $this->_items->getCount() : 0;
273
	}
274
275
	/**
276
	 * Creates a style object for the control.
277
	 * This method creates a {@link TTableStyle} to be used by datagrid.
278
	 * @return TTableStyle control style to be used
279
	 */
280
	protected function createStyle()
281
	{
282
		return new TTableStyle;
283
	}
284
285
	/**
286
	 * @return string the URL of the background image for the datagrid
287
	 */
288
	public function getBackImageUrl()
289
	{
290
		return $this->getStyle()->getBackImageUrl();
291
	}
292
293
	/**
294
	 * @param string $value the URL of the background image for the datagrid
295
	 */
296
	public function setBackImageUrl($value)
297
	{
298
		$this->getStyle()->setBackImageUrl($value);
299
	}
300
301
	/**
302
	 * @return TTableItemStyle the style for every item
303
	 */
304 View Code Duplication
	public function getItemStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
305
	{
306
		if (($style = $this->getViewState('ItemStyle', null)) === null) {
307
			$style = new TTableItemStyle;
308
			$this->setViewState('ItemStyle', $style, null);
309
		}
310
		return $style;
311
	}
312
313
	/**
314
	 * @return TTableItemStyle the style for each alternating item
315
	 */
316 View Code Duplication
	public function getAlternatingItemStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
317
	{
318
		if (($style = $this->getViewState('AlternatingItemStyle', null)) === null) {
319
			$style = new TTableItemStyle;
320
			$this->setViewState('AlternatingItemStyle', $style, null);
321
		}
322
		return $style;
323
	}
324
325
	/**
326
	 * @return TTableItemStyle the style for selected item
327
	 */
328 View Code Duplication
	public function getSelectedItemStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
329
	{
330
		if (($style = $this->getViewState('SelectedItemStyle', null)) === null) {
331
			$style = new TTableItemStyle;
332
			$this->setViewState('SelectedItemStyle', $style, null);
333
		}
334
		return $style;
335
	}
336
337
	/**
338
	 * @return TTableItemStyle the style for edit item
339
	 */
340 View Code Duplication
	public function getEditItemStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
341
	{
342
		if (($style = $this->getViewState('EditItemStyle', null)) === null) {
343
			$style = new TTableItemStyle;
344
			$this->setViewState('EditItemStyle', $style, null);
345
		}
346
		return $style;
347
	}
348
349
	/**
350
	 * @return TTableItemStyle the style for header
351
	 */
352 View Code Duplication
	public function getHeaderStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
353
	{
354
		if (($style = $this->getViewState('HeaderStyle', null)) === null) {
355
			$style = new TTableItemStyle;
356
			$this->setViewState('HeaderStyle', $style, null);
357
		}
358
		return $style;
359
	}
360
361
	/**
362
	 * @return TTableItemStyle the style for footer
363
	 */
364 View Code Duplication
	public function getFooterStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
365
	{
366
		if (($style = $this->getViewState('FooterStyle', null)) === null) {
367
			$style = new TTableItemStyle;
368
			$this->setViewState('FooterStyle', $style, null);
369
		}
370
		return $style;
371
	}
372
373
	/**
374
	 * @return TDataGridPagerStyle the style for pager
375
	 */
376 View Code Duplication
	public function getPagerStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
377
	{
378
		if (($style = $this->getViewState('PagerStyle', null)) === null) {
379
			$style = new TDataGridPagerStyle;
380
			$this->setViewState('PagerStyle', $style, null);
381
		}
382
		return $style;
383
	}
384
385
	/**
386
	 * @return TStyle the style for thead element, if any
387
	 * @since 3.1.1
388
	 */
389 View Code Duplication
	public function getTableHeadStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
390
	{
391
		if (($style = $this->getViewState('TableHeadStyle', null)) === null) {
392
			$style = new TStyle;
393
			$this->setViewState('TableHeadStyle', $style, null);
394
		}
395
		return $style;
396
	}
397
398
	/**
399
	 * @return TStyle the style for tbody element, if any
400
	 * @since 3.1.1
401
	 */
402 View Code Duplication
	public function getTableBodyStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
403
	{
404
		if (($style = $this->getViewState('TableBodyStyle', null)) === null) {
405
			$style = new TStyle;
406
			$this->setViewState('TableBodyStyle', $style, null);
407
		}
408
		return $style;
409
	}
410
411
	/**
412
	 * @return TStyle the style for tfoot element, if any
413
	 * @since 3.1.1
414
	 */
415 View Code Duplication
	public function getTableFootStyle()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
416
	{
417
		if (($style = $this->getViewState('TableFootStyle', null)) === null) {
418
			$style = new TStyle;
419
			$this->setViewState('TableFootStyle', $style, null);
420
		}
421
		return $style;
422
	}
423
424
	/**
425
	 * @return string caption for the datagrid
426
	 */
427
	public function getCaption()
428
	{
429
		return $this->getViewState('Caption', '');
430
	}
431
432
	/**
433
	 * @param string $value caption for the datagrid
434
	 */
435
	public function setCaption($value)
436
	{
437
		$this->setViewState('Caption', $value, '');
438
	}
439
440
	/**
441
	 * @return TTableCaptionAlign datagrid caption alignment. Defaults to TTableCaptionAlign::NotSet.
442
	 */
443
	public function getCaptionAlign()
444
	{
445
		return $this->getViewState('CaptionAlign', TTableCaptionAlign::NotSet);
446
	}
447
448
	/**
449
	 * @param TTableCaptionAlign $value datagrid caption alignment. Valid values include
450
	 */
451
	public function setCaptionAlign($value)
452
	{
453
		$this->setViewState('CaptionAlign', TPropertyValue::ensureEnum($value, 'TPrado\\Web\\UI\\WebControls\\TableCaptionAlign'), TTableCaptionAlign::NotSet);
454
	}
455
456
	/**
457
	 * @return TDataGridItem the header item
458
	 */
459
	public function getHeader()
460
	{
461
		return $this->_header;
462
	}
463
464
	/**
465
	 * @return TDataGridItem the footer item
466
	 */
467
	public function getFooter()
468
	{
469
		return $this->_footer;
470
	}
471
472
	/**
473
	 * @return TDataGridPager the pager displayed at the top of datagrid. It could be null if paging is disabled.
474
	 */
475
	public function getTopPager()
476
	{
477
		return $this->_topPager;
478
	}
479
480
	/**
481
	 * @return TDataGridPager the pager displayed at the bottom of datagrid. It could be null if paging is disabled.
482
	 */
483
	public function getBottomPager()
484
	{
485
		return $this->_bottomPager;
486
	}
487
488
	/**
489
	 * @return TDataGridItem the selected item, null if no item is selected.
490
	 */
491 View Code Duplication
	public function getSelectedItem()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
492
	{
493
		$index = $this->getSelectedItemIndex();
494
		$items = $this->getItems();
495
		if ($index >= 0 && $index < $items->getCount()) {
496
			return $items->itemAt($index);
497
		} else {
498
			return null;
499
		}
500
	}
501
502
	/**
503
	 * @return int the zero-based index of the selected item in {@link getItems Items}.
504
	 * A value -1 means no item selected.
505
	 */
506
	public function getSelectedItemIndex()
507
	{
508
		return $this->getViewState('SelectedItemIndex', -1);
509
	}
510
511
	/**
512
	 * Selects an item by its index in {@link getItems Items}.
513
	 * Previously selected item will be un-selected.
514
	 * If the item to be selected is already in edit mode, it will remain in edit mode.
515
	 * If the index is less than 0, any existing selection will be cleared up.
516
	 * @param int $value the selected item index
517
	 */
518
	public function setSelectedItemIndex($value)
519
	{
520
		if (($value = TPropertyValue::ensureInteger($value)) < 0) {
521
			$value = -1;
522
		}
523
		if (($current = $this->getSelectedItemIndex()) !== $value) {
524
			$this->setViewState('SelectedItemIndex', $value, -1);
525
			$items = $this->getItems();
526
			$itemCount = $items->getCount();
527 View Code Duplication
			if ($current >= 0 && $current < $itemCount) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
528
				$item = $items->itemAt($current);
529
				if ($item->getItemType() !== TListItemType::EditItem) {
530
					$item->setItemType($current % 2 ? TListItemType::AlternatingItem : TListItemType::Item);
531
				}
532
			}
533 View Code Duplication
			if ($value >= 0 && $value < $itemCount) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
534
				$item = $items->itemAt($value);
535
				if ($item->getItemType() !== TListItemType::EditItem) {
536
					$item->setItemType(TListItemType::SelectedItem);
537
				}
538
			}
539
		}
540
	}
541
542
	/**
543
	 * @return TDataGridItem the edit item
544
	 */
545 View Code Duplication
	public function getEditItem()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
546
	{
547
		$index = $this->getEditItemIndex();
548
		$items = $this->getItems();
549
		if ($index >= 0 && $index < $items->getCount()) {
550
			return $items->itemAt($index);
551
		} else {
552
			return null;
553
		}
554
	}
555
556
	/**
557
	 * @return int the zero-based index of the edit item in {@link getItems Items}.
558
	 * A value -1 means no item is in edit mode.
559
	 */
560
	public function getEditItemIndex()
561
	{
562
		return $this->getViewState('EditItemIndex', -1);
563
	}
564
565
	/**
566
	 * Edits an item by its index in {@link getItems Items}.
567
	 * Previously editting item will change to normal item state.
568
	 * If the index is less than 0, any existing edit item will be cleared up.
569
	 * @param int $value the edit item index
570
	 */
571 View Code Duplication
	public function setEditItemIndex($value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
572
	{
573
		if (($value = TPropertyValue::ensureInteger($value)) < 0) {
574
			$value = -1;
575
		}
576
		if (($current = $this->getEditItemIndex()) !== $value) {
577
			$this->setViewState('EditItemIndex', $value, -1);
578
			$items = $this->getItems();
579
			$itemCount = $items->getCount();
580
			if ($current >= 0 && $current < $itemCount) {
581
				$items->itemAt($current)->setItemType($current % 2 ? TListItemType::AlternatingItem : TListItemType::Item);
582
			}
583
			if ($value >= 0 && $value < $itemCount) {
584
				$items->itemAt($value)->setItemType(TListItemType::EditItem);
585
			}
586
		}
587
	}
588
589
	/**
590
	 * @return bool whether sorting is enabled. Defaults to false.
591
	 */
592
	public function getAllowSorting()
593
	{
594
		return $this->getViewState('AllowSorting', false);
595
	}
596
597
	/**
598
	 * @param bool $value whether sorting is enabled
599
	 */
600
	public function setAllowSorting($value)
601
	{
602
		$this->setViewState('AllowSorting', TPropertyValue::ensureBoolean($value), false);
603
	}
604
605
	/**
606
	 * @return bool whether datagrid columns should be automatically generated. Defaults to true.
607
	 */
608
	public function getAutoGenerateColumns()
609
	{
610
		return $this->getViewState('AutoGenerateColumns', true);
611
	}
612
613
	/**
614
	 * @param bool $value whether datagrid columns should be automatically generated
615
	 */
616
	public function setAutoGenerateColumns($value)
617
	{
618
		$this->setViewState('AutoGenerateColumns', TPropertyValue::ensureBoolean($value), true);
619
	}
620
621
	/**
622
	 * @return bool whether the header should be displayed. Defaults to true.
623
	 */
624
	public function getShowHeader()
625
	{
626
		return $this->getViewState('ShowHeader', true);
627
	}
628
629
	/**
630
	 * @param bool $value whether the header should be displayed
631
	 */
632
	public function setShowHeader($value)
633
	{
634
		$this->setViewState('ShowHeader', TPropertyValue::ensureBoolean($value), true);
635
	}
636
637
	/**
638
	 * @return bool whether the footer should be displayed. Defaults to false.
639
	 */
640
	public function getShowFooter()
641
	{
642
		return $this->getViewState('ShowFooter', false);
643
	}
644
645
	/**
646
	 * @param bool $value whether the footer should be displayed
647
	 */
648
	public function setShowFooter($value)
649
	{
650
		$this->setViewState('ShowFooter', TPropertyValue::ensureBoolean($value), false);
651
	}
652
653
	/**
654
	 * @return ITemplate the template applied when no data is bound to the datagrid
655
	 */
656
	public function getEmptyTemplate()
657
	{
658
		return $this->_emptyTemplate;
659
	}
660
661
	/**
662
	 * @param ITemplate $value the template applied when no data is bound to the datagrid
663
	 * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
664
	 */
665 View Code Duplication
	public function setEmptyTemplate($value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
666
	{
667
		if ($value instanceof ITemplate || $value === null) {
668
			$this->_emptyTemplate = $value;
669
		} else {
670
			throw new TInvalidDataTypeException('datagrid_template_required', 'EmptyTemplate');
671
		}
672
	}
673
674
	/**
675
	 * This method overrides parent's implementation to handle
676
	 * {@link onItemCommand OnItemCommand} event which is bubbled from
677
	 * {@link TDataGridItem} child controls.
678
	 * If the event parameter is {@link TDataGridCommandEventParameter} and
679
	 * the command name is a recognized one, which includes 'select', 'edit',
680
	 * 'delete', 'update', and 'cancel' (case-insensitive), then a
681
	 * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}).
682
	 * This method should only be used by control developers.
683
	 * @param TControl $sender the sender of the event
684
	 * @param TEventParameter $param event parameter
685
	 * @return bool whether the event bubbling should stop here.
686
	 */
687
	public function bubbleEvent($sender, $param)
688
	{
689
		if ($param instanceof TDataGridCommandEventParameter) {
690
			$this->onItemCommand($param);
691
			$command = $param->getCommandName();
692
			if (strcasecmp($command, self::CMD_SELECT) === 0) {
693
				$this->setSelectedItemIndex($param->getItem()->getItemIndex());
694
				$this->onSelectedIndexChanged($param);
695
				return true;
696
			} elseif (strcasecmp($command, self::CMD_EDIT) === 0) {
697
				$this->onEditCommand($param);
698
				return true;
699
			} elseif (strcasecmp($command, self::CMD_DELETE) === 0) {
700
				$this->onDeleteCommand($param);
701
				return true;
702
			} elseif (strcasecmp($command, self::CMD_UPDATE) === 0) {
703
				$this->onUpdateCommand($param);
704
				return true;
705
			} elseif (strcasecmp($command, self::CMD_CANCEL) === 0) {
706
				$this->onCancelCommand($param);
707
				return true;
708
			} elseif (strcasecmp($command, self::CMD_SORT) === 0) {
709
				$this->onSortCommand(new TDataGridSortCommandEventParameter($sender, $param));
710
				return true;
711
			} elseif (strcasecmp($command, self::CMD_PAGE) === 0) {
712
				$p = $param->getCommandParameter();
713
				if (strcasecmp($p, self::CMD_PAGE_NEXT) === 0) {
714
					$pageIndex = $this->getCurrentPageIndex() + 1;
715
				} elseif (strcasecmp($p, self::CMD_PAGE_PREV) === 0) {
716
					$pageIndex = $this->getCurrentPageIndex() - 1;
717
				} elseif (strcasecmp($p, self::CMD_PAGE_FIRST) === 0) {
718
					$pageIndex = 0;
719
				} elseif (strcasecmp($p, self::CMD_PAGE_LAST) === 0) {
720
					$pageIndex = $this->getPageCount() - 1;
721
				} else {
722
					$pageIndex = TPropertyValue::ensureInteger($p) - 1;
723
				}
724
				$this->onPageIndexChanged(new TDataGridPageChangedEventParameter($sender, $pageIndex));
725
				return true;
726
			}
727
		}
728
		return false;
729
	}
730
731
	/**
732
	 * Raises <b>OnCancelCommand</b> event.
733
	 * This method is invoked when a button control raises <b>OnCommand</b> event
734
	 * with <b>cancel</b> command name.
735
	 * @param TDataGridCommandEventParameter $param event parameter
736
	 */
737
	public function onCancelCommand($param)
738
	{
739
		$this->raiseEvent('OnCancelCommand', $this, $param);
740
	}
741
742
	/**
743
	 * Raises <b>OnDeleteCommand</b> event.
744
	 * This method is invoked when a button control raises <b>OnCommand</b> event
745
	 * with <b>delete</b> command name.
746
	 * @param TDataGridCommandEventParameter $param event parameter
747
	 */
748
	public function onDeleteCommand($param)
749
	{
750
		$this->raiseEvent('OnDeleteCommand', $this, $param);
751
	}
752
753
	/**
754
	 * Raises <b>OnEditCommand</b> event.
755
	 * This method is invoked when a button control raises <b>OnCommand</b> event
756
	 * with <b>edit</b> command name.
757
	 * @param TDataGridCommandEventParameter $param event parameter
758
	 */
759
	public function onEditCommand($param)
760
	{
761
		$this->raiseEvent('OnEditCommand', $this, $param);
762
	}
763
764
	/**
765
	 * Raises <b>OnItemCommand</b> event.
766
	 * This method is invoked when a button control raises <b>OnCommand</b> event.
767
	 * @param TDataGridCommandEventParameter $param event parameter
768
	 */
769
	public function onItemCommand($param)
770
	{
771
		$this->raiseEvent('OnItemCommand', $this, $param);
772
	}
773
774
	/**
775
	 * Raises <b>OnSortCommand</b> event.
776
	 * This method is invoked when a button control raises <b>OnCommand</b> event
777
	 * with <b>sort</b> command name.
778
	 * @param TDataGridSortCommandEventParameter $param event parameter
779
	 */
780
	public function onSortCommand($param)
781
	{
782
		$this->raiseEvent('OnSortCommand', $this, $param);
783
	}
784
785
	/**
786
	 * Raises <b>OnUpdateCommand</b> event.
787
	 * This method is invoked when a button control raises <b>OnCommand</b> event
788
	 * with <b>update</b> command name.
789
	 * @param TDataGridCommandEventParameter $param event parameter
790
	 */
791
	public function onUpdateCommand($param)
792
	{
793
		$this->raiseEvent('OnUpdateCommand', $this, $param);
794
	}
795
796
	/**
797
	 * Raises <b>OnItemCreated</b> event.
798
	 * This method is invoked right after a datagrid item is created and before
799
	 * added to page hierarchy.
800
	 * @param TDataGridItemEventParameter $param event parameter
801
	 */
802
	public function onItemCreated($param)
803
	{
804
		$this->raiseEvent('OnItemCreated', $this, $param);
805
	}
806
807
	/**
808
	 * Raises <b>OnPagerCreated</b> event.
809
	 * This method is invoked right after a datagrid pager is created and before
810
	 * added to page hierarchy.
811
	 * @param TDataGridPagerEventParameter $param event parameter
812
	 */
813
	public function onPagerCreated($param)
814
	{
815
		$this->raiseEvent('OnPagerCreated', $this, $param);
816
	}
817
818
	/**
819
	 * Raises <b>OnItemDataBound</b> event.
820
	 * This method is invoked for each datagrid item after it performs
821
	 * databinding.
822
	 * @param TDataGridItemEventParameter $param event parameter
823
	 */
824
	public function onItemDataBound($param)
825
	{
826
		$this->raiseEvent('OnItemDataBound', $this, $param);
827
	}
828
829
	/**
830
	 * Raises <b>OnPageIndexChanged</b> event.
831
	 * This method is invoked when current page is changed.
832
	 * @param TDataGridPageChangedEventParameter $param event parameter
833
	 */
834
	public function onPageIndexChanged($param)
835
	{
836
		$this->raiseEvent('OnPageIndexChanged', $this, $param);
837
	}
838
839
	/**
840
	 * Saves item count in viewstate.
841
	 * This method is invoked right before control state is to be saved.
842
	 */
843
	public function saveState()
844
	{
845
		parent::saveState();
846
		if (!$this->getEnableViewState(true)) {
847
			return;
848
		}
849
		if ($this->_items) {
850
			$this->setViewState('ItemCount', $this->_items->getCount(), 0);
851
		} else {
852
			$this->clearViewState('ItemCount');
853
		}
854 View Code Duplication
		if ($this->_autoColumns) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
855
			$state = [];
856
			foreach ($this->_autoColumns as $column) {
857
				$state[] = $column->saveState();
858
			}
859
			$this->setViewState('AutoColumns', $state, []);
860
		} else {
861
			$this->clearViewState('AutoColumns');
862
		}
863 View Code Duplication
		if ($this->_columns) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
864
			$state = [];
865
			foreach ($this->_columns as $column) {
866
				$state[] = $column->saveState();
867
			}
868
			$this->setViewState('Columns', $state, []);
869
		} else {
870
			$this->clearViewState('Columns');
871
		}
872
	}
873
874
	/**
875
	 * Loads item count information from viewstate.
876
	 * This method is invoked right after control state is loaded.
877
	 */
878
	public function loadState()
879
	{
880
		parent::loadState();
881
		if (!$this->getEnableViewState(true)) {
882
			return;
883
		}
884
		if (!$this->getIsDataBound()) {
885
			$state = $this->getViewState('AutoColumns', []);
886
			if (!empty($state)) {
887
				$this->_autoColumns = new TDataGridColumnCollection($this);
888
				foreach ($state as $st) {
889
					$column = new $this->AutoGenerateColumnName;
890
					$column->loadState($st);
891
					$this->_autoColumns->add($column);
892
				}
893
			} else {
894
				$this->_autoColumns = null;
895
			}
896
			$state = $this->getViewState('Columns', []);
897
			if ($this->_columns && $this->_columns->getCount() === count($state)) {
898
				$i = 0;
899
				foreach ($this->_columns as $column) {
900
					$column->loadState($state[$i]);
901
					$i++;
902
				}
903
			}
904
			$this->restoreGridFromViewState();
905
		}
906
	}
907
908
	/**
909
	 * Clears up all items in the datagrid.
910
	 */
911
	public function reset()
912
	{
913
		$this->getControls()->clear();
914
		$this->getItems()->clear();
915
		$this->_header = null;
916
		$this->_footer = null;
917
		$this->_topPager = null;
918
		$this->_bottomPager = null;
919
		$this->_useEmptyTemplate = false;
920
	}
921
922
	/**
923
	 * Restores datagrid content from viewstate.
924
	 */
925
	protected function restoreGridFromViewState()
926
	{
927
		$this->reset();
928
929
		$allowPaging = $this->getAllowPaging();
930
931
		$itemCount = $this->getViewState('ItemCount', 0);
932
		$dsIndex = $this->getViewState('DataSourceIndex', 0);
933
934
		$columns = new TList($this->getColumns());
935
		$columns->mergeWith($this->_autoColumns);
936
		$this->_allColumns = $columns;
937
938
		$items = $this->getItems();
939
940
		if ($columns->getCount()) {
941
			foreach ($columns as $column) {
942
				$column->initialize();
943
			}
944
			$selectedIndex = $this->getSelectedItemIndex();
945
			$editIndex = $this->getEditItemIndex();
946
			for ($index = 0;$index < $itemCount;++$index) {
947 View Code Duplication
				if ($index === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
948
					if ($allowPaging) {
949
						$this->_topPager = $this->createPager();
950
					}
951
					$this->_header = $this->createItemInternal(-1, -1, TListItemType::Header, false, null, $columns);
952
				}
953 View Code Duplication
				if ($index === $editIndex) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
954
					$itemType = TListItemType::EditItem;
955
				} elseif ($index === $selectedIndex) {
956
					$itemType = TListItemType::SelectedItem;
957
				} elseif ($index % 2) {
958
					$itemType = TListItemType::AlternatingItem;
959
				} else {
960
					$itemType = TListItemType::Item;
961
				}
962
				$items->add($this->createItemInternal($index, $dsIndex, $itemType, false, null, $columns));
963
				$dsIndex++;
964
			}
965 View Code Duplication
			if ($index > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
966
				$this->_footer = $this->createItemInternal(-1, -1, TListItemType::Footer, false, null, $columns);
967
				if ($allowPaging) {
968
					$this->_bottomPager = $this->createPager();
969
				}
970
			}
971
		}
972 View Code Duplication
		if (!$dsIndex && $this->_emptyTemplate !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
973
			$this->_useEmptyTemplate = true;
974
			$this->_emptyTemplate->instantiateIn($this);
975
		}
976
	}
977
978
	/**
979
	 * Performs databinding to populate datagrid items from data source.
980
	 * This method is invoked by {@link dataBind()}.
981
	 * You may override this function to provide your own way of data population.
982
	 * @param Traversable $data the bound data
983
	 */
984
	protected function performDataBinding($data)
985
	{
986
		$this->reset();
987
		$keys = $this->getDataKeys();
988
		$keys->clear();
989
		$keyField = $this->getDataKeyField();
990
991
		// get all columns
992
		if ($this->getAutoGenerateColumns()) {
993
			$columns = new TList($this->getColumns());
994
			$autoColumns = $this->createAutoColumns($data);
995
			$columns->mergeWith($autoColumns);
996
		} else {
997
			$columns = $this->getColumns();
998
		}
999
		$this->_allColumns = $columns;
1000
1001
		$items = $this->getItems();
1002
1003
		$index = 0;
1004
		$allowPaging = $this->getAllowPaging() && ($data instanceof TPagedDataSource);
1005
		$dsIndex = $allowPaging ? $data->getFirstIndexInPage() : 0;
1006
		$this->setViewState('DataSourceIndex', $dsIndex, 0);
1007
		if ($columns->getCount()) {
1008
			foreach ($columns as $column) {
1009
				$column->initialize();
1010
			}
1011
1012
			$selectedIndex = $this->getSelectedItemIndex();
1013
			$editIndex = $this->getEditItemIndex();
1014
			foreach ($data as $key => $row) {
1015
				if ($keyField !== '') {
1016
					$keys->add($this->getDataFieldValue($row, $keyField));
1017
				} else {
1018
					$keys->add($key);
1019
				}
1020 View Code Duplication
				if ($index === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1021
					if ($allowPaging) {
1022
						$this->_topPager = $this->createPager();
1023
					}
1024
					$this->_header = $this->createItemInternal(-1, -1, TListItemType::Header, true, null, $columns);
1025
				}
1026 View Code Duplication
				if ($index === $editIndex) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1027
					$itemType = TListItemType::EditItem;
1028
				} elseif ($index === $selectedIndex) {
1029
					$itemType = TListItemType::SelectedItem;
1030
				} elseif ($index % 2) {
1031
					$itemType = TListItemType::AlternatingItem;
1032
				} else {
1033
					$itemType = TListItemType::Item;
1034
				}
1035
				$items->add($this->createItemInternal($index, $dsIndex, $itemType, true, $row, $columns));
1036
				$index++;
1037
				$dsIndex++;
1038
			}
1039 View Code Duplication
			if ($index > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1040
				$this->_footer = $this->createItemInternal(-1, -1, TListItemType::Footer, true, null, $columns);
1041
				if ($allowPaging) {
1042
					$this->_bottomPager = $this->createPager();
1043
				}
1044
			}
1045
		}
1046
		$this->setViewState('ItemCount', $index, 0);
1047 View Code Duplication
		if (!$dsIndex && $this->_emptyTemplate !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1048
			$this->_useEmptyTemplate = true;
1049
			$this->_emptyTemplate->instantiateIn($this);
1050
			$this->dataBindChildren();
1051
		}
1052
	}
1053
1054
	/**
1055
	 * Merges consecutive cells who have the same text.
1056
	 * @since 3.1.1
1057
	 */
1058
	private function groupCells()
1059
	{
1060
		if (($columns = $this->_allColumns) === null) {
1061
			return;
1062
		}
1063
		$items = $this->getItems();
1064
		foreach ($columns as $id => $column) {
1065
			if (!$column->getEnableCellGrouping()) {
1066
				continue;
1067
			}
1068
			$prevCell = null;
1069
			$prevCellText = null;
1070
			foreach ($items as $item) {
1071
				$itemType = $item->getItemType();
1072
				$cell = $item->getCells()->itemAt($id);
1073
				if (!$cell->getVisible()) {
1074
					continue;
1075
				}
1076
				if ($itemType === TListItemType::Item || $itemType === TListItemType::AlternatingItem || $itemType === TListItemType::SelectedItem) {
1077
					if (($cellText = $this->getCellText($cell)) === '') {
1078
						$prevCell = null;
1079
						$prevCellText = null;
1080
						continue;
1081
					}
1082
					if ($prevCell === null || $prevCellText !== $cellText) {
1083
						$prevCell = $cell;
1084
						$prevCellText = $cellText;
1085
					} else {
1086
						if (($rowSpan = $prevCell->getRowSpan()) === 0) {
1087
							$rowSpan = 1;
1088
						}
1089
						$prevCell->setRowSpan($rowSpan + 1);
1090
						$cell->setVisible(false);
1091
					}
1092
				}
1093
			}
1094
		}
1095
	}
1096
1097
	private function getCellText($cell)
1098
	{
1099
		if (($data = $cell->getText()) === '' && $cell->getHasControls()) {
1100
			$controls = $cell->getControls();
1101
			foreach ($controls as $control) {
1102
				if ($control instanceof \Prado\IDataRenderer) {
1103
					return $control->getData();
1104
				}
1105
			}
1106
		}
1107
		return $data;
1108
	}
1109
1110
	/**
1111
	 * Creates a datagrid item instance based on the item type and index.
1112
	 * @param int $itemIndex zero-based item index
1113
	 * @param mixed $dataSourceIndex
1114
	 * @param TListItemType $itemType item type
1115
	 * @return TDataGridItem created data list item
1116
	 */
1117
	protected function createItem($itemIndex, $dataSourceIndex, $itemType)
1118
	{
1119
		return new TDataGridItem($itemIndex, $dataSourceIndex, $itemType);
1120
	}
1121
1122
	private function createItemInternal($itemIndex, $dataSourceIndex, $itemType, $dataBind, $dataItem, $columns)
1123
	{
1124
		$item = $this->createItem($itemIndex, $dataSourceIndex, $itemType);
1125
		$this->initializeItem($item, $columns);
1126
		$param = new TDataGridItemEventParameter($item);
1127
		if ($dataBind) {
1128
			$item->setData($dataItem);
1129
			$this->onItemCreated($param);
1130
			$this->getControls()->add($item);
1131
			$item->dataBind();
1132
			$this->onItemDataBound($param);
1133
		} else {
1134
			$this->onItemCreated($param);
1135
			$this->getControls()->add($item);
1136
		}
1137
		return $item;
1138
	}
1139
1140
	/**
1141
	 * Initializes a datagrid item and cells inside it
1142
	 * @param TDataGrid $item datagrid item to be initialized
1143
	 * @param TDataGridColumnCollection $columns datagrid columns to be used to initialize the cells in the item
1144
	 */
1145
	protected function initializeItem($item, $columns)
1146
	{
1147
		$cells = $item->getCells();
0 ignored issues
show
Bug introduced by
The method getCells() does not exist on Prado\Web\UI\WebControls\TDataGrid. Did you maybe mean getCellSpacing()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1148
		$itemType = $item->getItemType();
1149
		$index = 0;
1150
		foreach ($columns as $column) {
1151
			if ($itemType === TListItemType::Header) {
1152
				$cell = new TTableHeaderCell;
1153
			} else {
1154
				$cell = new TTableCell;
1155
			}
1156
			if (($id = $column->getID()) !== '') {
1157
				$item->registerObject($id, $cell);
1158
			}
1159
			$cells->add($cell);
1160
			$column->initializeCell($cell, $index, $itemType);
1161
			$index++;
1162
		}
1163
	}
1164
1165
	protected function createPager()
1166
	{
1167
		$pager = new TDataGridPager($this);
1168
		$this->buildPager($pager);
1169
		$this->onPagerCreated(new TDataGridPagerEventParameter($pager));
1170
		$this->getControls()->add($pager);
1171
		return $pager;
1172
	}
1173
1174
	/**
1175
	 * Builds the pager content based on pager style.
1176
	 * @param TDataGridPager $pager the container for the pager
1177
	 */
1178
	protected function buildPager($pager)
1179
	{
1180
		switch ($this->getPagerStyle()->getMode()) {
1181
			case TDataGridPagerMode::NextPrev:
1182
				$this->buildNextPrevPager($pager);
1183
				break;
1184
			case TDataGridPagerMode::Numeric:
1185
				$this->buildNumericPager($pager);
1186
				break;
1187
		}
1188
	}
1189
1190
	/**
1191
	 * Creates a pager button.
1192
	 * Depending on the button type, a TLinkButton or a TButton may be created.
1193
	 * If it is enabled (clickable), its command name and parameter will also be set.
1194
	 * Derived classes may override this method to create additional types of buttons, such as TImageButton.
1195
	 * @param mixed $pager the container pager instance of TActiveDatagridPager
1196
	 * @param string $buttonType button type, either LinkButton or PushButton
1197
	 * @param bool $enabled whether the button should be enabled
1198
	 * @param string $text caption of the button
1199
	 * @param string $commandName CommandName corresponding to the OnCommand event of the button
1200
	 * @param string $commandParameter CommandParameter corresponding to the OnCommand event of the button
1201
	 * @return mixed the button instance
1202
	 */
1203
	protected function createPagerButton($pager, $buttonType, $enabled, $text, $commandName, $commandParameter)
1204
	{
1205 View Code Duplication
		if ($buttonType === TDataGridPagerButtonType::LinkButton) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1206
			if ($enabled) {
1207
				$button = new TLinkButton;
1208
			} else {
1209
				$button = new TLabel;
1210
				$button->setText($text);
1211
				return $button;
1212
			}
1213
		} else {
1214
			$button = new TButton;
1215
			if (!$enabled) {
1216
				$button->setEnabled(false);
1217
			}
1218
		}
1219
		$button->setText($text);
1220
		$button->setCommandName($commandName);
1221
		$button->setCommandParameter($commandParameter);
1222
		$button->setCausesValidation(false);
1223
		return $button;
1224
	}
1225
1226
	/**
1227
	 * Builds a next-prev pager
1228
	 * @param TDataGridPager $pager the container for the pager
1229
	 */
1230
	protected function buildNextPrevPager($pager)
1231
	{
1232
		$style = $this->getPagerStyle();
1233
		$buttonType = $style->getButtonType();
1234
		$controls = $pager->getControls();
1235
		$currentPageIndex = $this->getCurrentPageIndex();
1236
		if ($currentPageIndex === 0) {
1237
			if (($text = $style->getFirstPageText()) !== '') {
1238
				$label = $this->createPagerButton($pager, $buttonType, false, $text, '', '');
1239
				$controls->add($label);
1240
				$controls->add("\n");
1241
			}
1242
1243
			$label = $this->createPagerButton($pager, $buttonType, false, $style->getPrevPageText(), '', '');
1244
			$controls->add($label);
1245 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1246
			if (($text = $style->getFirstPageText()) !== '') {
1247
				$button = $this->createPagerButton($pager, $buttonType, true, $text, self::CMD_PAGE, self::CMD_PAGE_FIRST);
1248
				$controls->add($button);
1249
				$controls->add("\n");
1250
			}
1251
1252
			$button = $this->createPagerButton($pager, $buttonType, true, $style->getPrevPageText(), self::CMD_PAGE, self::CMD_PAGE_PREV);
1253
			$controls->add($button);
1254
		}
1255
		$controls->add("\n");
1256
		if ($currentPageIndex === $this->getPageCount() - 1) {
1257
			$label = $this->createPagerButton($pager, $buttonType, false, $style->getNextPageText(), '', '');
1258
			$controls->add($label);
1259
			if (($text = $style->getLastPageText()) !== '') {
1260
				$controls->add("\n");
1261
				$label = $this->createPagerButton($pager, $buttonType, false, $text, '', '');
1262
				$controls->add($label);
1263
			}
1264 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1265
			$button = $this->createPagerButton($pager, $buttonType, true, $style->getNextPageText(), self::CMD_PAGE, self::CMD_PAGE_NEXT);
1266
			$controls->add($button);
1267
			if (($text = $style->getLastPageText()) !== '') {
1268
				$controls->add("\n");
1269
				$button = $this->createPagerButton($pager, $buttonType, true, $text, self::CMD_PAGE, self::CMD_PAGE_LAST);
1270
				$controls->add($button);
1271
			}
1272
		}
1273
	}
1274
1275
	/**
1276
	 * Builds a numeric pager
1277
	 * @param TDataGridPager $pager the container for the pager
1278
	 */
1279
	protected function buildNumericPager($pager)
1280
	{
1281
		$style = $this->getPagerStyle();
1282
		$buttonType = $style->getButtonType();
1283
		$controls = $pager->getControls();
1284
		$pageCount = $this->getPageCount();
1285
		$pageIndex = $this->getCurrentPageIndex() + 1;
1286
		$maxButtonCount = $style->getPageButtonCount();
1287
		$buttonCount = $maxButtonCount > $pageCount ? $pageCount : $maxButtonCount;
1288
		$startPageIndex = 1;
1289
		$endPageIndex = $buttonCount;
1290 View Code Duplication
		if ($pageIndex > $endPageIndex) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1291
			$startPageIndex = ((int) (($pageIndex - 1) / $maxButtonCount)) * $maxButtonCount + 1;
1292
			if (($endPageIndex = $startPageIndex + $maxButtonCount - 1) > $pageCount) {
1293
				$endPageIndex = $pageCount;
1294
			}
1295
			if ($endPageIndex - $startPageIndex + 1 < $maxButtonCount) {
1296
				if (($startPageIndex = $endPageIndex - $maxButtonCount + 1) < 1) {
1297
					$startPageIndex = 1;
1298
				}
1299
			}
1300
		}
1301
1302 View Code Duplication
		if ($startPageIndex > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1303
			if (($text = $style->getFirstPageText()) !== '') {
1304
				$button = $this->createPagerButton($pager, $buttonType, true, $text, self::CMD_PAGE, self::CMD_PAGE_FIRST);
1305
				$controls->add($button);
1306
				$controls->add("\n");
1307
			}
1308
			$prevPageIndex = $startPageIndex - 1;
1309
			$button = $this->createPagerButton($pager, $buttonType, true, $style->getPrevPageText(), self::CMD_PAGE, "$prevPageIndex");
1310
			$controls->add($button);
1311
			$controls->add("\n");
1312
		}
1313
1314 View Code Duplication
		for ($i = $startPageIndex;$i <= $endPageIndex;++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1315
			if ($i === $pageIndex) {
1316
				$label = $this->createPagerButton($pager, $buttonType, false, "$i", '', '');
1317
				$controls->add($label);
1318
			} else {
1319
				$button = $this->createPagerButton($pager, $buttonType, true, "$i", self::CMD_PAGE, "$i");
1320
				$controls->add($button);
1321
			}
1322
			if ($i < $endPageIndex) {
1323
				$controls->add("\n");
1324
			}
1325
		}
1326
1327 View Code Duplication
		if ($pageCount > $endPageIndex) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1328
			$controls->add("\n");
1329
			$nextPageIndex = $endPageIndex + 1;
1330
			$button = $this->createPagerButton($pager, $buttonType, true, $style->getNextPageText(), self::CMD_PAGE, "$nextPageIndex");
1331
			$controls->add($button);
1332
			if (($text = $style->getLastPageText()) !== '') {
1333
				$controls->add("\n");
1334
				$button = $this->createPagerButton($pager, $buttonType, true, $text, self::CMD_PAGE, self::CMD_PAGE_LAST);
1335
				$controls->add($button);
1336
			}
1337
		}
1338
	}
1339
1340
	/**
1341
	 * Automatically generates datagrid columns based on datasource schema
1342
	 * @param Traversable $dataSource data source bound to the datagrid
1343
	 * @return TDataGridColumnCollection
1344
	 */
1345
	protected function createAutoColumns($dataSource)
1346
	{
1347
		if (!$dataSource) {
1348
			return null;
1349
		}
1350
		$autoColumns = $this->getAutoColumns();
1351
		$autoColumns->clear();
1352
		foreach ($dataSource as $row) {
1353
			foreach ($row as $key => $value) {
1354
				$column = new $this->AutoGenerateColumnName;
1355
				if (is_string($key)) {
1356
					$column->setHeaderText($key);
1357
					$column->setDataField($key);
1358
					$column->setSortExpression($key);
1359
					$autoColumns->add($column);
1360
				} else {
1361
					$column->setHeaderText(TListItemType::Item);
1362
					$column->setDataField($key);
1363
					$column->setSortExpression(TListItemType::Item);
1364
					$autoColumns->add($column);
1365
				}
1366
			}
1367
			break;
1368
		}
1369
		return $autoColumns;
1370
	}
1371
1372
	/**
1373
	 * Applies styles to items, header, footer and separators.
1374
	 * Item styles are applied in a hierarchical way. Style in higher hierarchy
1375
	 * will inherit from styles in lower hierarchy.
1376
	 * Starting from the lowest hierarchy, the item styles include
1377
	 * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle},
1378
	 * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}.
1379
	 * Therefore, if background color is set as red in {@link getItemStyle ItemStyle},
1380
	 * {@link getEditItemStyle EditItemStyle} will also have red background color
1381
	 * unless it is set to a different value explicitly.
1382
	 */
1383
	protected function applyItemStyles()
1384
	{
1385
		$itemStyle = $this->getViewState('ItemStyle', null);
1386
1387
		$alternatingItemStyle = $this->getViewState('AlternatingItemStyle', null);
1388 View Code Duplication
		if ($itemStyle !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1389
			if ($alternatingItemStyle === null) {
1390
				$alternatingItemStyle = $itemStyle;
1391
			} else {
1392
				$alternatingItemStyle->mergeWith($itemStyle);
1393
			}
1394
		}
1395
1396
		$selectedItemStyle = $this->getViewState('SelectedItemStyle', null);
1397
1398
		$editItemStyle = $this->getViewState('EditItemStyle', null);
1399 View Code Duplication
		if ($selectedItemStyle !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1400
			if ($editItemStyle === null) {
1401
				$editItemStyle = $selectedItemStyle;
1402
			} else {
1403
				$editItemStyle->mergeWith($selectedItemStyle);
1404
			}
1405
		}
1406
1407
		$headerStyle = $this->getViewState('HeaderStyle', null);
1408
		$footerStyle = $this->getViewState('FooterStyle', null);
1409
		$pagerStyle = $this->getViewState('PagerStyle', null);
1410
		$separatorStyle = $this->getViewState('SeparatorStyle', null);
1411
1412
		foreach ($this->getControls() as $index => $item) {
1413
			if (!($item instanceof TDataGridItem) && !($item instanceof TDataGridPager)) {
1414
				continue;
1415
			}
1416
			$itemType = $item->getItemType();
1417
			switch ($itemType) {
1418 View Code Duplication
				case TListItemType::Header:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1419
					if ($headerStyle) {
1420
						$item->getStyle()->mergeWith($headerStyle);
1421
					}
1422
					if (!$this->getShowHeader()) {
1423
						$item->setVisible(false);
1424
					}
1425
					break;
1426 View Code Duplication
				case TListItemType::Footer:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1427
					if ($footerStyle) {
1428
						$item->getStyle()->mergeWith($footerStyle);
1429
					}
1430
					if (!$this->getShowFooter()) {
1431
						$item->setVisible(false);
1432
					}
1433
					break;
1434
				case TListItemType::Separator:
1435
					if ($separatorStyle) {
1436
						$item->getStyle()->mergeWith($separatorStyle);
1437
					}
1438
					break;
1439
				case TListItemType::Item:
1440
					if ($itemStyle) {
1441
						$item->getStyle()->mergeWith($itemStyle);
1442
					}
1443
					break;
1444
				case TListItemType::AlternatingItem:
1445
					if ($alternatingItemStyle) {
1446
						$item->getStyle()->mergeWith($alternatingItemStyle);
1447
					}
1448
					break;
1449 View Code Duplication
				case TListItemType::SelectedItem:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1450
					if ($selectedItemStyle) {
1451
						$item->getStyle()->mergeWith($selectedItemStyle);
1452
					}
1453
					if ($index % 2 == 1) {
1454
						if ($itemStyle) {
1455
							$item->getStyle()->mergeWith($itemStyle);
1456
						}
1457
					} else {
1458
						if ($alternatingItemStyle) {
1459
							$item->getStyle()->mergeWith($alternatingItemStyle);
1460
						}
1461
					}
1462
					break;
1463 View Code Duplication
				case TListItemType::EditItem:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1464
					if ($editItemStyle) {
1465
						$item->getStyle()->mergeWith($editItemStyle);
1466
					}
1467
					if ($index % 2 == 1) {
1468
						if ($itemStyle) {
1469
							$item->getStyle()->mergeWith($itemStyle);
1470
						}
1471
					} else {
1472
						if ($alternatingItemStyle) {
1473
							$item->getStyle()->mergeWith($alternatingItemStyle);
1474
						}
1475
					}
1476
					break;
1477
				case TListItemType::Pager:
1478
					if ($pagerStyle) {
1479
						$item->getStyle()->mergeWith($pagerStyle);
1480
						if ($index === 0) {
1481
							if ($pagerStyle->getPosition() === TDataGridPagerPosition::Bottom || !$pagerStyle->getVisible()) {
1482
								$item->setVisible(false);
1483
							}
1484
						} else {
1485
							if ($pagerStyle->getPosition() === TDataGridPagerPosition::Top || !$pagerStyle->getVisible()) {
1486
								$item->setVisible(false);
1487
							}
1488
						}
1489
					}
1490
					break;
1491
				default:
1492
					break;
1493
			}
1494
			if ($this->_columns && $itemType !== TListItemType::Pager) {
1495
				$n = $this->_columns->getCount();
1496
				$cells = $item->getCells();
0 ignored issues
show
Bug introduced by
The method getCells does only exist in Prado\Web\UI\WebControls\TDataGridItem, but not in Prado\Web\UI\WebControls\TDataGridPager.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1497
				for ($i = 0;$i < $n;++$i) {
1498
					$cell = $cells->itemAt($i);
1499
					$column = $this->_columns->itemAt($i);
1500
					if (!$column->getVisible()) {
1501
						$cell->setVisible(false);
1502
					} else {
1503
						if ($itemType === TListItemType::Header) {
1504
							$style = $column->getHeaderStyle(false);
1505
						} elseif ($itemType === TListItemType::Footer) {
1506
							$style = $column->getFooterStyle(false);
1507
						} else {
1508
							$style = $column->getItemStyle(false);
1509
						}
1510
						if ($style !== null) {
1511
							$cell->getStyle()->mergeWith($style);
1512
						}
1513
					}
1514
				}
1515
			}
1516
		}
1517
	}
1518
1519
	/**
1520
	 * Renders the openning tag for the datagrid control which will render table caption if present.
1521
	 * @param THtmlWriter $writer the writer used for the rendering purpose
1522
	 */
1523 View Code Duplication
	public function renderBeginTag($writer)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1524
	{
1525
		parent::renderBeginTag($writer);
1526
		if (($caption = $this->getCaption()) !== '') {
1527
			if (($align = $this->getCaptionAlign()) !== TTableCaptionAlign::NotSet) {
1528
				$writer->addAttribute('align', strtolower($align));
1529
			}
1530
			$writer->renderBeginTag('caption');
1531
			$writer->write($caption);
1532
			$writer->renderEndTag();
1533
		}
1534
	}
1535
1536
	/**
1537
	 * Renders the datagrid.
1538
	 * @param THtmlWriter $writer writer for the rendering purpose
1539
	 */
1540
	public function render($writer)
1541
	{
1542
		if ($this->getHasControls()) {
1543
			$this->groupCells();
1544
			if ($this->_useEmptyTemplate) {
1545
				$control = new TWebControl;
1546
				$control->setID($this->getClientID());
1547
				$control->copyBaseAttributes($this);
1548
				if ($this->getHasStyle()) {
1549
					$control->getStyle()->copyFrom($this->getStyle());
1550
				}
1551
				$control->renderBeginTag($writer);
1552
				$this->renderContents($writer);
1553
				$control->renderEndTag($writer);
1554
			} elseif ($this->getViewState('ItemCount', 0) > 0) {
1555
				$this->applyItemStyles();
1556
				if ($this->_topPager) {
1557
					$this->_topPager->renderControl($writer);
1558
					$writer->writeLine();
1559
				}
1560
				$this->renderTable($writer);
1561
				if ($this->_bottomPager) {
1562
					$writer->writeLine();
1563
					$this->_bottomPager->renderControl($writer);
1564
				}
1565
			}
1566
		}
1567
	}
1568
1569
	/**
1570
	 * Renders the tabular data.
1571
	 * @param THtmlWriter $writer writer
1572
	 */
1573
	protected function renderTable($writer)
1574
	{
1575
		$this->renderBeginTag($writer);
1576 View Code Duplication
		if ($this->_header && $this->_header->getVisible()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1577
			$writer->writeLine();
1578
			if ($style = $this->getViewState('TableHeadStyle', null)) {
1579
				$style->addAttributesToRender($writer);
1580
			}
1581
			$writer->renderBeginTag('thead');
1582
			$this->_header->render($writer);
1583
			$writer->renderEndTag();
1584
		}
1585
		$writer->writeLine();
1586
		if ($style = $this->getViewState('TableBodyStyle', null)) {
1587
			$style->addAttributesToRender($writer);
1588
		}
1589
		$writer->renderBeginTag('tbody');
1590
		foreach ($this->getItems() as $item) {
1591
			$item->renderControl($writer);
1592
		}
1593
		$writer->renderEndTag();
1594
1595 View Code Duplication
		if ($this->_footer && $this->_footer->getVisible()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1596
			$writer->writeLine();
1597
			if ($style = $this->getViewState('TableFootStyle', null)) {
1598
				$style->addAttributesToRender($writer);
1599
			}
1600
			$writer->renderBeginTag('tfoot');
1601
			$this->_footer->render($writer);
1602
			$writer->renderEndTag();
1603
		}
1604
1605
		$writer->writeLine();
1606
		$this->renderEndTag($writer);
1607
	}
1608
}
1609