Простой PHP генератор сложных HTML таблиц

Всем привет. Хочу поделиться свеженаписанным генератором HTML таблиц.

Участились случаи сбора различной статистики и компоновки ее в сложные таблицы с различными групировками.

image

Заметив такую тенденцию решил автоматизировать рисование таблиц.

Как итог:
  • Избавил себя от составления слоеных циклов для вывода массива;
  • Получил стандартизацию структуры массивов;


image

Простейшая структура


Собственно сам класс статичный, имеет 1 public метод, который принимает 2 параметра и возвращает HTML код таблицы:

Table::html($data, $tableInfo = false);

  • $data — сам массив, который предстоит напечатать
  • $tableInfo(опционально) — массив настроек таблицы

Массив $data имеет следующую структуру:



Array
(
	[0] => Array		// 1 строка
	(
		[0] => Cell_00			// 1 ячейка 1 строки
		[1] => Cell_01			// 2 ячейка 1 строки
		[2] => Cell_02			// 3 ячейка 1 строки
	)
	[1]	=> Array		// 2 строка
	(
		[0] => Cell_10			// 1 ячейка
		[1] => Cell_11			// 2 ячейка
		[2] => Cell_12			// 3 ячейка
	)
);



for($i=0;$i<5;$i++)
	for($j=0;$j<5;$j++)
		$matrix[$i][$j] = $i.$j;


Вложенные массивы


Но это примеры простейших матриц. Главной особенностью класса является автоматический подсчет rowspan и группировка данных.

Если вместо строки данных(ячейки) вставить аналогичный массив, то все данные этого подмассива будут помещены в строку родителя:

image
Код
Array
(
    [0] => Array
    (
        [0] => Cell_00
        [1] => Cell_01
        [2] => Cell_02
    )

    [1] => Array
    (
        [0] => Cell_10
        [1] => Cell_11
        [2] => Array
        (
            [0] => Array
            (
                [0] => Cell_120
            )

            [1] => Array
            (
                [0] => Cell_121
            )
        )
    )
)

Чуть глубже:
image
Код
Array
(
    [0] => Array
    (
        [0] => Cell_00
        [1] => Cell_01
        [2] => Cell_02
    )
    [1] => Array
    (
        [0] => Cell_10
        [1] => Array
        (
            [0] => Array
            (
                [0] => Cell_110
                [1] => Array
                (
                    [0] => Array
                    (
                        [0] => Cell_1100
                    )
                    [1] => Array
                    (
                        [0] => Cell_1101
                    )
                )
            )
            [1] => Array
            (
                [0] => Cell_111
                [1] => Cell_112
            )
        )
    )
)


Кастомизация


Итак, класс умеет автоматически рисовать сложные таблицы и подсчитывать rowspan'ы. Теперь перейдем к ручной настройке таблицы.
Массив $data может содержать подмассив ['tableInfo'] — в нем можно указывать различные настройки, в зависимости от его('tableInfo') расположения.
Он может находиться:
  1. В корне массива, при перечислении строк

    Пример
    Array
    (
    	[0] => Array(...)
    	[1]	=> Array(...)
    	...
    	[n]	=> Array(...)
    	[tableInfo]	=> Array (...)
    )
    

    'tableInfo' расположенный в корне массива позволяет задать общие настройки и параметры для всей таблицы:

    Array
    (
        [rowspan] => false		//Отключить автоматическую группировку. Глобальное правило для всей таблицы.
        [cols] => Array		//Массив колонок
        (
            [0] => Array
            (
                [title] => ФИО	//title указывает заголовки колонок(выводятся в th).
                [key] => name	//key указывает какие и в каком порядке выводить ячейки.
            )
    
            [1] => Array		//title и key независимы друг от друга
            (			//в массиве cols можно задать только title, можно только key
                [title] => Телефон	//Но если решили указывать оба правила, то указывайте их попарно для каждой колонки,
                [key] => tel	//иначе получите нежелательный результат
            )
    
        )
        //Ряд HTML атрибутов для таблицы:
        [id] => tableId
        [class] => tableClass
        [style] => color:#000;
        [args] => align=left width=80%	//В args можно указывать любые атрибуты, будут переданы без изменений.
    )
    

    Этот же массив можно передать вторым параметром методу:

    Table::html($data, $tableInfo = false);

    Если используются оба способа одновременно(isset($data['tableInfo'] && isset($tableInfo)), то в спорных случаях приоритет отдается правилам из второго массива $tableInfo.

    array_replace_recursive($data['tableInfo'], $tableInfo);

    Пример использования title
    image
    Код
    Array
    (
        [0] => Array
        (
            [0] => Cell_00
            [1] => Cell_01
            [2] => Cell_02
        )
        [1] => Array
        (
            [0] => Cell_10
            [1] => Cell_11
            [2] => Cell_12
        )
        [tableInfo] => Array
        (
            [cols] => Array
            (
                [0] => Array
                (
                    [title] => Title 1
                )
                [1] => Array
                (
                    [title] => Title 2
                )
                [2] => Array
                (
                    [title] => Title 3
                )
            )
        )
    )
    

    Пример использования key
    image
    Код
    Array
    (
        [0] => Array
        (
            [0] => Cell_00
            [1] => Cell_01
            [2] => Cell_02
        )
        [1] => Array
        (
            [0] => Cell_10
            [1] => Cell_11
            [2] => Cell_12
            [3] => Cell_13    //Будет проигнорирован, т.к. не указан в key
        )
        [tableInfo] => Array
        (
            [cols] => Array
            (
                [0] => Array
                (
                    [key] => 2
                )
                [1] => Array
                (
                    [key] => 0
                )
                [2] => Array
                (
                    [key] => 1
                )
            )
        )
    )
    

    image
    Код
    $exmpl3 = array(
    	'tableInfo' => array(
    		'cols' => array(
    			0 => array('key' => 'cell0'),
    			1 => array('key' => 'cell1'),
    			2 => array('key' => 'cell2'),
    		)
    	),
    	0 => array(
    		'cell0' => 'Cell_00',
    		'cell2' => 'Cell_02',
    		'cell1' => 'Cell_01',
    	),
    	1 => array(
    		'cell0' => 'Cell_10',
    		'sub' => array(
    			0 => array(
    				'cell1' => 'Cell_110',
    				'sub' => array(
    					0 => array(
    						'cell2' => 'Cell_1100',
    					),
    					1 => array(
    						'cell0' => 'asdasd_10',	//Не будут выведены, т.к. при выводе подмассива
    						'cell1' => 'asdasd_110',//учитываются только неиспользованные ключи
    						'cell2' => 'Cell_1101'
    					)
    				)
    			),
    			'sub' => array('cell1' => 'Cell_111', 'cell2' => 'Cell_112'),
    		)
    	)
    );
    

    Здесь стоит упомянуть, что если key вообще не используются, то массив выводимых ключей заполняется всеми ключами ячеек(не подмассивов), которые находятся в этой строке. Например в этом случае:

    Код
    $exmpl = array(
    	0 => array(
    		0 => 'Cell_00',
    		1 => 'Cell_01',
    		2 => 'Cell_02',
    	),
    	1 => array(
    		0 => 'Cell_10',
    		1 => 'Cell_11',
    		2 => 'Cell_12',
    	),
    );
    

    Для обоих строчек массив $colKeys (массив выводимых ячеек) будет выглядеть вот так:

    $colKeys = array(0, 1, 2);
    

  2. В любом массиве ячеек (на любом уровне)

    Пример
    Array
    (
    	[0] => Array(
    		[qwe] => йцу
    		[asd] => фыв
    		[tableInfo]	=> Array (...)
    	)
    	[1]	=> Array(...)
    	...
    )
    

    Array
    (
    	[0] => Array(
    		[qwe] => йцу
    		[asd] => фыв
    		[zxc] => Array(
    			[0] => Array(
    				[jkl] => олд
    				[tableInfo]	=> Array (...)
    			)
    			[1] => Array()
    		)
    	)
    	[1]	=> Array(...)
    	...
    )
    

    'tableInfo' расположенный в массиве ячеек, может содержать настройки для строки, в которой он находится и/или настройки для каждой отдельной ячейки:

    'tableInfo' => array(
    	'rowspan' => false,	//Запретить подсчет rowspan для данной строки
    	'rowspan' => true,	//Разрешить подсчет rowspan для данной строки
    	'rowspan' => 5,	//Указать точное значение rowspan для данной строки
    	
    	'keys' => array(			//Правила по модификации масива выводимых ячеек
    		'delete' => 'all',		//Удалить все ключи выводимых ячеек
    		'delete' => array('cell2'),	//Удалить некоторые ключи(тут 'cell2')
    		'add' => array('cell3'),	//Добавить в конец несколько ключей(тут 'cell3')
    		'forwarding' => array(		//Замена одного ключа на другой. Можно указать несколько правил
    			0 => array(
    				'src' => 'cell1',	//В этом примере вместо ячейки cell1
    				'dst' => 'cell2'	//Будет выведена ячейка cell2
    			),
    		),				//Эти правила действуют, даже если вы не указывали явно список ключей для таблицы
    	),					//т.к. в этом случае массив выводимых ячеек будет содержать все ячейки строки
    	
    	'cells' => array(			//Правила для ячеек
    		'cell2'	=>	array(		//Массив правил для ячейки 'cell2'
    			'rowspan' => false,		//Отключить
    			'rowspan' => true,		//Включить
    			'rowspan' => 5,		//Указать точное значение
    			'colspan' => 6,		//Указать ТОЛЬКО точное значение
    			//Ряд HTML атрибутов для ячейки:
    			'id'	=> 'cellId',
    			'class'	=> 'cellClass',
    			'style'	=> 'color:#000;',
    			'args'	=> 'align=left width=80%'
    		)
    	),
    	//Ряд HTML атрибутов для строки:
    	'id'	=> 'rowId',
    	'class'	=> 'rowClass',
    	'style'	=> 'color:#000;',
    	'args'	=> 'align=left width=80%'
    );
    


Приоритеты rowspan

  1. Правило для ячейки (самое главное правило);
  2. Правило для строки;
  3. Правило для таблицы;
  4. По умолчанию rowspan считается всегда, если присутствует подмассив.


Ну, вот и все. Некоторые моменты решил не рассматривать подробно, если будут вопросы — распишу.
Скачать класс Table

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 18

    +3
    Может оформить как пакет для composer?
    +9
    Это крайне полезная и инновационная разработка, соответствующая лучшим образцам php-программирования 1998-го года. Рекомендую вам вернуться в него и создать целый фреймворк на таких технологиях.
      +3
      Поясните подробней. Что здесь не так, кроме лишнего статического класса?
        –3
        Были времена, году эдак в 2008, когда модно было таблицы в верстке не использовать вообще и вспоминать год эдак 1998.
        Скорее всего оно тут про это.
      +3
      Спасибо, полезная вещь.
      Мне кажется, что возвращать текст было бы лучше — заморачиваться с буферизацией вывода не очень приятно и не всегда возможно.
        +2
        исправил. Теперь возвращает текст.
        +2
        Задумка «на попробовать» хороша, но использовать такое в современных реалиях я бы не стал. А может вспомним таки про json/js? Попробуйте перенести логику отображения на js.
          +3
          Не забудьте добавить jquery
          +1
          Если отрисовщик таблиц не умеет работать в потоковом режиме (построчная запись с немедленным аутпутом в стрим), то проблемы с памятью обеспечены.
            +5
            Это что за таблица такая должна быть, бюджет страны?
              +1
              У нас на проекте есть несколько страниц с репортами, которые представляют собой таблицу на 19 столбиков и 500+ строк. Причём это всё увешано ссылками, hover-ами, и несколько tooltip-ов. Так вот всё это чудо рендерится довольно небыстро, хотя браузер всё равно прожёвывать не успевает. И нет, уменьшить таблицу никак, надо все данные сразу. Так что бюджет страны совсем не обязателен.
            +3
            static -> global -> evil

            Почему бы не сделать класс TableView с конструктором от данных поумолчанию и набор методов для манипуляций над деревом, в числе которых и разнообразные настройки отображения?

            Если уж хочется абстракций, то уж лучше так: gist.github.com/saksmt/68c39037ccabadbbd987
                0
                Сначала вы одну копию данных копите в массив, потом её класс пачкой прожёвывает и выплёвывает вторую копию данных (разбухшую после генерации). Добавить возможные утечки памяти и лишние копирования внутри класса, добавить расход памяти на собственно получение данных через какую-нить орм (а придётся их и оттуда то ли сразу пачкой грузить и итерировать во входной массив (+1 копия), то ли напрямую гидрировать… И вот ваши 15МБ данных превращаются в 150, а такое уже в фон надо отправлять.

                И все сложности от того, что в классе-генераторе таблиц нету стриминга.
                  +1
                  Мой комментарий телепортировался в какую-то подпространственную ветку из соседней…

              0
              Картинки в статье не отображаются. Стоит поменять хостинг картинок, а лучше вообще habrastorage использовать

              Инфо:
              habrastorage.org/
              habrahabr.ru/company/tm/blog/139897/
                0
                Вы тоже из 98го года? :D
                Все картинки давно автоматически переливаются на хабрасторадж, и в статье они уже там.
                  0
                  это я их на хабрастор перезалил. На момент его комментария они были на другом хостинге.

            Only users with full accounts can post comments. Log in, please.