GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

TbGroupGridView::getDataCellContent()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
ccs 0
cts 14
cp 0
rs 9.2
cc 4
eloc 10
nc 6
nop 3
crap 20
1
<?php
2
/**
3
 *## TbGroupGridView class file
4
 *
5
 * @author         Vitaliy Potapov <[email protected]>
6
 * @version        1.1
7
 * @see            http://groupgridview.demopage.ru/
8
 *
9
 * @since          24/09/2012 added to yiibooster library
10
 * @author         antonio ramirez <[email protected]>
11
 */
12
13
Yii::import('booster.widgets.TbGridView');
14
15
/**
16
 *## TbGroupGridView widget
17
 *
18
 * A Grid View that groups rows by any column(s)
19
 *
20
 * @property TbDataColumn[] $columns
21
 *
22
 * @package booster.widgets.grids
23
 */
24
class TbGroupGridView extends TbGridView
25
{
26
27
	const MERGE_SIMPLE = 'simple';
28
	const MERGE_NESTED = 'nested';
29
	const MERGE_FIRSTROW = 'firstrow';
30
31
	/**
32
	 * @var array $mergeColumns the columns to merge on the grid
33
	 */
34
	public $mergeColumns = array();
35
36
	/**
37
	 * @var string $mergeType the merge type. Defaults to MERGE_SIMPLE
38
	 */
39
	public $mergeType = self::MERGE_SIMPLE;
40
41
	/**
42
	 * @var string $mergeCellsCss the styles to apply to merged cells
43
	 */
44
	public $mergeCellCss = 'text-align: center; vertical-align: middle';
45
46
	/**
47
	 * @var array $extraRowColumns the group column names
48
	 */
49
	public $extraRowColumns = array();
50
51
	/**
52
	 * @var string $extraRowExpression
53
	 */
54
	public $extraRowExpression;
55
56
	/**
57
	 * @var array the HTML options for the extrarow cell tag.
58
	 */
59
	public $extraRowHtmlOptions = array();
60
61
	/**
62
	 * @var string $extraRowCssClass the class to be used to be set on the extrarow cell tag.
63
	 */
64
	public $extraRowCssClass = 'extrarow';
65
66
	/**
67
	 * @var array the column data changes
68
	 */
69
	private $_changes;
70
71
	/**
72
	 * Widget initialization
73
	 */
74
	public function init()
75
	{
76
		parent::init();
77
78
		/**
79
		 * check whether we have extraRowColumns set, forbid filters
80
		 */
81
		if (!empty($this->extraRowColumns)) {
82
			foreach ($this->columns as $column) {
83
				if ($column instanceof CDataColumn && in_array($column->name, $this->extraRowColumns)) {
0 ignored issues
show
Bug introduced by
The class CDataColumn does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
84
					$column->filterHtmlOptions = array('style' => 'display:none');
85
					$column->filter = false;
86
				}
87
			}
88
		}
89
		/**
90
		 * setup extra row options
91
		 */
92
		if (isset($this->extraRowHtmlOptions['class']) && !empty($this->extraRowCssClass)) {
93
			$this->extraRowHtmlOptions['class'] .= ' ' . $this->extraRowCssClass;
94
		} else {
95
			$this->extraRowHtmlOptions['class'] = $this->extraRowCssClass;
96
		}
97
	}
98
	
99
	/**
100
	 * Registers necessary client scripts.
101
	 */
102
	public function registerClientScript()
103
	{
104
		$id=$this->getId();
105
	
106
		if($this->ajaxUpdate===false)
107
			$ajaxUpdate=false;
108
		else
109
			$ajaxUpdate=array_unique(preg_split('/\s*,\s*/',$this->ajaxUpdate.','.$id,-1,PREG_SPLIT_NO_EMPTY));
110
		$options=array(
111
				'ajaxUpdate'=>$ajaxUpdate,
112
				'ajaxVar'=>$this->ajaxVar,
113
				'pagerClass'=>$this->pagerCssClass,
114
				'loadingClass'=>$this->loadingCssClass,
115
				'filterClass'=>$this->filterCssClass,
116
				'tableClass'=>$this->itemsCssClass,
117
				'selectableRows'=>$this->selectableRows,
118
				'enableHistory'=>$this->enableHistory,
119
				'updateSelector'=>$this->updateSelector,
120
				'filterSelector'=>$this->filterSelector
121
		);
122
		if($this->ajaxUrl!==null)
123
			$options['url']=CHtml::normalizeUrl($this->ajaxUrl);
124
		if($this->ajaxType!==null)
125
			$options['ajaxType']=strtoupper($this->ajaxType);
126
		if($this->enablePagination)
127
			$options['pageVar']=$this->dataProvider->getPagination()->pageVar;
128
		foreach(array('beforeAjaxUpdate', 'afterAjaxUpdate', 'ajaxUpdateError', 'selectionChanged') as $event)
129
		{
130
			if($this->$event!==null)
131
			{
132
				if($this->$event instanceof CJavaScriptExpression)
0 ignored issues
show
Bug introduced by
The class CJavaScriptExpression does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
133
					$options[$event]=$this->$event;
134
				else
135
					$options[$event]=new CJavaScriptExpression($this->$event);
136
			}
137
		}
138
	
139
		$options=CJavaScript::encode($options);
140
		$cs=Yii::app()->getClientScript();
141
		$cs->registerCoreScript('jquery');
142
		$cs->registerCoreScript('bbq');
143
		if($this->enableHistory)
144
			$cs->registerCoreScript('history');
145
		$cs->registerPackage('group-grid-view');
146
		$cs->registerScript(__CLASS__.'#'.$id,"jQuery('#$id').yiiGroupGridView($options);");
147
	}
148
149
	/**
150
	 * Renders the table body.
151
	 */
152
	public function renderTableBody()
153
	{
154
		if (!empty($this->mergeColumns) || !empty($this->extraRowColumns)) {
155
			$this->groupByColumns();
156
		}
157
158
		parent::renderTableBody();
159
	}
160
161
	/**
162
	 * find and store changing of group columns
163
	 */
164
	public function groupByColumns()
165
	{
166
		$data = $this->dataProvider->getData();
167
		if (count($data) == 0) {
168
			return;
169
		}
170
171
		if (!is_array($this->mergeColumns)) {
172
			$this->mergeColumns = array($this->mergeColumns);
173
		}
174
		if (!is_array($this->extraRowColumns)) {
175
			$this->extraRowColumns = array($this->extraRowColumns);
176
		}
177
178
		//store columns for group. Set object for existing columns in grid and string for attributes
179
		$groupColumns = array_unique(array_merge($this->mergeColumns, $this->extraRowColumns));
180
		foreach ($groupColumns as $key => $colName) {
181
			foreach ($this->columns as $column) {
182
				if (property_exists($column, 'name') && $column->name == $colName) {
183
					$groupColumns[$key] = $column;
184
					break;
185
				}
186
			}
187
		}
188
189
190
		//values for first row
191
		$lastStored = $this->getRowValues($groupColumns, $data[0], 0);
192
		foreach ($lastStored as $colName => $value) {
193
			$lastStored[$colName] = array(
194
				'value' => $value,
195
				'count' => 1,
196
				'index' => 0,
197
			);
198
		}
199
200
		//iterate data
201
		$rowcount = count($data);
202
		for ($i = 1; $i < $rowcount; $i++) {
203
			//save row values in array
204
			$current = $this->getRowValues($groupColumns, $data[$i], $i);
205
206
			//define is change occured. Need this extra foreach for correctly proceed extraRows
207
			$changedColumns = array();
208
			foreach ($current as $colName => $curValue) {
209
				if ($curValue != $lastStored[$colName]['value']) {
210
					$changedColumns[] = $colName;
211
				}
212
			}
213
214
			/**
215
			 * if this flag = true -> we will write change (to $this->_changes) for all grouping columns.
216
			 * It's required when change of any column from extraRowColumns occurs
217
			 */
218
			$saveChangeForAllColumns = (count(array_intersect($changedColumns, $this->extraRowColumns)) > 0);
219
220
			/**
221
			 * this changeOccurred related to foreach below. It is required only for mergeType == self::MERGE_NESTED,
222
			 * to write change for all nested columns when change of previous column occurred
223
			 */
224
			$changeOccurred = false;
225
			foreach ($current as $colName => $curValue) {
226
				//value changed
227
				$valueChanged = ($curValue != $lastStored[$colName]['value']);
228
				//change already occured in this loop and mergeType set to MERGETYPE_NESTED
229
				$saveChange = $valueChanged || ($changeOccurred && $this->mergeType == self::MERGE_NESTED);
230
231
				if ($saveChangeForAllColumns || $saveChange) {
232
					$changeOccurred = true;
233
234
					//store in class var
235
					$prevIndex = $lastStored[$colName]['index'];
236
					$this->_changes[$prevIndex]['columns'][$colName] = $lastStored[$colName];
237
					if (!isset($this->_changes[$prevIndex]['count'])) {
238
						$this->_changes[$prevIndex]['count'] = $lastStored[$colName]['count'];
239
					}
240
241
					//update lastStored for particular column
242
					$lastStored[$colName] = array(
243
						'value' => $curValue,
244
						'count' => 1,
245
						'index' => $i,
246
					);
247
248
				} else {
249
					$lastStored[$colName]['count']++;
250
				}
251
			}
252
		}
253
254
		//storing for last row
255
		foreach ($lastStored as $colName => $v) {
256
			$prevIndex = $v['index'];
257
			$this->_changes[$prevIndex]['columns'][$colName] = $v;
258
259
			if (!isset($this->_changes[$prevIndex]['count'])) {
260
				$this->_changes[$prevIndex]['count'] = $v['count'];
261
			}
262
		}
263
	}
264
265
	/**
266
	 * Renders a table body row.
267
	 *
268
	 * @param int $row
269
	 */
270
	public function renderTableRow($row)
271
	{
272
		$change = false;
273
		if ($this->_changes && array_key_exists($row, $this->_changes)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_changes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
274
			$change = $this->_changes[$row];
275
			//if change in extracolumns --> put extra row
276
			$columnsInExtra = array_intersect(array_keys($change['columns']), $this->extraRowColumns);
277
			if (count($columnsInExtra) > 0) {
278
				$this->renderExtraRow($row, $change, $columnsInExtra);
279
			}
280
		}
281
282
		// original CGridView code
283
		$htmlOptions = array();
284
		if ($this->rowHtmlOptionsExpression !== null) {
285
			$data = $this->dataProvider->data[$row];
286
			$options = $this->evaluateExpression(
287
				$this->rowHtmlOptionsExpression,
288
				array('row' => $row, 'data' => $data)
289
			);
290
			if (is_array($options)) {
291
				$htmlOptions = $options;
292
			}
293
		}
294
295
		if ($this->rowCssClassExpression !== null) {
296
			$data = $this->dataProvider->data[$row];
297
			$class = $this->evaluateExpression($this->rowCssClassExpression, array('row' => $row, 'data' => $data));
298
		} elseif (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0) {
299
			$class = $this->rowCssClass[$row % $n];
300
		}
301
302
		if (!empty($class)) {
303
			if (isset($htmlOptions['class'])) {
304
				$htmlOptions['class'] .= ' ' . $class;
305
			} else {
306
				$htmlOptions['class'] = $class;
307
			}
308
		}
309
310
		echo CHtml::openTag('tr', $htmlOptions);
311
		if (!$this->_changes) { //standart CGridview's render
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_changes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
312
			foreach ($this->columns as $column) {
313
				$column->renderDataCell($row);
314
			}
315
		} else { //for grouping
316
			foreach ($this->columns as $column) {
317
				$isGroupColumn = property_exists($column, 'name') && in_array($column->name, $this->mergeColumns);
318
				if (!$isGroupColumn) {
319
					$column->renderDataCell($row);
320
					continue;
321
				}
322
323
				$isChangedColumn = $change && array_key_exists($column->name, $change['columns']);
324
325
				//for rowspan show only changes (with rowspan)
326
				switch ($this->mergeType) {
327
					case self::MERGE_SIMPLE:
328
					case self::MERGE_NESTED:
329
						if ($isChangedColumn) {
330
							$options = $column->htmlOptions;
331
							$column->htmlOptions['rowspan'] = $change['columns'][$column->name]['count'];
332
							$column->htmlOptions['class'] = 'merge';
333
							$style = isset($column->htmlOptions['style']) ? $column->htmlOptions['style'] : '';
334
							$column->htmlOptions['style'] = $style . ';' . $this->mergeCellCss;
335
							$column->renderDataCell($row);
336
							$column->htmlOptions = $options;
337
						}
338
						break;
339
340
					case self::MERGE_FIRSTROW:
341
						if ($isChangedColumn) {
342
							$column->renderDataCell($row);
343
						} else {
344
							echo '<td></td>';
345
						}
346
						break;
347
				}
348
349
			}
350
		}
351
352
		echo "</tr>\n";
353
	}
354
355
	/**
356
	 * returns array of rendered column values (TD)
357
	 *
358
	 * @param string[]|TbDataColumn[] $columns
359
	 * @param CActiveRecord $data
360
	 * @param integer $rowIndex
361
	 *
362
	 * @throws CException
363
	 * @return mixed
364
	 */
365
	private function getRowValues($columns, $data, $rowIndex)
366
	{
367
		$result = array();
368
		foreach ($columns as $column) {
369
			if ($column instanceOf TbDataColumn) {
370
				$result[$column->name] = $this->getDataCellContent($column, $data, $rowIndex);
371
			} elseif (is_string($column)) {
372
				if (is_array($data) && array_key_exists($column, $data)) {
373
					$result[$column] = $data[$column];
374
				} elseif ($data instanceOf CActiveRecord && $data->hasAttribute($column)) {
375
					$result[$column] = $data->getAttribute($column);
376
				} else {
377
					throw new CException('Column or attribute "' . $column . '" not found!');
378
				}
379
			}
380
		}
381
		return $result;
382
	}
383
384
	/**
385
	 * renders extra row
386
	 *
387
	 * @param integer $beforeRow
388
	 * @param mixed $change
389
	 * @param array $columnsInExtra
390
	 */
391
	private function renderExtraRow($beforeRow, $change, $columnsInExtra)
392
	{
393
		$data = $this->dataProvider->data[$beforeRow];
394
		if ($this->extraRowExpression) { //user defined expression, use it!
395
			$content = $this->evaluateExpression(
396
				$this->extraRowExpression,
397
				array('data' => $data, 'row' => $beforeRow, 'values' => $change['columns'])
398
			);
399
		} else { //generate value
400
			$values = array();
401
			foreach ($columnsInExtra as $c) {
402
				$values[] = $change['columns'][$c]['value'];
403
			}
404
405
			$content = '<strong>' . implode(' :: ', $values) . '</strong>';
406
		}
407
408
		$colspan = count($this->columns);
409
410
		echo '<tr class="extrarow">';
411
		$this->extraRowHtmlOptions['colspan'] = $colspan;
412
		echo CHtml::openTag('td', $this->extraRowHtmlOptions);
413
		echo $content;
414
		echo CHtml::closeTag('td');
415
		echo '</tr>';
416
	}
417
418
	/**
419
	 * need to rewrite this function as it is protected in CDataColumn: it is strange as all methods inside are public
420
	 *
421
	 * @param TbDataColumn $column
422
	 * @param mixed $row
423
	 * @param mixed $data
424
	 *
425
	 * @return string
426
	 */
427
	private function getDataCellContent($column, $data, $row)
428
	{
429
		if ($column->value !== null) {
430
			$value = $column->evaluateExpression($column->value, array('data' => $data, 'row' => $row));
431
		} else if ($column->name !== null) {
432
			$value = CHtml::value($data, $column->name);
433
		}
434
435
		return !isset($value)
436
			? $column->grid->nullDisplay
437
			: $column->grid->getFormatter()->format(
438
				$value,
439
				$column->type
440
			);
441
	}
442
}
443