CGridView и "сложная" шапка
Идея была такова - отобразить шапку в несколько строк с объединением столбцов и строк (использованием rowspan и colspan). В итоге должно получиться следующее (ссылка на скачивание файла внизу страницы):
Итак, приступим.
Переопределяем класс CGridView и его методы: initColumns() и renderTableHeader().
Функция формирования строк шапки таблицы:
Вызов этого компонента осуществляется следующим образом:
Массивы $columns и $headers могут различаться по составу, но в итоге будут выведены только поля, присутствующие в обоих массивах.
Скачать
Переопределяем класс CGridView и его методы: initColumns() и renderTableHeader().
protected function initColumns() {
if ($this->columns === array()) {
if ($this->dataProvider instanceof CActiveDataProvider) {
$this->columns = $this->dataProvider->model->attributeNames();
} else if ($this->dataProvider instanceof IDataProvider) {
// use the keys of the first row of data as the default columns
$data = $this->dataProvider->getData();
if (isset($data[0]) && is_array($data[0]))
$this->columns = array_keys($data[0]);
}
}
/* Получаем массив ключей колонок */
$columns = array();
foreach ($this->columns as $k => $v) {
$columns[is_array($v) ? $v['name'] : $v] = $v;
}
/* Формируем шапку таблицы */
$this->renderHeaderRow($this->headers, $columns);
/* Исключаем колонки, не присутствующие в шапке */
$this->columns = array();
foreach ($this->headerColumns as $v) {
if (is_array($columns[$v])) {
$this->columns[] = $columns[$v];
} else {
$this->columns[] = $columns[$v];
}
}
$this->headerColumns = array();
$id = $this->getId();
foreach ($this->columns as $i => $column) {
if (is_string($column))
$column = $this->createDataColumn($column);
else {
if (!isset($column['class']))
$column['class'] = 'CDataColumn';
$column = Yii::createComponent($column, $this);
}
if (!$column->visible) {
unset($this->columns[$i]);
continue;
}
if ($column->id === null)
$column->id = $id . '_c' . $i;
$this->columns[$i] = $column;
$this->headerColumns[$column->name] = $i;
}
foreach ($this->columns as $column) {
$column->init();
}
}
Функция формирования строк шапки таблицы:
private function renderHeaderRow($row, $columns, $level = 0) {
$colspan = 0;
foreach ($row as $k => $item) {
if (is_array($item)) {
if (isset($item['childs'])) {
if (count($this->headerRows)) {
}
$count = $this->renderHeaderRow($item['childs'], $columns, $level + 1);
if ($count) {
$this->headerRows[$level][] = array(
'colspan' => $count,
'header' => isset($item['label']) ? $item['label'] : $k
);
$colspan += $count - 1;
} else {
unset($row[$k]);
}
}
} else {
if (array_key_exists($item, $columns)) {
$this->headerRows[$level][] = array(
'name' => $item
);
$this->headerColumns[] = $item;
} else {
unset($row[$k]);
}
}
}
return count($row) + $colspan;
}
И, наконец, вывод шапки: необходимо заменить строки в renderTableHeader()
echo "\n";
foreach($this->columns as $column)
$column->renderHeaderCell();
echo "\n";
на
for ($r = 0; $r < count($this->headerRows); $r++) {
$row = $this->headerRows[$r];
echo '';
foreach ($row as $k => $v) {
$htmlOptions = array();
$colspan = (isset($v['colspan'])) ? $v['colspan'] : false;
if ($colspan > 1) {
$htmlOptions['colspan'] = $colspan;
}
$rowspan = ($r < count($this->headerRows) && !$colspan) ? count($this->headerRows) - $r : false;
if ($rowspan) {
$htmlOptions['rowspan'] = $rowspan;
}
if (isset($v['name'])) {
$column = $this->columns [$this->headerColumns[$v['name']]];
$column->headerHtmlOptions = $htmlOptions;
$column->renderHeaderCell();
} else {
echo $this->renderHeaderCellSimple(isset($v['header']) ? $v['header'] : " ", $htmlOptions);
}
}
echo '';
}
Вызов этого компонента осуществляется следующим образом:
$this->widget('MTable', array(
'id'=>'obj',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
'title',
'address',
'kray_id',
'city_id' => array(
'name'=>'city_id',
'value'=>'$data->city->title', //вот это важно
'filter'=>CHtml::listData(City::model()->findAll(), 'id', 'title'),
),
),
'headers'=>array(
'id',
'title',
'data' => array(
'label'=>Yii::t('main', 'Данные'), // если label отсутствует, то будет выведен ключ элемента (в данном случае 'data')
'childs'=>array(
'address',
'geo' => array(
'label'=>Yii::t('main', 'Местоположение'),
'childs'=> array(
'city_id',
'kray_id',
),
),
),
),
),
));
Массивы $columns и $headers могут различаться по составу, но в итоге будут выведены только поля, присутствующие в обоих массивах.
Скачать
спасибо большое, нужная статья.
ОтветитьУдалитьа подскажите, пожалуйста, как новый класс с таблицей к проекту привязать?
Необходимо поместить файл в папку components. Пример вызова приведен в конце статьи ($this->widget('MTable'....). Будут еще вопросы - спрашивайте )
УдалитьДобрый день. Вопрос вот в чём, будет ли это всё работать с колонками типа CButtonColumn, то есть с теми, у которых отсутствует аттрибут name.. Такое впечатление, что нет..
ОтветитьУдалитьЕсть предложение писать так :
protected function initColumns(){
.......................
$id = null;
if(isset($column->name))
$id = $column->name;
elseif(isset($column->id)){
$id = $column->id;
}
if($id)
$this->headerColumns[$id] = $i;
}
c CButtonColumn без отдельной обработки работать не будет - это факт :)
УдалитьПросто в моем проекте эта колонка в принципе отсутствует и не нужна, т.к. по клику на строку открывается диалоговое окно, где и производятся все манипуляции.
И ещё, в классе renderTableHeader нужно ведь в цикле выводить tr - ки, а не просто пустые строки
ОтветитьУдалитьобновила пост :) И залила новый файл. Если интересно - посмотрите.
УдалитьВ целом это даже не конечная версия, но наиболее близкая к той, что была опубликована здесь.
Добавлена возможность задания id строкам таблицы, и исправлены ошибки, которые были раньше.
К сожалению, файл недоступен для скачивания.
ОтветитьУдалитьв очередной раз загрузила новый файл :) Теперь на гуглдоках, надеюсь, больше он не пропадет :)
УдалитьБольшущее спасибо, вы спасли мои мозги))
УдалитьСпасибо за пост. Всё работает как надо. Жаль только, что поля без атрибута 'name' не обрабатываются
ОтветитьУдалитьНе думаю, что это проблема, если очень нужно, то и такую задачу можно решить :)
УдалитьКлассное расширение. Вот добавить бы еще объединение строк и группировку, но не так заморочено как здесь:
ОтветитьУдалитьhttp://groupgridview.demopage.ru/index.php?r=site/extrarowmerge
и цены бы не было )
Для объединения строк и группировки уже вроде есть готовые решения :) Правда чаще они платные )
УдалитьДа есть, но они без "сложной шапки". А что есть даже платные решения?) Не встречал, кроме как комплексных решений, ладно.
УдалитьВот если бы вы доделали ваше расширение, было бы очень очень здорово.
Можешь доделать ты, т.к. сейчас я мало работаю в Yii :) Но буду только рада дополнить этот пост :)
УдалитьХотелось бы, но у меня сейчас катастрофически не хватает времени (
Удалитьно я бы мог попробовать проспонсировать новые доработки =)
Как насчёт попробовать опять поработать в Yii ?))
Пишите в личку :)
УдалитьДевушка пишет такие штуки - молодцом!
ОтветитьУдалитьспасибо! приятно :)
УдалитьВ protected function initColumns:
ОтветитьУдалить/* Исключаем колонки, не присутствующие в шапке */
$this->columns = array();
foreach ($this->headerColumns as $v) {
if (is_array($columns[$v])) {
$this->columns[] = $columns[$v];
} else {
$this->columns[] = $columns[$v];
}
}
в if видимо что-то не так:))
видимо да )) И спустя год это заметили ))
УдалитьХорошая вещь, пришлась очень кстати, благодарю.
ОтветитьУдалитьможете обеснить
ОтветитьУдалить'city_id' => array(
'name'=>'city_id',
'value'=>'$data->city->title', //вот это важно
'filter'=>CHtml::listData(City::model()->findAll(), 'id', 'title'),
Что именно объяснить?
УдалитьДля поля city_id мы описываем, откуда должны браться данные и в каком виде выводиться.
name - поле таблицы, которая является моделью для CGridView
value - значение, которое должно быть выведено. В данном случае мы используем поле title связанной модели, которая описана через связь city
filter - этот параметр задает, что будет выведено в строке фильтра. В данном случае выпадающий список, в качестве значений которого выводятся названия городов из модели City
Этот комментарий был удален автором.
УдалитьЭтот комментарий был удален автором.
Удалитьвсе понял.
ОтветитьУдалитьу вас тут есть маленький ошибка в начале файла
наследование от Grida
можешь подсказать как сделать в шапке кнопку и выпадающий список
ОтветитьУдалитьвот здесь я описывала, как это делается http://progergirl.blogspot.ru/2012/06/cgridview.html
Удалитьспасибо большое
УдалитьЭтот комментарий был удален автором.
ОтветитьУдалитьПодскажи плз как сделать такую шапку. http://prntscr.com/6v9zlb
ОтветитьУдалитьпри How_many нужно выводить связзаные данные в отдельные столбцы (столбец = дата) все обыскал ничего подобного не нашел.
Спасибо.
Очевидно, тебе нужно формировать из полученных данных массив для вывода и использовать CArrayDataProvider для грида. Другого варианта я не вижу, т.к. грид сам по себе не сможет транспонировать данные перед выводом в том виде, в каком ты хочешь
Удалитьвсе можно сделать в три шага. смотреть тут http://des1roer.blogspot.ru/2015/05/yii_25.html
ОтветитьУдалить