/* Modified Yii2 beta GridView that extends Kartik's GridView In this version you can display more then one attribute in column (group columns). Example view code: http://paste.ots.me/560724/text */ ' because '
' is not visible in 'export' to CSV public $defaultHeaderColumnGroupsSeparator = ",
"; public $defaultFooterColumnGroupsSeparator = ",
"; public $defaultDataColumnGroupsSeparator = ",
"; // filter by default adds 'new line' between filters [filter width 100%?] public $defaultFilterColumnGroupsSeparator = ""; /** * @var array separator of each group [header column] * Default set to $defaultGroupsSeparator */ public $headerColumnGroupsSeparators = []; /** * @var array separator of each group [footer column] * Default set to $defaultGroupsSeparator */ public $footerColumnGroupsSeparators = []; /** * @var array separator of each group [filter column] * Default set to $defaultGroupsSeparator */ public $filterColumnGroupsSeparators = []; /** * @var array separator of each group [data column] * Default set to $defaultGroupsSeparator */ public $dataColumnGroupsSeparators = []; /** * @var array[] columns grouped by column group ID */ public $groups = []; /** * Groups columns by column group ID. */ public function init() { parent::init(); // group columns foreach ($this->columns as $column) { $this->addToGroup($column); } // set separators for not configured groups foreach($this->groups as $group) { if(!isset($this->headerColumnGroupsSeparators[$group['id']])) { $this->headerColumnGroupsSeparators[$group['id']] = $this->defaultHeaderColumnGroupsSeparator; } if(!isset($this->footerColumnGroupsSeparators[$group['id']])) { $this->footerColumnGroupsSeparators[$group['id']] = $this->defaultFooterColumnGroupsSeparator; } if(!isset($this->filterColumnGroupsSeparators[$group['id']])) { $this->filterColumnGroupsSeparators[$group['id']] = $this->defaultFilterColumnGroupsSeparator; } if(!isset($this->dataColumnGroupsSeparators[$group['id']])) { $this->dataColumnGroupsSeparators[$group['id']] = $this->defaultDataColumnGroupsSeparator; } } } /** * Adds column to it's group if exists or creates new group. * Defines order of columns! */ public function addToGroup($column) { if ($column->group !== null) { foreach ($this->groups as $gID => $group) { if ($group['id'] == $column->group) { // group exists $this->groups[$gID]['columns'][] = $column; return; } } // group not exists, creates $this->groups[] = ['id' => $column->group, 'columns' => [$column]]; } else { // column without group, creates new group [with 1 element] $this->groups[] = ['id' => -1, 'columns' => [$column]]; } } /** * Renders the column group HTML. * @return bool|string the column group HTML or `false` if no column group should be rendered. */ public function renderColumnGroup() { $requireColumnGroup = false; foreach ($this->columns as $column) { /* @var $column Column */ if (!empty($column->options)) { $requireColumnGroup = true; break; } } if ($requireColumnGroup) { $cols = []; foreach ($this->groups as $group) { $options = []; foreach ($group['columns'] as $column) { $options = array_merge($options, $column->options); } $cols[] = Html::tag('col', '', $options); } // [FIX!] czy edytowalne kolumny zadzialaja dobrze // bez 'rel' (jedno 'rel' na pare kolumn) w 'col'? return Html::tag('colgroup', implode("\n", $cols)); } else { return false; } } /** * Renders the table header. * @return string the rendering result. */ public function renderTableHeader() { $cells = []; foreach ($this->groups as $group) { $groupedColumns = []; $groupedColumnsOptions = []; foreach ($group['columns'] as $column) { $columnHeader = $column->renderHeaderCell(); if($column->group === null) { // not grouped column, pure HTML table cell code $cell = $columnHeader; } else { // grouped column, merge options and content $groupedColumns[] = $columnHeader['content']; $groupedColumnsOptions = array_merge($groupedColumnsOptions, $columnHeader['options']); } } if(count($groupedColumns) > 0) { //separator $cellContent = implode($this->headerColumnGroupsSeparators[$group['id']], $groupedColumns); $cell = Html::tag('th', $cellContent, $groupedColumnsOptions); } $cells[] = $cell; } $content = Html::tag('tr', implode('', $cells), $this->headerRowOptions); if ($this->filterPosition == self::FILTER_POS_HEADER) { $content = $this->renderFilters() . $content; } elseif ($this->filterPosition == self::FILTER_POS_BODY) { $content .= $this->renderFilters(); } return "\n" . $content . "\n"; } /** * Renders the table footer. * @return string the rendering result. */ public function renderTableFooter() { // [FIX!] nie testowane $cells = []; foreach ($this->groups as $group) { $groupedColumns = []; $groupedColumnsOptions = []; foreach ($group['columns'] as $column) { $columnFooter = $column->renderFooterCell(); if($column->group === null) { // not grouped column, pure HTML table cell code $cell = $columnFooter; } else { // grouped column, merge options and content $groupedColumns[] = $columnFooter['content']; $groupedColumnsOptions = array_merge($groupedColumnsOptions, $columnFooter['options']); } } if(count($groupedColumns) > 0) { $cellContent = implode($this->footerColumnGroupsSeparators[$group['id']], $groupedColumns); $cell = Html::tag('td', $cellContent, $groupedColumnsOptions); } $cells[] = $cell; } $content = Html::tag('tr', implode('', $cells), $this->footerRowOptions); if ($this->filterPosition == self::FILTER_POS_FOOTER) { $content .= $this->renderFilters(); } return "\n" . $content . "\n"; } /** * Renders the filter. * @return string the rendering result. */ public function renderFilters() { if ($this->filterModel !== null) { $cells = []; foreach ($this->groups as $group) { $groupedColumns = []; $groupedColumnsOptions = []; foreach ($group['columns'] as $column) { $columnFilter = $column->renderFilterCell(); if($column->group === null) { // not grouped column, pure HTML table cell code $cell = $columnFilter; } else { // grouped column, merge options and content $groupedColumns[] = $columnFilter['content']; $groupedColumnsOptions = array_merge($groupedColumnsOptions, $columnFilter['options']); } } if(count($groupedColumns) > 0) { $cellContent = implode($this->filterColumnGroupsSeparators[$group['id']], $groupedColumns); $cell = Html::tag('td', $cellContent, $groupedColumnsOptions); } $cells[] = $cell; } return Html::tag('tr', implode('', $cells), $this->filterRowOptions); } else { return ''; } } /** * Renders the table body. * @return string the rendering result. */ public function renderTableBody() { $models = array_values($this->dataProvider->getModels()); $keys = $this->dataProvider->getKeys(); $rows = []; foreach ($models as $index => $model) { $key = $keys[$index]; if ($this->beforeRow !== null) { $row = call_user_func($this->beforeRow, $model, $key, $index, $this); if (!empty($row)) { $rows[] = $row; } } $rows[] = $this->renderTableRow($model, $key, $index); if ($this->afterRow !== null) { $row = call_user_func($this->afterRow, $model, $key, $index, $this); if (!empty($row)) { $rows[] = $row; } } } if (empty($rows)) { $colspan = count($this->columns); return "\n" . $this->renderEmpty() . "\n"; } else { return "\n" . implode("\n", $rows) . "\n"; } } /** * Renders a table row with the given data model and key. * @param mixed $model the data model to be rendered * @param mixed $key the key associated with the data model * @param integer $index the zero-based index of the data model among the model array returned by [[dataProvider]]. * @return string the rendering result */ public function renderTableRow($model, $key, $index) { $cells = []; foreach ($this->groups as $group) { $groupedColumns = []; $groupedColumnsOptions = []; foreach ($group['columns'] as $column) { $columnData = $column->renderDataCell($model, $key, $index); if($column->group === null) { // not grouped column, pure HTML table cell code $cell = $columnData; } else { // grouped column, merge options and content $groupedColumns[] = $columnData['content']; $groupedColumnsOptions = array_merge($groupedColumnsOptions, $columnData['options']); } } if(count($groupedColumns) > 0) { $cellContent = implode($this->dataColumnGroupsSeparators[$group['id']], $groupedColumns); $cell = Html::tag('td', $cellContent, $groupedColumnsOptions); } $cells[] = $cell; } if ($this->rowOptions instanceof Closure) { $options = call_user_func($this->rowOptions, $model, $key, $index, $this); } else { $options = $this->rowOptions; } $options['data-key'] = is_array($key) ? json_encode($key) : (string)$key; return Html::tag('tr', implode('', $cells), $options); } /** * Creates a [[DataColumn]] object based on a string in the format of "attribute:[format]:[label]:[groupID]". * @param string $text the column specification string * @return DataColumn the column instance * @throws InvalidConfigException if the column specification is invalid */ protected function createDataColumn($text) { if (!preg_match('/^([^:]+)(:(\w*))?(:([^:]*))?(:(.*))?$/', $text, $matches)) { throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"'); } return Yii::createObject([ 'class' => $this->dataColumnClass ? : DataColumn::className(), 'grid' => $this, 'attribute' => $matches[1], 'format' => isset($matches[3]) ? $matches[3] : 'text', // You may want keep orginal label and assign column to group 'label' => isset($matches[5]) && !empty($matches[5]) ? $matches[5] : null, 'group' => isset($matches[7]) ? $matches[7] : null, ]); } }