con = new mysqli($host, $un, $pw, $db); if(mysqli_connect_errno()) throw new Exception(mysqli_connect_error()); $stmt = null; $rs = null; $this->sql = $sql; } public function query($offset, $numrows) { if($this->rs != null) $this->rs->free(); if($this->stmt != null) $this->stmt->close(); if($this->stmt = $this->con->prepare("$this->sql LIMIT $offset,$numrows")) { $this->stmt->execute(); $this->rs = $this->stmt->get_result(); } else { throw new Exception($this->con->error); } } public function getRow() { return $this->rs->fetch_array(MYSQLI_ASSOC); } public function close() { if($this->rs != null) $this->rs->free(); if($this->stmt != null) $this->stmt->close(); $this->con->close(); } } /** * DataFetcher class for PDO. */ abstract class PDODataFetcher implements DataFetcher { private $con; private $stmt; private $sql; private $params; /** * Constructor. * @param string $constr connection string * @param string $un username * @param string $pw password * @param string $sql base SQL statement (will be modified before execution) * @param array $params parameters for SQL statement * @throws Exception */ public function __construct($constr, $un, $pw, $sql, $params) { $this->con = new PDO($constr, $un, $pw); $this->con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt = null; $rs = null; $this->sql = $sql; $this->params = $params; } public function query($offset, $numrows) { if($this->stmt != null) $this->stmt->closeCursor(); $this->stmt = $this->con->prepare($this->convertSQL($this->sql, $offset, $numrows)); $this->stmt->execute($this->params); } public function getRow() { return $this->stmt->fetch(PDO::FETCH_ASSOC); } public function close() { if($this->stmt != null) $this->stmt->closeCursor(); } /** * Convert SQL for pagination. * @param string $sql base SQL statement * @param integer $offset row offset (0 based) * @param integer $numrows number of rows * @return string modified SQL statement */ protected abstract function convertSQL($sql, $offset, $numrows); } /** * DataFetcher class for PDO with MySQL. */ class PDOmysqlDataFetcher extends PDODataFetcher { /** * Constructor. * @param string $host server * @param string $un username * @param string $pw password * @param string $db database * @param string $sql base SQL statement (will be modified before execution) * @param array $params parameters for SQL statement * @throws Exception */ public function __construct($host, $un, $pw, $db, $sql, $params) { parent::__construct("mysql:host=$host;dbname=$db", $un, $pw, $sql, $params); } protected function convertSQL($sql, $offset, $numrows) { return "$sql LIMIT $offset,$numrows"; } } /** * DataFetcher class for PDO with SQLServer. */ class PDOsqlsrvDataFetcher extends PDODataFetcher { /** * Constructor. * @param string $host server * @param string $un username * @param string $pw password * @param string $db database * @param string $sql base SQL statement (will be modified before execution) * @param array $params parameters for SQL statement * @throws Exception */ public function __construct($host, $un, $pw, $db, $sql, $params) { parent::__construct("sqlsrv:server=$host;database=$db", $un, $pw, $sql, $params); } protected function convertSQL($sql, $offset, $numrows) { return "SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rownum,x.* FROM ($sql) x) y WHERE rownum BETWEEN ($offset+1) AND ($offset+$numrows)"; } } /** * Data paginator class. */ class DataPaginator { private $df; private $pgsiz; private $first; private $last; private $data; private $fnd1more; private function update() { $this->df->query($this->first - 1, $this->last - $this->first + 2); $this->data = array(); while((count($this->data) < $this->pgsiz) && ($row = $this->df->getRow())) { $this->data[] = $row; } $extrarow = $this->df->getRow(); $this->fnd1more = is_array($extrarow); } /** * Constructor. * @param object $df data fetcher * @param integer $pgsiz page size * @param integer $start start row number (1 based) */ public function __construct($df, $pgsiz, $start = 1) { $this->df = $df; $this->pgsiz = $pgsiz; $this->first = $start; $this->last = $this->first + $this->pgsiz - 1; $this->update(); } /** * Get data. * @return array indexed array where each element is a row and row is an associative array where each element is a field with field name as index and field value as value */ public function getData() { return $this->data; } /** * Get first row of current page. * @return integer row number (1 based) */ public function getFirst() { return $this->first; } /** * Get last row of current page. * @return integer row number (1 based) */ public function getLast() { return min($this->last, $this->first + count($this->data) - 1); } /** * Get page size. * @return integer page size */ public function getPageSize() { return $this->pgsiz; } /** * Does previous page exist. * @return boolean */ public function hasPrev() { return $this->first > 1; } /** * Does next page exist. * @return boolean */ public function hasNext() { return $this->fnd1more; } /** * Go to next page. */ public function next() { $this->first += $this->pgsiz; $this->last = $this->first + $this->pgsiz - 1; $this->update(); } /** * Go to previous page. */ public function prev() { $this->first -= $this->pgsiz; $this->last = $this->first + $this->pgsiz - 1; $this->update(); } } /** * Visual paginator class. */ abstract class VisualPaginator { private $dp; private $url; /** * Constructor. * @param object $dp data paginator * @param string $url URL to use for previous/next links (usually same URL as current page) */ public function __construct($dp, $url) { $this->dp = $dp; $this->url = $url; } /** * Display paginated data. */ public function display() { $this->displayHeader($this->dp->getFirst(), $this->dp->getLast()); foreach($this->dp->getData() as $row) { $this->displayRow($row); } $this->displayFooter($this->url, $this->dp->hasPrev(), $this->dp->getFirst(), $this->dp->hasNext(), $this->dp->getLast(), $this->dp->getPageSize()); } /** * Display header. * @param integer $first first row number on page (1 based) * @param integer $last last row number on page (1 based) */ protected abstract function displayHeader($first, $last); /** * Display single row. * @param array $row associative array where each element is a field with field name as index and field value as value */ protected abstract function displayRow($row); /** * Display footer. * @param string $url URL to use for previous/next links (usually same URL as current page) * @param boolean $hasprev does previous page exist * @param integer $first first row number on page (1 based) * @param boolean $hasnext does next page exist * @param integer $last last row number on page (1 based) * @param integer $pgsiz page size */ protected abstract function displayFooter($url, $hasprev, $first, $hasnext, $last, $pgsiz); } /** * Table visual paginator class (display data in HTML table). */ abstract class TableVisualPaginator extends VisualPaginator { private $desc; private $colhdr; private function spacer($attr) { return (strlen($attr) > 0) ? (' ' . $attr) : ''; } /** * Constructor. * @param object $dp data paginator * @param string $url URL to use for previous/next links (usually same URL as current page) * @param string $desc descriptive text used in header * @param array $colhdr array with column header names */ public function __construct($dp, $url, $desc, $colhdr) { parent::__construct($dp, $url); $this->desc = $desc; $this->colhdr = $colhdr; } protected function displayHeader($first, $last) { echo $this->getHeader($this->desc, $first, $last); echo "spacer($this->getTableAttr()) . ">\r\n"; echo "spacer($this->getTrAttr()) . ">\r\n"; foreach($this->colhdr as $ch) { echo "spacer($this->getThAttr()) . ">$ch\r\n"; } echo "\r\n"; } protected function displayRow($row) { echo "spacer($this->getTrAttr()) . ">\r\n"; foreach($row as $f => $v) { echo "spacer($this->getTdAttr()) . ">$v\r\n"; } foreach($this->getExtraCols() as $cb) { echo "spacer($this->getTdAttr()) . ">" . call_user_func($cb, $row) . "\r\n"; } echo "\r\n"; } protected function displayFooter($url, $hasprev, $first, $hasnext, $last, $pgsiz) { echo "\r\n"; echo $this->getFooter($url, $hasprev, $first, $hasnext, $last, $pgsiz); } /** * Get header string. * @param string $desc descriptive text * @param integer $first first row number on page (1 based) * @param integer $last last row number on page (1 based) * @return string header */ protected abstract function getHeader($desc, $first, $last); /** * Get footer string. * @param string $url URL to use for previous/next links (usually same URL as current page) * @param boolean $hasprev does previous page exist * @param integer $first first row number on page (1 based) * @param boolean $hasnext does next page exist * @param integer $last last row number on page (1 based) * @param integer $pgsiz page size * @return footer */ protected abstract function getFooter($url, $hasprev, $first, $hasnext, $last, $pgsiz); /** * Get attributes on table tag. * @return string attributes */ protected abstract function getTableAttr(); /** * Get attributes on tr tag. * @return string attributes */ protected abstract function getTrAttr(); /** * Get attributes on th tag. * @return string attributes */ protected abstract function getThAttr(); /** * Get attributes on td tag. * @return string attributes */ protected abstract function getTdAttr(); /** * Get extra columns to add. * @return array array with column values */ protected abstract function getExtraCols(); } /** * List visual paginator class (display data in HTML list). */ abstract class ListVisualPaginator extends VisualPaginator { private $desc; private function spacer($attr) { return (strlen($attr) > 0) ? (' ' . $attr) : ''; } /** * Constructor. * @param object $dp data paginator * @param string $url URL to use for previous/next links (usually same URL as current page) * @param string $desc descriptive text used in header */ public function __construct($dp, $url, $desc) { parent::__construct($dp, $url); $this->desc = $desc; } protected function displayHeader($first, $last) { echo $this->getHeader($this->desc, $first, $last); echo "spacer($this->getUlAttr()) . ">\r\n"; } protected function displayRow($row) { echo "spacer($this->getLiAttr()) . ">\r\n"; echo $this->getBody($row); echo "\r\n"; } protected function displayFooter($url, $hasprev, $first, $hasnext, $last, $pgsiz) { echo "\r\n"; echo $this->getFooter($url, $hasprev, $first, $hasnext, $last, $pgsiz); } /** * Get header string. * @param string $desc descriptive text * @param integer $first first row number on page (1 based) * @param integer $last last row number on page (1 based) * @return string header */ protected abstract function getHeader($desc, $first, $last); /** * Get footer string. * @param string $url URL to use for previous/next links (usually same URL as current page) * @param boolean $hasprev does previous page exist * @param integer $first first row number on page (1 based) * @param boolean $hasnext does next page exist * @param integer $last last row number on page (1 based) * @param integer $pgsiz page size * @return footer */ protected abstract function getFooter($url, $hasprev, $first, $hasnext, $last, $pgsiz); /** * Get attributes on ul tag. * @return string attributes */ protected abstract function getUlAttr(); /** * Get attributes on li tag. * @return string attributes */ protected abstract function getLiAttr(); /** * Get representation of one row. * @param array $row associative array where each element is a field with field name as index and field value as value */ protected abstract function getBody($row); } /** * Standard header and footer trait. */ trait StandardHeaderFooter { /** * Get header string. * @param string $desc descriptive text * @param integer $first first row number on page (1 based) * @param integer $last last row number on page (1 based) * @return string header */ public function getHeader($desc, $first, $last) { return "$desc $first - $last:\r\n"; } private function getUrl($url, $start) { return "$url?start=$start"; } private function getLink($text, $url, $start) { return "$text"; } /** * Get footer string. * @param string $url URL to use for previous/next links (usually same URL as current page) * @param boolean $hasprev does previous page exist * @param integer $first first row number on page (1 based) * @param boolean $hasnext does next page exist * @param integer $last last row number on page (1 based) * @param integer $pgsiz page size * @return footer */ public function getFooter($url, $hasprev, $first, $hasnext, $last, $pgsiz) { return ($hasprev ? $this->getLink('Previous', $url, $first - $pgsiz) : '') . ' ' . ($hasnext ? $this->getLink('Next', $url, $last + 1) : '') . "\r\n"; } } /** * Simple table visual paginator class (just a plain table with no extras). */ class SimpleTableVisualPaginator extends TableVisualPaginator { use StandardHeaderFooter; /** * Constructor. * @param object $dp data paginator * @param string $url URL to use for previous/next links (usually same URL as current page) * @param string $desc descriptive text used in header * @param array $colhdr array with column header names */ public function __construct($dp, $url, $desc, $colhdr) { parent::__construct($dp, $url, $desc, $colhdr); } protected function getTableAttr() { return "border='1'"; } protected function getTrAttr() { return ''; } protected function getThAttr() { return ''; } protected function getTdAttr() { return ''; } protected function getExtraCols() { return array(); } } /** * Simple list visual paginator class (just a plain list with no extras). */ class SimpleListVisualPaginator extends ListVisualPaginator { use StandardHeaderFooter; public function __construct($dp, $url, $desc) { parent::__construct($dp, $url, $desc); } protected function getUlAttr() { return ''; } protected function getLiAttr() { return ''; } protected function getBody($row) { $res = ''; foreach($row as $f => $v) { $res .= "$v\r\n"; } return $res; } } define('NOFLD',''); define('NOFUNC', null); /** * Advanced table visual paginator class (table with classes on tags and extra columns with edit and delete links). */ class AdvancedTableVisualPaginator extends TableVisualPaginator { use StandardHeaderFooter; private $clz; private $idfld; private $editurl; private $delurl; private function getClassAttr($elm) { if(array_key_exists($elm, $this->clz)) { return "class='" . $this->clz[$elm] . "'"; } else { return ''; } } /** * Constructor. * @param object $dp data paginator * @param string $url URL to use for previous/next links (usually same URL as current page) * @param string $desc descriptive text used in header * @param array $colhdr array with column header names * @param array $clz associative array where each element is a class attribute with tag name as index and class name as value * @param string $idfld name of field to use as is for edit and delete * @param string $editurl URL to use for edit links * @param string $editurl URL to use for delete links */ public function __construct($dp, $url, $desc, $colhdr, $clz, $idfld = NOFLD, $editurl = NOFUNC, $delurl = NOFUNC) { parent::__construct($dp, $url, $desc, $colhdr); $this->clz = $clz; $this->idfld = $idfld; $this->editurl = $editurl; $this->delurl = $delurl; } protected function getTableAttr() { $clzattr = $this->getClassAttr('table'); if(strlen($clzattr) > 0) { return "border='1' $clzattr"; } else { return "border='1'"; } } protected function getTrAttr() { return $this->getClassAttr('tr'); } protected function getThAttr() { return $this->getClassAttr('th'); } protected function getTdAttr() { return $this->getClassAttr('td'); } protected function getExtraCols() { $res = array(); if(($this->idfld != NOFLD) && ($this->editurl != NOFUNC)) { $res[] = array($this, 'getEditLink'); } if(($this->idfld != NOFLD) && ($this->delurl != NOFUNC)) { $res[] = array($this, 'getDelLink'); } return $res; } protected function getEditLink($row) { return "Edit"; } protected function getDelLink($row) { return "Delete"; } } /** * Advanced list visual paginator class (list with classes on tags and data formatted via template). */ class AdvancedListVisualPaginator extends ListVisualPaginator { use StandardHeaderFooter; private $clz; private $tmpl; private function getClassAttr($elm) { if(array_key_exists($elm, $this->clz)) { return "class='" . $this->clz[$elm] . "'"; } else { return ''; } } /** * Constructor. * @param object $dp data paginator * @param string $url URL to use for previous/next links (usually same URL as current page) * @param string $desc descriptive text used in header * @param array $colhdr array with column header names * @param array $clz associative array where each element is a class attribute with tag name as index and class name as value * @param strint $tmpl template for displaying a row (colon plus field name get replaced with field value) */ public function __construct($dp, $url, $desc, $clz, $tmpl) { parent::__construct($dp, $url, $desc); $this->clz = $clz; $this->tmpl = $tmpl; } protected function getUlAttr() { return $this->getClassAttr('ul'); } protected function getLiAttr() { return $this->getClassAttr('li'); } protected function getBody($row) { $res = $this->tmpl; foreach($row as $f => $v) { $res = str_replace(':' . $f, $v, $res); } return $res; } } ?>