Complex classes like TbGroupGridView often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use TbGroupGridView, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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() |
||
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) |
||
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() |
||
160 | |||
161 | /** |
||
162 | * find and store changing of group columns |
||
163 | */ |
||
164 | public function groupByColumns() |
||
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)) { |
||
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 |
||
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) |
||
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) |
||
442 | } |
||
443 |
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 thecomposer.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
orrequire-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 you have not tested against this specific condition, such errors might go unnoticed.