Db_pager widget for paged/sorted sql query.
authorJanusz Dobrowolski <janusz@frontaccounting.eu>
Tue, 4 Nov 2008 12:33:02 +0000 (12:33 +0000)
committerJanusz Dobrowolski <janusz@frontaccounting.eu>
Tue, 4 Nov 2008 12:33:02 +0000 (12:33 +0000)
includes/db_pager.inc [new file with mode: 0644]
includes/ui/db_pager_view.inc [new file with mode: 0644]

diff --git a/includes/db_pager.inc b/includes/db_pager.inc
new file mode 100644 (file)
index 0000000..ce73953
--- /dev/null
@@ -0,0 +1,363 @@
+<?php
+//
+//     Controler part of database table pager with column sort.
+//     To display actual html object call display_db_pager($name) inside
+//  any form.
+//
+//     View definition you will find in the following file:
+include_once($path_to_root."/includes/ui/db_pager_view.inc");
+
+class db_pager {
+       var $sql;
+       var $name;
+       var $columns;           // column definitions (head, type, order)
+
+       var $marker;            // marker check function
+       var $marker_txt;        
+       var $marker_class;
+       var $notice_class;
+                       
+       var $data = array();
+
+       var $curr_page,
+               $max_page,
+           $last_page, 
+           $prev_page, 
+           $next_page,
+           $first_page;
+           
+       var $page_len,
+           $rec_count;
+       
+       var $select,
+               $where,
+           $from,
+               $group;
+       var     $extra_where;
+       
+       var $ready = false;
+
+       //  db_pager constructor
+       //  accepts $sql like 'SELECT .... FROM .... [WHERE ...] [GROUP ...]'
+       //      $name is base name for pager controls
+       function db_pager($sql, $name, $page_len=0) 
+       {
+               if ($page_len == 0) $page_len = user_query_size();
+               $this->name = $name;
+               $this->page_len = $page_len;
+               $this->set_sql($sql);
+       }
+       //
+       //      Parse base sql select query.
+       //
+       function set_sql($sql)
+       {
+               if ($sql != $this->sql) {
+                   $this->sql = $sql;
+                   $this->ready = false;
+                       $select = $sql;
+                       $from = $where = $group = '';
+               $parts = preg_split('/\sFROM\s/si', $sql, 2);
+                       if(count($parts) == 2) {
+                               $select = $parts[0];
+                               $from = $parts[1];
+                       $parts = preg_split('/\sWHERE\s/si', $from, 2);
+                               if(count($parts) == 2) {
+                                       $from = $parts[0];
+                                       $where = $parts[1];
+                               $parts = preg_split('/\sGROUP\s*BY\s/si', $where, 2);
+                                       if(count($parts) == 2) {
+                                               $where = $parts[0];
+                                               $group = $parts[1];
+                                       }
+                               }
+                       }
+                       $this->select = $select;
+                       $this->from = $from;
+                       $this->where = $where;
+                       $this->group = $group;
+/*
+               display_error("sql: $sql");
+               display_error($select);
+               display_error("FROM $from");
+               display_error("WHERE $where");
+               display_error("GROUP BY $group");
+*/
+               }
+       }
+       //
+       //      Set additional constraint on record set
+       //
+       function set_where($where) 
+       {
+           if (!is_array($where))
+                 $where = array($where);
+
+           if (count($where) != count($this->extra_where) || 
+                       count(array_diff($this->extra_where, $where))) {
+                               $this->extra_where = $where;
+                               $this->ready = false;
+           }
+       }
+       //
+       //      Set query result page
+       //
+       function change_page($page=null) 
+       {
+           $this->set_page($page);
+           $this->query();
+           return true;
+       }
+       //
+       //      Change sort column direction 
+       //      in order asc->desc->none->asc
+       //
+       function sort_table($col=null) 
+       {
+           $ord = $this->columns[$col]['ord'];
+           $ord = ($ord == '') ? 'asc' : (($ord == 'asc') ? 'desc' : '');
+           $this->columns[$col]['ord'] = $ord;
+           $this->set_page(1);
+           $this->query();
+           return true;
+       }
+       //
+       // Query database
+       //
+       function query() 
+       {
+               global $Ajax;
+
+               $Ajax->activate("_{$this->name}_span");
+           $this->data = array();
+           if (!$this->_init()) 
+                 return false;
+
+           if ($this->rec_count == 0) return true;
+
+           $sql = $this->_sql_gen(false);
+
+           $result = db_query($sql, 'Error browsing database: '.$sql );
+
+           if ($result) {
+               // setting field names for subsequent queries
+                       $c = 0;
+                 // add result field names to column defs for 
+                 // col value retrieve and sort purposes 
+                       for ($c = $i = 0; $c < count($this->columns); $c++) {
+                               if ($this->columns[$c]['type'] != 'insert')
+                                       $this->columns[$c]['name']= mysql_field_name($result, $i++);
+                       }
+                 
+                       while ($row = db_fetch_assoc($result))
+                               $this->data[] = $row;
+                 
+               } else 
+                 return false;
+               return true;
+       }           
+       //
+       //      Calculates page numbers for html controls.
+       //
+       function set_page($to) 
+       {
+           switch($to) {
+               case 'next':
+                   $page = $this->curr_page+1; break;
+               case 'prev':
+                   $page = $this->curr_page-1; break;
+               case 'last':
+                   $page = $this->last_page; break;
+               default:
+                   if (is_numeric($to)) {
+                        $page = $to; break;
+                   }
+               case 'first':
+                   $page = 1; break;
+           }
+         if ($page < 1) 
+           $page = 1;
+         $max = $this->max_page;
+         if ($page > $max) 
+           $page = $max;
+         $this->curr_page = $page;
+         $this->next_page = ($page < $max) ? $page+1 : null;
+         $this->prev_page = ($page > 1) ? ($page-1) : null;
+         $this->last_page = ($page < $max) ? $max : null;
+         $this->first_page = ($page != 1) ? 1: null;
+       }
+       //
+       //      Set column definitions
+       //  $flds: array( fldname1, fldname2=>type,...)
+       function set_columns($flds)
+       {
+               $this->columns = array();
+               if (!is_array($flds)) {
+                       $flds = array($flds);
+               }
+               foreach ($flds as $colnum=>$coldef) {
+                       if (!is_numeric($colnum)) {     // 'colname'=>params
+                         $h = $colnum;
+                         $c = $coldef;
+                       } else {                        //  n=>params
+                               if (is_array($coldef)) {
+                                       $h = '';
+                                       $c = $coldef;
+                               } else {
+                                       $h = $coldef;
+                                       $c = 'text';
+                               }
+                         $c = '';
+                       }
+                       if (!is_array($c))                      // params is simple column type
+                         $c = array('type'=>$c);
+
+                       if (!isset($c['type']))
+                         $c['type'] = 'text';
+
+                       switch($c['type']) {
+                         case 'insert':
+                         default:
+                               $c['head'] = $h; break;
+                         case 'skip':          // skip the column (no header)
+                               unset($c['head']);      // paranoid code
+                       }
+                       $this->columns[] = $c;  
+               }
+       }
+       //
+       // Generate db query from base sql
+       // $count==false - for current page data retrieval 
+       // $count==true  - for total records count
+       //
+       function _sql_gen($count=false) 
+       {
+               $select = $this->select;
+               $from = $this->from;
+               $where = $this->where;
+               $group = $this->group;          
+
+               if(count($this->extra_where)) {
+                   $wherw .= ($where=='' ? '' : ' AND ')
+                               .implode( $this->extra_where, ' AND ');
+               }
+               if ($where) $where = " WHERE($where)";
+
+               if ($count) {
+                       $group = $group == '' ? "*" : "DISTINCT $group";
+
+                       return "SELECT COUNT($group) FROM $from $where";
+               }
+
+               $sql = "$select FROM $from $where GROUP BY $group";
+           $ord = array();
+
+           foreach( $this->columns as $col) {
+               if (isset($col['ord'])) {
+                       if ( $col['ord'] != '' && isset($col['name'])) {
+                           $ord[] = $col['name'] .' '. $col['ord'];
+                           }
+                       }
+           }
+                               
+           if (count($ord)) 
+                       $sql .= " ORDER BY " . implode($ord, ',');
+
+           $page_len = $this->page_len;
+           $offset = ($this->curr_page - 1) * $page_len;
+
+           $sql .= " LIMIT $offset, $page_len";
+
+               return $sql;
+               
+       }
+       //
+       //      Initialization after changing record set
+       //
+       function _init() 
+       {
+           if ($this->ready == false ) {
+                       $sql = $this->_sql_gen(true);
+                       $result = db_query($sql, 'Error reading record set');
+                       if ($result == false) 
+                               return false;
+                       $row = db_fetch_row($result);
+                       $this->rec_count = $row[0];
+                       $this->max_page = ceil($this->rec_count/$this->page_len);
+                       $this->set_page(1);
+                       $this->ready = true;
+           }
+       return true;
+       }
+       //
+       //      Set current page in response to user control.
+       //
+       function select_records() 
+       {
+               global $Ajax;
+               
+               $page = find_submit($this->name.'_page_', false);
+               $sort = find_submit($this->name.'_sort_', false);
+               if ($page) {
+                       $this->change_page($page);
+               } elseif ($sort) {
+                       $this->sort_table($sort);
+               } else
+                       $this->query();
+       }
+       //
+       //      Set check function to mark some rows.
+       //      
+       function set_marker($func, $notice='', $markercl='overduebg', $msgclass='overduefg' )
+       {
+               $this->marker = $func;
+               $this->marker_txt = $notice;
+               $this->marker_class = $markercl;
+               $this->notice_class = $msgclass;
+       }
+};
+//-----------------------------------------------------------------------------
+//     Creates new db_pager $_SESSION object on first page call.
+//  Retrieves from $_SESSION var on subsequent $_POST calls
+//
+//  $name - base name for pager controls and $_SESSION object name
+//  $sql  - base sql for data inquiry. Order of fields implies
+//             pager columns order.
+//     $coldef - array of column definitions. Example definitions
+//             Text column with title 'User name':
+//                             'User name'
+//             Skipped field from sql query. Data for the field is not displayed:
+//                             'dummy' => 'skip'
+//             Column without title, formated with function func(). Field value 
+// is passed as parameter:
+//                             array('type'=>'spec', 'fun'=>'func')
+//             Inserted column with title 'Some', formated with function rowfun(). Row
+//             values are passed as parameter array:
+//                             'Some' => array('type'=>'insert', 'fun'=>'rowfun')
+//             Column with name 'Another', formatted as date, 
+// sortable with ascending start order (available orders: asc,desc, '').
+//                             'Another' => array('type'=>'date', 'ord'=>'asc')
+//
+//     All available column format types you will find in db_pager_view.inc file.
+//             If query result has more fields than count($coldef), rest of data is ignored
+//  during display, but can be used in format handlers for 'spec' and 'insert' 
+//     type columns.
+
+function &new_db_pager($name, $sql, $coldef, $page_len = 0)  {
+
+    if (isset($name) && isset($_SESSION[$name])) {
+                // kill old pager if any on first page call
+           if ($_SERVER['REQUEST_METHOD'] == 'GET')
+                       unset($_SESSION[$name]);
+           else
+               return $_SESSION[$name];
+    }
+    $ret =& new db_pager($sql, $name, $page_len);
+    $ret->set_columns($coldef);
+
+    if (isset($name)) $_SESSION[$name] = &$ret;
+
+    return $ret;
+}
+
+?>
\ No newline at end of file
diff --git a/includes/ui/db_pager_view.inc b/includes/ui/db_pager_view.inc
new file mode 100644 (file)
index 0000000..a30487f
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+//--------------------------------------------------------------------------------------------------
+function pager_link($link_text, $url)
+{
+       global $path_to_root;
+       
+       $link = access_string($link_text);
+
+       $href = $path_to_root . $url;
+       return "<a href='$href'$link[1]>" . $link[0] . "</a>";
+}
+
+function navi_button($name, $value, $enabled=true, $icon = false) {
+       global $path_to_root;
+       return "<button ". ($enabled ? '':'disabled')
+               ." class=\"navibutton\" type=\"submit\""
+           ." name=\"$name\"  id=\"$name\" value=\"$value\">"
+               .($icon ? "<img src='$path_to_root/themes/".user_theme()."/images/".$icon."'>":'')
+               ."<span>$value</span></button>\n";
+}
+
+function navi_button_cell($name, $value, $enabled=true, $align='left') {
+       label_cell(navi_button($name, $value, $enabled), "align='$align'");
+}
+//-----------------------------------------------------------------------------
+//
+//    Sql paged table view. Call this function inside form.
+//
+function display_db_pager(&$pager) {
+    global     $table_style, $use_popup_windows, $use_date_picker, $path_to_root;
+
+       $pager->select_records();
+
+       div_start("_{$pager->name}_span");
+    $headers = array();
+//display_error(print_r($pager->columns,true));
+       foreach($pager->columns as $num_col=>$col) {
+       if (isset($col['head'])) {
+                       if (!isset($col['ord'])) 
+                               $headers[] = $col['head'];
+                       else {
+                               $icon = (($col['ord'] == 'desc') ? 'sort_desc.gif' : 
+                                       ($col['ord'] == 'asc' ? 'sort_asc.gif' : 'sort_none.gif'));
+                               $headers[] = navi_button($pager->name.'_sort_'.$num_col, 
+                                       $col['head'], true, $icon);
+                       }
+               }
+       }
+    /* show a table of records returned by the sql */
+    start_table("$table_style width=95%");
+    table_header($headers);
+
+       $cc = 0; //row colour counter
+       foreach($pager->data as $line_no => $row) {     
+
+               $marker = $pager->marker;
+           if ($marker && $marker($row)) 
+               start_row("class='$pager->marker_class'");
+           else        
+                       alt_table_row_color($cc);
+           foreach ($pager->columns as $k=>$col) {
+                  $coltype = $col['type'];
+                  $cell = isset($col['name']) ? $row[$col['name']] : '';
+
+                  switch($coltype) {
+                   case 'date':
+                         label_cell(sql2date($cell), "align='center'"); break;
+                   case 'dstamp':      // time stamp displayed as date
+                         label_cell(sql2date(substr($cell, 0, 10)), "align='center'"); break;
+                   case 'tstamp':      // time stamp - FIX user format
+                         label_cell(sql2date(substr($cell, 0, 10)).
+                         ' '. substr($cell, 10), "align='center'"); break;
+                   case 'percent':
+                         percent_cell($cell); break;
+                   case 'amount':
+                         amount_cell($cell); break;
+                   case 'qty':
+                         qty_cell($cell); break;
+                   case 'rate':
+                         rate_cell($cell);  break;
+                       case 'insert':  // extra inserted column
+                       case 'spec':    // special formatting function
+                               $fun = $col['fun']; 
+                               $param = $coltype=='spec' ? $cell : $row;
+                           if (method_exists($pager, $fun)) { 
+                                       $cell = $pager->$fun($param);
+                               } elseif (function_exists($fun)) {
+                                       $cell = $fun($param);
+                               } else
+                                       $cell = '';
+//                 case 'text':
+                   default:
+                         label_cell($cell);
+                   case 'skip':        // column not displayed
+                 }
+         }
+           end_row();
+       }
+       //end of while loop
+       end_table();
+       start_table("$table_style align='center' class='navibar' width=95%");
+       start_row();
+        if($pager->rec_count) {
+               $from = ($pager->curr_page-1)*$pager->page_len+1;
+               $to = $from + $pager->page_len - 1;
+               if ($to > $pager->rec_count)
+                 $to = $pager->rec_count;
+               $all = $pager->rec_count;
+               label_cell(sprintf( _('Records %d-%d of %d'), $from, $to, $all),
+                       "style='border:none'");
+               echo "<td style='border:none'>";
+               $but_pref = $pager->name.'_page_';
+           start_table("align='right'");
+               start_row();
+               echo navi_button_cell($but_pref.'first', _('First'), $pager->first_page, 'right');
+               echo navi_button_cell($but_pref.'prev', _('Prev'), $pager->prev_page,'right');
+               echo navi_button_cell($but_pref.'next', _('Next'), $pager->next_page,'right');
+               echo navi_button_cell($but_pref.'last', _('Last'), $pager->last_page, 'right');
+               end_row(); 
+               end_table();    
+               echo "</td>";
+       } else {
+         label_cell( _('No records'));
+       }
+       end_row();
+       end_table();
+
+   if (isset($pager->marker_txt))
+               display_note($pager->marker_txt, 0, 1, "class='$pager->notice_class'");
+
+  div_end();
+  return true;
+}
+
+
+?>
\ No newline at end of file