]> git.andy128k.dev Git - ipf-legacy-orm.git/commitdiff
Pager Improve
authoravl <alex.litovchenko@gmail.com>
Mon, 15 Sep 2008 09:49:36 +0000 (12:49 +0300)
committeravl <alex.litovchenko@gmail.com>
Mon, 15 Sep 2008 09:49:36 +0000 (12:49 +0300)
ipf/orm/connection/profiler.php
ipf/orm/pager.php [new file with mode: 0644]
ipf/orm/pager/layout.php [new file with mode: 0644]
ipf/orm/pager/range.php [new file with mode: 0644]
ipf/orm/pager/range/jumping.php [new file with mode: 0644]
ipf/orm/pager/range/sliding.php [new file with mode: 0644]

index 3c0a34010d43a28156ca7bd351c101cedf6a1848..6230025f8d78c7284443f64b9309f44ca39f78bc 100644 (file)
@@ -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 (file)
index 0000000..bf143d0
--- /dev/null
@@ -0,0 +1,255 @@
+<?php
+
+class IPF_ORM_Pager
+{
+    protected $_query;
+    protected $_countQuery;
+    protected $_countQueryParams;
+    protected $_numResults;
+    protected $_maxPerPage;
+    protected $_page;
+    protected $_lastPage;
+    protected $_executed;
+
+    public function __construct($query, $page, $maxPerPage = 0)
+    {
+        $this->_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 (file)
index 0000000..1d83ebb
--- /dev/null
@@ -0,0 +1,228 @@
+<?php
+
+class IPF_ORM_Pager_Layout
+{
+    private $_pager;
+    private $_pagerRange;
+    private $_template;
+    private $_selectedTemplate;
+    private $_separatorTemplate;
+    private $_urlMask;
+    private $_maskReplacements = array();
+
+    public function __construct($pager, $pagerRange, $urlMask)
+    {
+        $this->_setPager($pager);
+        $this->_setPagerRange($pagerRange);
+        $this->_setUrlMask($urlMask);
+
+        $this->setTemplate('[<a href="{%url}">{%page}</a>]');
+        $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 (file)
index 0000000..18ffd8f
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+abstract class IPF_ORM_Pager_Range
+{
+    protected $_options;
+    private $pager;
+
+    final public function __construct($options = array(), $pager = null)
+    {
+        $this->_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 (file)
index 0000000..4324921
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+class IPF_ORM_Pager_Range_Jumping extends IPF_ORM_Pager_Range
+{
+    private $_chunkLength;
+
+    protected function _initialize()
+    {
+        if (isset($this->_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 (file)
index 0000000..b704772
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+
+class IPF_ORM_Pager_Range_Sliding extends IPF_ORM_Pager_Range
+{
+    private $_chunkLength;
+
+    protected function _initialize()
+    {
+        if (isset($this->_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'
+        );
+    }
+}