From: avl Date: Mon, 15 Sep 2008 09:49:36 +0000 (+0300) Subject: Pager Improve X-Git-Tag: 0.5~485 X-Git-Url: https://git.andy128k.dev/?a=commitdiff_plain;h=ab5b96f6679cbc850e586d4b503141356abd0ed5;p=ipf.git Pager Improve --- diff --git a/ipf/orm/connection/profiler.php b/ipf/orm/connection/profiler.php index 3c0a340..6230025 100644 --- a/ipf/orm/connection/profiler.php +++ b/ipf/orm/connection/profiler.php @@ -21,7 +21,7 @@ class IPF_ORM_Connection_Profiler implements IPF_ORM_Overloadable, IteratorAggre public function __call($m, $a) { if ( ! ($a[0] instanceof IPF_ORM_Event)) { - throw new IPF_ORM_Exception_Profiler("Couldn't listen event. Event should be an instance of Doctrine_Event."); + throw new IPF_ORM_Exception_Profiler("Couldn't listen event. Event should be an instance of IPF_Event."); } diff --git a/ipf/orm/pager.php b/ipf/orm/pager.php new file mode 100644 index 0000000..bf143d0 --- /dev/null +++ b/ipf/orm/pager.php @@ -0,0 +1,255 @@ +_setExecuted(false); + + $this->_setQuery($query); + $this->_setPage($page); + + $this->setMaxPerPage($maxPerPage); + } + + protected function _initialize($params = array()) + { + // retrieve the number of items found + $count = $this->getCountQuery()->count($this->getCountQueryParams($params)); + + $this->_setNumResults($count); + $this->_setExecuted(true); // _adjustOffset relies of _executed equals true = getNumResults() + + $this->_adjustOffset(); + } + + protected function _adjustOffset() + { + // Define new total of pages + $this->_setLastPage( + max(1, ceil($this->getNumResults() / $this->getMaxPerPage())) + ); + $offset = ($this->getPage() - 1) * $this->getMaxPerPage(); + + // Assign new offset and limit to IPF_ORM_Query object + $p = $this->getQuery(); + $p->offset($offset); + $p->limit($this->getMaxPerPage()); + } + + public function getExecuted() + { + return $this->_executed; + } + + protected function _setExecuted($executed) + { + $this->_executed = $executed; + } + + public function getRange($rangeStyle, $options = array()) + { + $class = 'IPF_ORM_Pager_Range_' . ucfirst($rangeStyle); + return new $class($options, $this); + } + + public function getNumResults() + { + if ($this->getExecuted()) { + return $this->_numResults; + } + throw new IPF_ORM_Exception( + 'Cannot retrieve the number of results of a not yet executed Pager query' + ); + } + + protected function _setNumResults($nb) + { + $this->_numResults = $nb; + } + + public function getFirstPage() + { + return 1; + } + + public function getLastPage() + { + if ($this->getExecuted()) { + return $this->_lastPage; + } + + throw new IPF_ORM_Exception( + 'Cannot retrieve the last page number of a not yet executed Pager query' + ); + } + + protected function _setLastPage($page) + { + $this->_lastPage = $page; + + if ($this->getPage() > $page) { + $this->_setPage($page); + } + } + + public function getPage() + { + return $this->_page; + } + + public function getNextPage() + { + if ($this->getExecuted()) { + return min($this->getPage() + 1, $this->getLastPage()); + } + + throw new IPF_ORM_Exception( + 'Cannot retrieve the last page number of a not yet executed Pager query' + ); + } + + public function getPreviousPage() + { + if ($this->getExecuted()) { + return max($this->getPage() - 1, $this->getFirstPage()); + } + + throw new IPF_ORM_Exception( + 'Cannot retrieve the previous page number of a not yet executed Pager query' + ); + } + + public function getFirstIndice() + { + return ($this->getPage() - 1) * $this->getMaxPerPage() + 1; + } + + public function getLastIndice() + { + return min($this->getNumResults(), ($this->getPage() * $this->getMaxPerPage())); + } + + public function haveToPaginate() + { + if ($this->getExecuted()) { + return $this->getNumResults() > $this->getMaxPerPage(); + } + + throw new IPF_ORM_Exception( + 'Cannot know if it is necessary to paginate a not yet executed Pager query' + ); + } + + public function setPage($page) + { + $this->_setPage($page); + $this->_setExecuted(false); + } + + private function _setPage($page) + { + $page = intval($page); + $this->_page = ($page <= 0) ? 1 : $page; + } + + public function getMaxPerPage() + { + return $this->_maxPerPage; + } + + public function setMaxPerPage($max) + { + if ($max > 0) { + $this->_maxPerPage = $max; + } else if ($max == 0) { + $this->_maxPerPage = 25; + } else { + $this->_maxPerPage = abs($max); + } + + $this->_setExecuted(false); + } + + public function getResultsInPage() + { + $page = $this->getPage(); + + if ($page != $this->getLastPage()) { + return $this->getMaxPerPage(); + } + + $offset = ($this->getPage() - 1) * $this->getMaxPerPage(); + + return abs($this->getNumResults() - $offset); + } + + public function getQuery() + { + return $this->_query; + } + + protected function _setQuery($query) + { + if (is_string($query)) { + $query = IPF_ORM_Query::create()->parseQuery($query); + } + + $this->_query = $query; + } + + public function getCountQuery() + { + return ($this->_countQuery !== null) ? $this->_countQuery : $this->_query; + } + + public function setCountQuery($query, $params = null) + { + if (is_string($query)) { + $query = IPF_ORM_Query::create()->parseQuery($query); + } + + $this->_countQuery = $query; + + $this->setCountQueryParams($params); + + $this->_setExecuted(false); + } + + public function getCountQueryParams($defaultParams = array()) + { + return ($this->_countQueryParams !== null) ? $this->_countQueryParams : $defaultParams; + } + + public function setCountQueryParams($params = array(), $append = false) + { + if ($append && is_array($this->_countQueryParams)) { + $this->_countQueryParams = array_merge($this->_countQueryParams, $params); + } else { + if ($params !== null && !is_array($params)) { + $params = array($params); + } + + $this->_countQueryParams = $params; + } + + $this->_setExecuted(false); + } + + public function execute($params = array(), $hydrationMode = IPF_ORM::FETCH_RECORD) + { + if (!$this->getExecuted()) { + $this->_initialize($params); + } + return $this->getQuery()->execute($params, $hydrationMode); + } +} \ No newline at end of file diff --git a/ipf/orm/pager/layout.php b/ipf/orm/pager/layout.php new file mode 100644 index 0000000..1d83ebb --- /dev/null +++ b/ipf/orm/pager/layout.php @@ -0,0 +1,228 @@ +_setPager($pager); + $this->_setPagerRange($pagerRange); + $this->_setUrlMask($urlMask); + + $this->setTemplate('[{%page}]'); + $this->setSelectedTemplate(''); + $this->setSeparatorTemplate(''); + } + + public function getPager() + { + return $this->_pager; + } + + protected function _setPager($pager) + { + $this->_pager = $pager; + } + + public function execute($params = array(), $hydrationMode = IPF_ORM::FETCH_RECORD) + { + return $this->getPager()->execute($params, $hydrationMode); + } + + public function getPagerRange() + { + return $this->_pagerRange; + } + + protected function _setPagerRange($pagerRange) + { + $this->_pagerRange = $pagerRange; + $this->getPagerRange()->setPager($this->getPager()); + } + + public function getUrlMask() + { + return $this->_urlMask; + } + + protected function _setUrlMask($urlMask) + { + $this->_urlMask = $urlMask; + } + + public function getTemplate() + { + return $this->_template; + } + + public function setTemplate($template) + { + $this->_template = $template; + } + + public function getSelectedTemplate() + { + return $this->_selectedTemplate; + } + + public function setSelectedTemplate($selectedTemplate) + { + $this->_selectedTemplate = $selectedTemplate; + } + + public function getSeparatorTemplate() + { + return $this->_separatorTemplate; + } + + public function setSeparatorTemplate($separatorTemplate) + { + $this->_separatorTemplate = $separatorTemplate; + } + + public function addMaskReplacement($oldMask, $newMask, $asValue = false) + { + if (($oldMask = trim($oldMask)) != 'page_number') { + $this->_maskReplacements[$oldMask] = array( + 'newMask' => $newMask, + 'asValue' => ($asValue === false) ? false : true + ); + } + } + + public function removeMaskReplacement($oldMask) + { + if (isset($this->_maskReplacements[$oldMask])) { + $this->_maskReplacements[$oldMask] = null; + unset($this->_maskReplacements[$oldMask]); + } + } + + public function cleanMaskReplacements() + { + $this->_maskReplacements = null; + $this->_maskReplacements = array(); + } + + public function display($options = array(), $return = true) + { + $range = $this->getPagerRange()->rangeAroundPage(); + $str = ''; + + // For each page in range + for ($i = 0, $l = count($range); $i < $l; $i++) { + // Define some optional mask values + $options['page_number'] = $range[$i]; + + $str .= $this->processPage($options); + + // Apply separator between pages + if ($i < $l - 1) { + $str .= $this->getSeparatorTemplate(); + } + } + + // Possible wish to return value instead of print it on screen + if ($return) { + return $str; + } + + echo $str; + } + + public function processPage($options = array()) + { + // Check if at least basic options are defined + if (!isset($options['page_number'])) { + throw new IPF_ORM_Exception( + 'Cannot process template of the given page. ' . + 'Missing at least one of needed parameters: \'page\' or \'page_number\'' + ); + + // Should never reach here + return ''; + } + + // Assign "page" options index if not defined yet + if (!isset($this->_maskReplacements['page']) && !isset($options['page'])) { + $options['page'] = $options['page_number']; + } + + return $this->_parseTemplate($options); + } + + public function __toString() + { + return $this->display(array(), true); + } + + protected function _parseTemplate($options = array()) + { + $str = $this->_parseUrlTemplate($options); + $replacements = $this->_parseReplacementsTemplate($options); + + return strtr($str, $replacements); + } + + protected function _parseUrlTemplate($options = array()) + { + $str = ''; + + // If given page is the current active one + if ($options['page_number'] == $this->getPager()->getPage()) { + $str = $this->_parseMaskReplacements($this->getSelectedTemplate()); + } + + // Possible attempt where Selected == Template + if ($str == '') { + $str = $this->_parseMaskReplacements($this->getTemplate()); + } + + return $str; + } + + protected function _parseReplacementsTemplate($options = array()) + { + // Defining "url" options index to allow {%url} mask + $options['url'] = $this->_parseUrl($options); + + $replacements = array(); + + foreach ($options as $k => $v) { + $replacements['{%'.$k.'}'] = $v; + } + + return $replacements; + } + + protected function _parseUrl($options = array()) + { + $str = $this->_parseMaskReplacements($this->getUrlMask()); + + $replacements = array(); + + foreach ($options as $k => $v) { + $replacements['{%'.$k.'}'] = $v; + } + + return strtr($str, $replacements); + } + + protected function _parseMaskReplacements($str) + { + $replacements = array(); + + foreach ($this->_maskReplacements as $k => $v) { + $replacements['{%'.$k.'}'] = ($v['asValue'] === true) ? $v['newMask'] : '{%'.$v['newMask'].'}'; + } + + return strtr($str, $replacements); + } +} diff --git a/ipf/orm/pager/range.php b/ipf/orm/pager/range.php new file mode 100644 index 0000000..18ffd8f --- /dev/null +++ b/ipf/orm/pager/range.php @@ -0,0 +1,61 @@ +_setOptions($options); + + if ($pager !== null) { + $this->setPager($pager); + } + } + + public function getPager() + { + return $this->pager; + } + + public function setPager($pager) + { + $this->pager = $pager; + + // Lazy-load initialization. It only should be called when all + // needed information data is ready (this can only happens when we have + // options stored and a IPF_ORM_Pager assocated) + $this->_initialize(); + } + + public function getOptions() + { + return $this->_options; + } + + public function getOption($option) + { + if (isset($this->_options[$option])) { + return $this->_options[$option]; + } + + throw new IPF_ORM_Exception( + 'Cannot access unexistent option \'' . $option . '\' in IPF_ORM_Pager_Range class' + ); + } + + protected function _setOptions($options) + { + $this->_options = $options; + } + + public function isInRange($page) + { + return (array_search($page, $this->rangeAroundPage()) !== false); + } + + abstract protected function _initialize(); + + abstract public function rangeAroundPage(); +} diff --git a/ipf/orm/pager/range/jumping.php b/ipf/orm/pager/range/jumping.php new file mode 100644 index 0000000..4324921 --- /dev/null +++ b/ipf/orm/pager/range/jumping.php @@ -0,0 +1,51 @@ +_options['chunk'])) { + $this->_setChunkLength($this->_options['chunk']); + } else { + throw new IPF_ORM_Exception('Missing parameter \'chunk\' that must be define in options.'); + } + } + + public function getChunkLength() + { + return $this->_chunkLength; + } + + protected function _setChunkLength($chunkLength) + { + $this->_chunkLength = $chunkLength; + } + + public function rangeAroundPage() + { + $pager = $this->getPager(); + + if ($pager->getExecuted()) { + $page = $pager->getPage(); + + // Define initial assignments for StartPage and EndPage + $startPage = $page - ($page - 1) % $this->getChunkLength(); + $endPage = ($startPage + $this->getChunkLength()) - 1; + + // Check for EndPage out-range + if ($endPage > $pager->getLastPage()) { + $endPage = $pager->getLastPage(); + } + + // No need to check for out-range in start, it will never happens + + return range($startPage, $endPage); + } + + throw new IPF_ORM_Exception( + 'Cannot retrieve the range around the page of a not yet executed Pager query' + ); + } +} diff --git a/ipf/orm/pager/range/sliding.php b/ipf/orm/pager/range/sliding.php new file mode 100644 index 0000000..b704772 --- /dev/null +++ b/ipf/orm/pager/range/sliding.php @@ -0,0 +1,67 @@ +_options['chunk'])) { + $this->_setChunkLength($this->_options['chunk']); + } else { + throw new IPF_ORM_Exception('Missing parameter \'chunk\' that must be defined in options.'); + } + } + + public function getChunkLength() + { + return $this->_chunkLength; + } + + protected function _setChunkLength($chunkLength) + { + $chunkLength = (int) $chunkLength; + if (!$chunkLength) { + $chunkLength = 1; + } else { + $this->_chunkLength = $chunkLength; + } + } + + public function rangeAroundPage() + { + $pager = $this->getPager(); + + if ($pager->getExecuted()) { + $page = $pager->getPage(); + $pages = $pager->getLastPage(); + + $chunk = $this->getChunkLength(); + + if ($chunk > $pages) { + $chunk = $pages; + } + + $chunkStart = $page - (floor($chunk / 2)); + $chunkEnd = $page + (ceil($chunk / 2)-1); + + if ($chunkStart < 1) { + $adjust = 1 - $chunkStart; + $chunkStart = 1; + $chunkEnd = $chunkEnd + $adjust; + } + + if ($chunkEnd > $pages) { + $adjust = $chunkEnd - $pages; + $chunkStart = $chunkStart - $adjust; + $chunkEnd = $pages; + } + + return range($chunkStart, $chunkEnd); + } + + throw new IPF_ORM_Exception( + 'Cannot retrieve the range around the page of a not yet executed Pager query' + ); + } +}