Groupgridview And Total

Hello,

i use this extension :

http://www.yiiframework.com/extension/groupgridview/

and I try to modify this extension for add total with grouped data.

You can see at the attached image what I would like to do.

as you can see, it doesnt’s work, the grouped row is not at the good place.And the total is not right, too !




<?php


Yii::import('zii.widgets.grid.CGridView');


/**

 * A Grid View that groups rows by any column(s)

 *

 * @category       User Interface

 * @package        extensions

 * @author         Vitaliy Potapov <noginsk@rambler.ru>

 * @version        1.2

 * change nach : ligne extra row déplacée en bas du groupement, ajout text "Total"

 */

class GroupGridView extends CGridView {


    const MERGE_SIMPLE = 'simple';

    const MERGE_NESTED = 'nested';

    const MERGE_FIRSTROW = 'firstrow';


    public $mergeColumns = array();

    public $mergeType = self::MERGE_SIMPLE;

    public $mergeCellCss = 'text-align: center; vertical-align: middle';

    public $extraRowColumns = array();

    public $extraRowExpression;

    private $valueTot = 0;

    private $_changes;


    public function renderTableBody() {

        if (!empty($this->mergeColumns) || !empty($this->extraRowColumns)) {

            $this->groupByColumns();

        }

        parent::renderTableBody();

    }


    /**

     * find and store changing of group columns

     * 

     * @param mixed $data

     */

    public function groupByColumns() {

        $data = $this->dataProvider->getData();

       

       

      

        if (count($data) == 0)

            return;


        if (!is_array($this->mergeColumns))

            $this->mergeColumns = array($this->mergeColumns);

        if (!is_array($this->extraRowColumns))

            $this->extraRowColumns = array($this->extraRowColumns);


        //store columns for group. Set object for existing columns in grid and string for attributes

        $groupColumns = array_unique(array_merge($this->mergeColumns, $this->extraRowColumns));

       

        foreach ($groupColumns as $key => $colName) {

            foreach ($this->columns as $column) {

                if (property_exists($column, 'name') && $column->name == $colName) {

                    $groupColumns[$key] = $column;                  

                    break;

                }

            }

        }


        

       

        //values for first row

        $lastStored = $this->getRowValues($groupColumns, $data[0], 0);

        foreach ($lastStored as $colName => $value) {

            $lastStored[$colName] = array(

                'value' => $value,

                'count' => 1,

                'index' => 0,

            );

        }


        //iterate data 

        for ($i = 1; $i < count($data); $i++) {

            //save row values in array

            $current = $this->getRowValues($groupColumns, $data[$i], $i);


            //define is change occured. Need this extra foreach for correctly proceed extraRows

            $changedColumns = array();

            foreach ($current as $colName => $curValue) {

                if ($curValue != $lastStored[$colName]['value']) {

                    $changedColumns[] = $colName;

                }

            }


            /*

              if this flag = true -> we will write change (to $this->_changes) for all grouping columns.

              It's required when change of any column from extraRowColumns occurs

             */

            $saveChangeForAllColumns = (count(array_intersect($changedColumns, $this->extraRowColumns)) > 0);

           

         

            /*

              this changeOccured related to foreach below. It is required only for mergeType == self::MERGE_NESTED,

              to write change for all nested columns when change of previous column occured

             */

            $changeOccured = false;

            foreach ($current as $colName => $curValue) {

                //value changed

                $valueChanged = ($curValue != $lastStored[$colName]['value']);

                //change already occured in this loop and mergeType set to MERGETYPE_NESTED

                $saveChange = $valueChanged || ($changeOccured && $this->mergeType == self::MERGE_NESTED);


                if ($saveChangeForAllColumns || $saveChange) {

                    $changeOccured = true;


                    //store in class var

                    $prevIndex = $lastStored[$colName]['index'];

                   

                 //$prevIndex +=1;

                    $this->_changes[$prevIndex]['columns'][$colName] = $lastStored[$colName];

                    if (!isset($this->_changes[$prevIndex]['count'])) {

                       // $x = $this->_changes[$prevIndex]['count'];

                        $this->_changes[$prevIndex]['count'] = $lastStored[$colName]['count'];

                      

                        echo '<pre>';

                        print_r($this->_changes[$prevIndex]['count'].'---------'.$prevIndex);

                        echo '</pre>';

                       

                    }


                    //update lastStored for particular column

                    $lastStored[$colName] = array(

                        'value' => $curValue,

                        'count' => 1,

                        'index' => $i,

                    );

                } else {

                    $lastStored[$colName]['count']++;

                }

            }

        }


        //storing for last row

        foreach ($lastStored as $colName => $v) {

           

         

            $prevIndex = $v['index'];

           

           

            $this->_changes[$prevIndex]['columns'][$colName] = $v;

            if (!isset($this->_changes[$prevIndex]['count'])) {

                $this->_changes[$prevIndex]['count'] = $v['count'];

               

            }

        }

    }


    public function renderTableRow($row) {

        $change = false;


        

        // original CGridView code

        if ($this->rowCssClassExpression !== null) {

            $data = $this->dataProvider->data[$row];

            echo '<tr class="' . $this->evaluateExpression($this->rowCssClassExpression, array('row' => $row, 'data' => $data)) . '">';

        } else if (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0)

            echo '<tr class="' . $this->rowCssClass[$row % $n] . '">';

        else

            echo '<tr>';

        

 




        if (!$this->_changes) { //standart CGridview's render

            foreach ($this->columns as $column) {

                $column->renderDataCell($row);

            }

        } else {  //for grouping       

            foreach ($this->columns as $column) {


                $isGroupColumn = property_exists($column, 'name') && in_array($column->name, $this->mergeColumns);


                if (!$isGroupColumn) {

                    $column->renderDataCell($row);

                    continue;

                }


                $isChangedColumn = $change && array_key_exists($column->name, $change['columns']);


                //for rowspan show only changes (with rowspan)

                switch ($this->mergeType) {

                    case self::MERGE_SIMPLE:

                    case self::MERGE_NESTED:

                        if ($isChangedColumn) {

                            $options = $column->htmlOptions;

                            $column->htmlOptions['rowspan'] = $change['columns'][$column->name]['count'];

                            $column->htmlOptions['class'] = 'merge';

                            $style = isset($column->htmlOptions['style']) ? $column->htmlOptions['style'] : '';

                            $column->htmlOptions['style'] = $style . ';' . $this->mergeCellCss;

                            $column->renderDataCell($row);

                            $column->htmlOptions = $options;

                        }

                        break;


                    case self::MERGE_FIRSTROW:

                        if ($isChangedColumn) {

                            $column->renderDataCell($row);

                        } else {

                            echo '<td></td>';

                        }

                        break;

                }

            }

        }

  // change nach :

        $this->valueTot += $this->dataProvider->data[$row]->heures;

        if ($this->_changes && array_key_exists($row, $this->_changes)) {

            

            $change = $this->_changes[$row];

            //if change in extracolumns --> put extra row

            $columnsInExtra = array_intersect(array_keys($change['columns']), $this->extraRowColumns);

            if (count($columnsInExtra) > 0) {

                $this->renderExtraRow($row, $change, $columnsInExtra, $this->valueTot);

                $this->valueTot = 0;

            }

        }     

      

        echo "</tr>\n";

    }

// change nach fin


    /**

     * returns array of rendered column values (TD)

     * 

     * @param mixed $columns

     * @param mixed $rowIndex

     */

    private function getRowValues($columns, $data, $rowIndex) {

        foreach ($columns as $column) {

            if ($column instanceOf CGridColumn) {

                $result[$column->name] = $this->getDataCellContent($column, $data, $rowIndex);

            } elseif (is_string($column)) {

                if (is_array($data) && array_key_exists($column, $data)) {

                    $result[$column] = $data[$column];

                } elseif ($data instanceOf CModel && $data->hasAttribute($column)) {

                    $result[$column] = $data->getAttribute($column);

                } else {

                    throw new CException('Column or attribute "' . $column . '" not found!');

                }

            }

        }


        return $result;

    }


// change nach :

    /**

     * renders extra row

     * 

     * @param mixed $beforeRow

     * @param mixed $change

     */

    private function renderExtraRow($beforeRow, $change, $columnsInExtra, $valueTot) {


        $data = $this->dataProvider->data[$beforeRow];

        if ($this->extraRowExpression) { //user defined expression, use it!

            $content = $this->evaluateExpression($this->extraRowExpression, array('data' => $data, 'row' => $beforeRow, 'values' => $change['columns']));

        } else {  //generte value

            $values = array();

            foreach ($columnsInExtra as $c) {

                $values[] = $change['columns'][$c]['value'];

            }

            $content = '<strong style="margin-left:20px">Total du ' . implode(' :: ', $values) . ' : ' . $valueTot . ' heures</strong>';

        }


        $colspan = count($this->columns);


        echo '<tr>';

        echo '<td class="extrarow" colspan="' . $colspan . '">' . $content . '</td>';

        echo '</tr>';

    }


// change nach fin


    /**

     * need to rewrite this function as it is protected in CDataColumn: it is strange as all methods inside are public 

     * 

     * @param mixed $column

     * @param mixed $row

     * @param mixed $data

     */

    private function getDataCellContent($column, $data, $row) {

        if ($column->value !== null)

            $value = $column->evaluateExpression($column->value, array('data' => $data, 'row' => $row));

        else if ($column->name !== null)

            $value = CHtml::value($data, $column->name);


        return $value === null ? $column->grid->nullDisplay : $column->grid->getFormatter()->format($value, $column->type);

    }


}



I modify 2 section. this section are between the comment : // change nach :

Thank you

nath

Seems it is working fine… only you need is to group the data based on currently visible i.e. per page… I think there should be a calculation of last grouped data…

No, it’isn’t working fine, data are not grouped well as you can see in the attached thumbnail.

Total du 26-12-2012 is not at the good place, total du 3-12-2012 is not at the good place, too.