Last active
August 29, 2015 14:20
HTML Table Generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Advantages | |
Add data in any format. Associative, numberic, multiple rows, single rows, etc | |
Allows to add data and display in two methods. | |
Flexibility, extensibility. Any attrs, rows, cells, table, even/odd, etc | |
Filter rows for flexibility in attrs (row thirds, etc), as well as filter data and attrs based on data | |
Option - to show empty sections? Or only specific empty sections? - most notably datatables requiring header | |
Tools | |
CSS, classes, attrs | |
- http://tablestyler.com/ | |
Php classes | |
Codeigniter - best - https://ellislab.com/codeigniter/user-guide/libraries/table.html | |
https://github.com/naomik/htmlgen | |
http://www.dyn-web.com/php/table_class/code.php | |
http://www.phpclasses.org/package/6089-PHP-Display-data-from-arrays-in-HTML-tables-.htmlgen | |
http://www.phpclasses.org/package/667-PHP-Class-for-easy-composing-complex-html-tables.html | |
*/ | |
// col groups? | |
// row groups? | |
// Data input options | |
// add one row at a time | |
$data = array('first cell', 'second cell', 'third cell'); // add single row numerically | |
// each one, cell with data then attrs | |
$data = array( | |
array( 'data' => 'first cell', 'attrs' => array()), | |
array( 'data' => 'second cell', 'attrs' => array()), | |
array( 'data' => 'third cell', 'attrs' => array()), | |
); | |
// multiple rows | |
// without attrs | |
$data = array( | |
array('first cell, 'second cell', 'third cell'), // add multiple rows, numerically | |
array('first cell, 'second cell', 'third cell') | |
); | |
// with attrs - same setup | |
$data = array( | |
array( | |
'tr-attrs' => array(), | |
'td' => array( // this can be added without attrs as numeric array | |
array( 'data' => 'first cell', 'attrs' => array() ), // each one, cell with data then attrs | |
array( 'data' => 'second cell', 'attrs' => array()), | |
array( 'data' => 'third cell', 'attrs' => array()), | |
) | |
), | |
array() // next row | |
); | |
// filtering data | |
// check for most complicated full first by checking keys. Then check simpler and simpler. | |
// display warnings if improper keys? Make assumptions? | |
// associative arrays? | |
// - set order, if not take first row to setup order. | |
// - use keys as classes? | |
/* | |
table->generate() => display | |
table->set_caption() | |
table->set_heading() | |
table->add_row() | |
table->add_rows() // my custom | |
table->make_columns() // numeric array, broken up by number. Not a fan. | |
table->clear() // remove content. | |
table->function = 'htmlspecialchars'; // filter function. | |
My methods | |
table->get_table() // return, don't display | |
table->add_data() // add multiple rows, data, flexibile. | |
table->add_footer() | |
table->add_header() | |
table->set_assoc_order() // if associative array, set order of sells. Defaults to first row. | |
table->set_default_attrs(); | |
*/ | |
public $options = array( | |
'default_section_attrs' => true, // use default attributes | |
'append_sections_attrs' => true, | |
'debug' => false, | |
); | |
private $section_attrs => array(); | |
public $errors = ''; | |
private $thead_data = array(); | |
private $tbody_data = array(); | |
private $tfoot_data = array(); | |
private $caption = ''; | |
public function __construct($options = array(), $data = array(), $sections_attrs = array()){ | |
if($options){ | |
$this->options = array_merge($this->options, $options); | |
} | |
$this->clear_sections_attrs(); // ensure proper keys | |
if($this->options['default_section_attrs']){ | |
$this->reset_sections_attr_defaults(); | |
} | |
if($sections_attrs){ | |
$this->set_sections_attrs($sections_attrs, $this->options['append_sections_attrs']); | |
} | |
if($data){ | |
$this->add_data($data); | |
} | |
} | |
private function reset_sections_attrs_defaults(){ | |
$this->section_attrs => array( | |
'table-attrs' => array( | |
'class' => 'table-striped', // dataTable, table-striped, table-bordered, table-hovered, 'table-condensed', all bootstrap | |
'id' => '' | |
), | |
'row-attrs' => array( 'class' => 'row' ), | |
'row-even-attrs' => array( 'class' => 'even row-even' ), | |
'row-odd-attrs' => array( 'class' => 'odd row-odd' ), | |
'col-even-attrs' => array( 'class' => 'col-even' ), | |
'col-odd-attrs' => array( 'class' => 'col-odd' ), | |
); | |
} | |
public function clear_sections_attrs(){ | |
$this->section_attrs => array( | |
'table-attrs' => array(), | |
'caption-attrs' => array(), | |
'thead-attrs' => array(), | |
'tbody-attrs' => array(), | |
'tfoot-attrs' => array(), | |
'row-attrs' => array(), | |
'row-even-attrs' => array(), | |
'row-odd-attrs' => array(), | |
'col-even-attrs' => array(), | |
'col-odd-attrs' => array(), | |
); | |
} | |
public function clear_section_attrs($reset_defaults = true){ | |
if($reset_defaults){ | |
$this->reset_sections_attr_defaults(); | |
}else{ | |
$this->clear_sections_attrs(); | |
} | |
} | |
// changing default attrs | |
public function set_sections_attrs($attrs, $if_append = true){ // set multiple attrs at the same time? | |
// $if_append, add to default attrs. Otherwise overwrite. | |
foreach($attrs as $section => $section_attrs){ | |
if(isset($this->section_attrs[$section])){ | |
$this->set_section_attrs($section, $section_attrs, $append){ | |
}else{ | |
$this->errors .= 'method set_section_attrs, $attrs improperly formatted. Ensure matches default_attrs array keys. '; | |
} | |
} | |
} | |
public function set_section_attrs($section, $attrs, $append = true){ | |
if( isset($section_attrs[$section]) ){ | |
foreach($attrs as $attr => $val){ | |
if($append){ | |
$this->section_attrs[$section][$attr] .= $val; | |
}else{ | |
$this->section_attrs[$section][$attr] = $val; | |
} | |
} | |
} | |
} // change one attr only | |
public function clear($section = 'all'){ | |
$this->clear_data($section); | |
} | |
public function clear_data($section = 'all', $section_attrs = 'defaults'){ | |
if($section == 'thead' || $section == 'header'){ | |
$this->thead_data[] = array(); | |
}else if($section == 'tfoot' || $section == 'footer'){ | |
$this->tfoot_data[] = array(); | |
}else if($section == 'tbody' || $section == 'body'){ | |
$this->tbody_data[] = array(); | |
}else if($section == 'caption'){ | |
$this->caption = ''; | |
}else if($section == 'all'){ | |
$this->thead_data[] = array(); | |
$this->tfoot_data[] = array(); | |
$this->tbody_data[] = array(); | |
$this->caption = ''; | |
}else{ | |
$this->errors += 'clear_data $section unrecognized. Inputed: '.$section.'. Default to clearing all. '; | |
$this->thead_data[] = array(); | |
$this->tfoot_data[] = array(); | |
$this->tbody_data[] = array(); | |
$this->caption = ''; | |
} | |
} | |
public function set_caption($text){ | |
$this->add_data($text, 'caption'); | |
} | |
public function add_caption($text){ | |
$this->add_data($text, 'caption'); | |
} | |
public function add_footer($data){ | |
$this->add_data($data, 'tfoot'); | |
} | |
public function add_header($data){ | |
$this->add_data($data, 'thead'); | |
} | |
public function add_row($data){ | |
$this->add_data($data, 'tbody'); | |
} | |
public function add_rows($data){ | |
$this->add_data($data, 'tbody'); | |
} | |
public function add_data($data, $section = 'tbody'){ | |
if($data != 'caption'){ | |
$user_data = format_input_data($data); | |
}else{ | |
$user_data = $data; | |
} | |
if($section == 'thead' || $section == 'header'){ | |
$this->thead_data[] = $user_data; | |
}else if($section == 'tfoot' || $section == 'footer'){ | |
$this->tfoot_data[] = $user_data; | |
}else if($section == 'tbody' || $section == 'body'){ | |
$this->tbody_data[] = $user_data; | |
}else if($section == 'caption'){ | |
$this->caption = $user_data; | |
}else{ | |
$this->errors += 'set_data $section unrecognized. Inputed: '.$section.'. Default adding to tbody. '; | |
$this->tbody_data[] = $user_data; | |
} | |
} | |
private function single_dimension_format($array){ | |
// single row | |
$row = array( | |
'tr-attrs' => array(), | |
'td' => array() | |
); | |
foreach($array as $cell){ | |
$row['td']['data'] => $cell; | |
$row['td']['attrs'] => array(); // defaults | |
} | |
return $row; | |
} | |
private function format_single_row_td_data($array){ | |
$row = array( | |
'tr-attrs' => array(), // default tr attrs | |
'td' => array() | |
); | |
foreach($array as $cell_data){ | |
$row['td']['data'] = (is_array($cell_data) && isset($cell_data['data'])) ? $cell_data['data'] : $cell_data; // improve me - check all for data, if array, if not? | |
$row['td']['attrs'] = ($cell_data['attrs']) ? $cell_data['attrs'] : array(); // defaults | |
} | |
return $row; | |
} | |
private function format_single_row_tr_attrs($array){ | |
$row = array( | |
'tr-attrs' => $array['tr-attrs'], | |
'td' => array(); | |
); | |
foreach($array['td'] as $cell){ | |
$row['td'][] = array( | |
'attrs' => $cell['attrs'], | |
'data' => $cell['data'] | |
); | |
} | |
return $row; | |
} | |
// There has to be a better way. More reliable and better pattern oriented. | |
private function format_input_data($array){ | |
// Logic | |
/* | |
if numeric, and single dimension, each row. | |
if numeric then assoc - check which kind assoc. tr-attrs, td, | |
if numeric, numeric, then 'attr' and 'data' then sinle row with td attrs and data | |
if numeric, and numeric again, then not 'attr' or 'data' - adding multiple rows. Detect which kind | |
if only two numeric no assoc - multiple rows | |
*/ | |
if(!is_array($data)){ | |
// return, not array | |
} | |
// if not associative then not attrs on first elems | |
if(!$this->is_assoc($array) && !isset($array['tr-attrs']) ){ | |
/* Each of these should be in it's own function */ | |
// if more than single row or cell with attrs | |
if(!$this->is_multi_dimension($array)){ | |
$row = $this->single_dimension_format($array); | |
}else{ | |
// multidimensional rows | |
// each cell with data and attrs | |
if(isset($array[0]['data']) && !isset($array['tr-attrs'])){ | |
$row = $this->format_single_row_td_data($array); | |
}else if(isset($array['tr-attrs'])){ | |
// on row, tr-attrs, | |
$row = $this->format_single_row_tr_attrs($array); | |
}else if( isset($array[0]) && is_array($array[0]) ){ | |
// multidimensional rows? | |
// multiple rows | |
// ??? | |
// $formated_data | |
$formated_data = array(); // this is all the data the user added | |
foreach($array as $row_array){ | |
$formated_data[] = $this->format_input_data($row_array); | |
} | |
// all inputed data filtered, return | |
return $formatted_data; | |
} | |
} | |
}else{ | |
// associative array, how deal with? | |
} | |
return $row; | |
} | |
public function generate(){ | |
$this->display_table(); | |
} | |
public function display_table(){ | |
echo $this->get_table(); | |
} | |
public function get_table(){ | |
$html = ''; | |
$html .= '<table '.attrs_to_html($this->section_attrs['table-attrs']).'>'; | |
$html .= get_section_html('caption'); | |
$html .= get_section_html('thead'); | |
$html .= get_section_html('tbody'); | |
$html .= get_section_html('tfoot'); | |
$html .= '</table>'; | |
return $html; | |
} | |
public function get_section_html($section == 'tbody'){ | |
$html = ''; | |
if( $section == 'caption' && $this->caption){ | |
$html .= '<caption '.attrs_to_html($this->section_attrs['caption-attrs']).'>'.$this->caption.'</caption>'; | |
}else if(($section == 'thead' || $section == 'header') && $this->thead_data){ | |
$html .= '<thead '.attrs_to_html($this->section_attrs['thead-attrs']).'>'; | |
$html .= $this->section_to_html($this->thead_data, 'thead'); | |
$html .= '</thead>'; | |
}else if(($section == 'tbody' || $section == 'body') && $this->tbody_data){ | |
$html .= '<tbody '.attrs_to_html($this->section_attrs['tbody-attrs']).'>'; | |
$html .= $this->section_to_html($this->tbody_data, 'tbody'); | |
$html .= '</tbody>'; | |
}else if(($section == 'tfoot' || $section == 'footer') && $this->tfoot_data){ | |
$html .= '<tfoot '.attrs_to_html($this->section_attrs['tfoot-attrs']).'>'; | |
$html .= $this->section_to_html($this->tfoot_data, 'tfoot'); | |
$html .= '</tfoot>'; | |
}else{ | |
// error fix me | |
} | |
} | |
private function section_to_html($data, $section = 'tbody'){ | |
$html_data = ''; | |
$tag = ($section == 'thead') ? 'th' : 'td'; | |
$row_i=0; | |
foreach($data as $row){ | |
$html_data .= $this->row_to_html($row, $row_i, $tag); | |
$row_i++; | |
} | |
return $html_data; | |
} | |
/* | |
'row-attrs' => array( 'class' => 'row' ), | |
'row-even-attrs' => array( 'class' => 'even row-even' ), | |
'row-odd-attrs' => array( 'class' => 'odd row-odd' ), | |
'col-even-attrs' => array( 'class' => 'col-even' ), | |
'col-odd-attrs' => array( 'class' => 'col-odd' ), | |
*/ | |
private function row_to_html($row, $row_i, $tag = 'td'){ | |
$html_row = ''; | |
$tr_attrs = ''; | |
$tr_attrs .= attrs_to_html($row['tr-attrs']); | |
$tr_attrs .= attrs_to_html($this->section_attrs['row-attrs']); | |
$tr_attrs .= ($i%$row_i) ? attrs_to_html($this->section_attrs['row-odd-attrs']) : attrs_to_html($this->section_attrs['row-even-attrs']); | |
$td_i = 0; | |
$td_attrs = ''; // col even, col odd | |
// '<tr attrs><td attrs></td></tr>' | |
$html_row .= '<tr '.$tr_attrs.'>'; | |
foreach($row['td'] as $cell){ | |
$td_attrs = attrs_to_html($cell['attrs']); | |
$td_attrs .= ($i%$td_i) ? attrs_to_html($this->section_attrs['col-odd-attrs']) : attrs_to_html($this->section_attrs['col-even-attrs']); | |
$html_row .= '<'.$tag.' '.$td_attrs.'>'.$cell['data'].'</'.$tag.'>'; | |
$td_i++; | |
} | |
$html_row .= '</tr>'; | |
return $html_row; | |
} | |
private function attrs_to_html($attrs){ | |
if(!$attrs || !is_array($attrs)){ | |
return $attrs; | |
} | |
$attrs_html = ''; | |
foreach($attrs as $key => $val){ | |
$attrs_html = '"'.$key.'"="'.$val.'"'; | |
} | |
return $attrs_html; | |
} | |
// helper functions | |
private function is_assoc($array) { | |
return (bool)count(array_filter(array_keys($array), 'is_string')); | |
} | |
private function is_multi_dimension($array){ | |
foreach($array as $key => $val){ | |
if(is_array($val)){ | |
return true; | |
} | |
} | |
return false; | |
} | |
// Defaults = what to do with empty vals? | |
// This is old data, examples from previous project | |
function setup_datatables($datatables, $cols, $col_names = array(), $tr_class_filter = '', $td_class_filter = '', $row_data_filter = '', $complete_row_filter = ''){ | |
$inactive_row_classes = array( | |
// 0 => 'error', | |
// 1 => 'error', | |
// 2 => 'pending' | |
); | |
// if it has any of these columns, then it's the pricing table | |
$pricing_table_cols = array('monthly', 'up_front', 'pay_as_go'); // 'labels', 'custom' | |
$is_pricing_table = (array_intersect($pricing_table_cols, $cols)) ? true : false; | |
$html = ''; | |
$max_cols = count($cols); | |
// Add the headers, unless set to false | |
if($col_names != false){ | |
if(!$col_names){ | |
$col_names = $cols; | |
} | |
$html = '<thead><tr>'; | |
// Add headers | |
for($c=0;$c<$max_cols;$c++){ | |
$html .= '<th class="'.$cols[$c].'">'.$col_names[$c].'</th>'; | |
} | |
$html .= '</tr></thead>'; | |
} | |
$html .= '<tbody>'; | |
$max = count($datatables); | |
// loop through rows | |
for($r=0;$r<$max;$r++){ | |
$tr_class = ''; | |
if($tr_class_filter && is_callable($tr_class_filter)){ | |
// pass entire row to get tr class. given default classes row-even, etc, pass in current | |
$tr_class = call_user_func($tr_class_filter, $tr_class, $datatables[$r]); | |
} | |
if($row_data_filter && is_callable($row_data_filter)){ | |
// pass entire row to get tr class. given default classes row-even, etc, pass in current | |
$datatables[$r] = call_user_func($row_data_filter, $datatables[$r]); | |
} | |
// pass entire row, or one cell at a time? | |
// one cell. | |
// $row_data_filter = '', $complete_row_filter = '' | |
// overview classes | |
$row_class = ''; | |
if( isset($datatables[$r]['total_classes']) && isset($inactive_row_classes[$datatables[$r]['total_classes']]) ){ | |
$row_class = $inactive_row_classes[$datatables[$r]['total_classes']]; | |
} | |
$html .= '<tr class="'.trim($row_class.' '.$tr_class).'" >'; | |
// loop through columns - cells. | |
for($c=0;$c<$max_cols;$c++){ | |
// make sure val exists, if not, add blank. | |
$cell_val = (isset($datatables[$r][$cols[$c]])) ? $datatables[$r][$cols[$c]] : ''; | |
$td_class = ''; | |
if($td_class_filter && is_callable($td_class_filter)){ | |
// pass key, val, to get td class | |
$td_class = call_user_func($td_class_filter, $td_class, $cols[$c], $cell_val); | |
} | |
// add label to td class to highlight it | |
$label_class = ($is_pricing_table && $cell_val && detect_dom_radio_checked($cell_val)) ? 'label' : ''; | |
$html .= '<td class="'.trim( $cols[$c].' '.$label_class.' '.$td_class ).'">'.$cell_val.'</td>'; | |
} | |
$html .= '</tr>'; | |
} | |
$html .= '</tbody>'; | |
// <table> before function to help clarify for css | |
return $html; | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment