Pager.php 13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<?php

/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
20
 * <http://www.phpdoctrine.org>.
21 22 23 24 25 26 27 28 29 30
 */

/**
 * Doctrine_Pager
 *
 * @author      Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @package     Doctrine
 * @subpackage  Pager
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @version     $Revision$
31 32
 * @link        www.phpdoctrine.org
 * @since       0.9
33 34 35
 */
class Doctrine_Pager
{
36
    /**
zYne's avatar
zYne committed
37
     * @var Doctrine_Query $_query      Doctrine_Query object related to the pager
38
     */
zYne's avatar
zYne committed
39
    protected $_query;
40

41 42 43 44 45 46 47 48 49 50
    /**
     * @var Doctrine_Query $_countQuery Doctrine_Query object related to the counter of pager
     */
    protected $_countQuery;

    /**
     * @var array $_countQueryParams    Hold the params to be used by Doctrine_Query counter object of pager
     */
    protected $_countQueryParams;

51
    /**
zYne's avatar
zYne committed
52
     * @var integer $_numResults        Number of results found
53
     */
zYne's avatar
zYne committed
54
    protected $_numResults;
55

56
    /**
zYne's avatar
zYne committed
57
     * @var integer $_maxPerPage        Maximum number of itens per page
58
     */
zYne's avatar
zYne committed
59
    protected $_maxPerPage;
60 61

    /**
zYne's avatar
zYne committed
62
     * @var integer $page               Current page
63
     */
zYne's avatar
zYne committed
64
    protected $_page;
65 66

    /**
zYne's avatar
zYne committed
67
     * @var integer $_lastPage          Last page (total of pages)
68
     */
zYne's avatar
zYne committed
69
    protected $_lastPage;
70

71 72 73 74 75
    /**
     * @var boolean $_executed          Pager was initialized (called "execute" at least once)
     */
    protected $_executed;

76 77 78 79 80


    /**
     * __construct
     *
81
     * @param mixed $query     Accepts either a Doctrine_Query object or a string 
82
     *                        (which does the Doctrine_Query class creation).
83 84 85 86
     * @param int $page     Current page
     * @param int $maxPerPage     Maximum itens per page
     * @return void
     */
zYne's avatar
zYne committed
87
    public function __construct($query, $page, $maxPerPage = 0)
88
    {
89
        $this->_setExecuted(false);
90

91
        $this->_setQuery($query);
92 93
        $this->_setPage($page);

94
        $this->setMaxPerPage($maxPerPage);
95 96 97 98
    }


    /**
99
     * _initialize
100 101 102
     *
     * Initialize Pager object calculating number of results
     *
103
     * @param $params  Optional parameters to Doctrine_Query::execute
104 105
     * @return void
     */
106
    protected function _initialize($params = array())
107
    {
zYne's avatar
zYne committed
108
        // retrieve the number of items found
109
        $count = $this->getCountQuery()->count($this->getCountQueryParams($params));
110

111 112
        $this->_setNumResults($count);
        $this->_setExecuted(true); // _adjustOffset relies of _executed equals true = getNumResults()
113

114
        $this->_adjustOffset();
115 116 117 118
    }


    /**
119
     * _adjustOffset
120 121 122 123 124
     *
     * Adjusts last page of Doctrine_Pager, offset and limit of Doctrine_Query associated
     *
     * @return void
     */
125
    protected function _adjustOffset()
126
    {
127 128 129 130 131 132 133 134 135 136
        // 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 Doctrine_Query object
        $p = $this->getQuery();
        $p->offset($offset);
        $p->limit($this->getMaxPerPage());
137
    }
138

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

    /**
     * getExecuted
     *
     * Returns the check if Pager was already executed at least once
     *
     * @return boolen        Pager was executed
     */
    public function getExecuted()
    {
        return $this->_executed;
    }


    /**
     * _setExecuted
     *
     * Defines if Pager was already executed
     *
     * @param $executed       Pager was executed
     * @return void
     */
    protected function _setExecuted($executed)
    {
        $this->_executed = $executed;
164
    }
165 166 167


    /**
zYne's avatar
zYne committed
168
     * getNumResults
169 170 171 172 173
     *
     * Returns the number of results found
     *
     * @return int        the number of results found
     */
zYne's avatar
zYne committed
174
    public function getNumResults()
175
    {
176 177 178 179 180 181 182
        if ($this->getExecuted()) {
            return $this->_numResults;
        }

        throw new Doctrine_Pager_Exception(
            'Cannot retrieve the number of results of a not yet executed Pager query'
        );
183
    }
184 185 186


    /**
187
     * _setNumResults
188 189 190 191 192 193
     *
     * Defines the number of total results on initial query
     *
     * @param $nb       Number of results found on initial query fetch
     * @return void
     */
194
    protected function _setNumResults($nb)
195
    {
zYne's avatar
zYne committed
196
        $this->_numResults = $nb;
197
    }
198 199 200 201 202 203 204 205 206


    /**
     * getFirstPage
     *
     * Returns the first page
     *
     * @return int        first page
     */
207 208 209 210
    public function getFirstPage()
    {
        return 1;
    }
211 212 213 214 215 216 217 218 219


    /**
     * getLastPage
     *
     * Returns the last page (total of pages)
     *
     * @return int        last page (total of pages)
     */
220 221
    public function getLastPage()
    {
222 223 224 225 226 227 228
        if ($this->getExecuted()) {
            return $this->_lastPage;
        }

        throw new Doctrine_Pager_Exception(
            'Cannot retrieve the last page number of a not yet executed Pager query'
        );
229
    }
230 231 232


    /**
233
     * _setLastPage
234 235 236 237 238 239
     *
     * Defines the last page (total of pages)
     *
     * @param $page       last page (total of pages)
     * @return void
     */
240
    protected function _setLastPage($page)
241
    {
zYne's avatar
zYne committed
242
        $this->_lastPage = $page;
243

zYne's avatar
zYne committed
244
        if ($this->getPage() > $page) {
245 246 247
            $this->_setPage($page);
        }
    }
248 249 250 251 252 253 254 255 256


    /**
     * getLastPage
     *
     * Returns the current page
     *
     * @return int        current page
     */
257 258
    public function getPage()
    {
zYne's avatar
zYne committed
259
        return $this->_page;
260
    }
261 262 263 264 265 266 267 268 269


    /**
     * getLastPage
     *
     * Returns the next page
     *
     * @return int        next page
     */
270 271
    public function getNextPage()
    {
272
        if ($this->getExecuted()) {
273
            return min($this->getPage() + 1, $this->getLastPage());
274 275 276 277
        }

        throw new Doctrine_Pager_Exception(
            'Cannot retrieve the last page number of a not yet executed Pager query'
278
        );
279
    }
280 281 282 283 284 285 286 287 288


    /**
     * getLastPage
     *
     * Returns the previous page
     *
     * @return int        previous page
     */
289 290
    public function getPreviousPage()
    {
291 292 293 294 295 296 297
        if ($this->getExecuted()) {
            return max($this->getPage() - 1, $this->getFirstPage());
        }

        throw new Doctrine_Pager_Exception(
            'Cannot retrieve the previous page number of a not yet executed Pager query'
        );
298 299 300 301 302 303 304 305 306 307 308 309
    }


    /**
     * haveToPaginate
     *
     * Return true if it's necessary to paginate or false if not
     *
     * @return bool        true if it is necessary to paginate, false otherwise
     */
    public function haveToPaginate()
    {
310 311 312 313 314 315 316
        if ($this->getExecuted()) {
            return $this->getNumResults() > $this->getMaxPerPage();
        }

        throw new Doctrine_Pager_Exception(
            'Cannot know if it is necessary to paginate a not yet executed Pager query'
        );
317 318 319 320
    }


    /**
321 322 323 324 325 326 327
     * setPage
     *
     * Defines the current page and automatically adjust offset and limits
     *
     * @param $page       current page
     * @return void
     */
328 329 330
    public function setPage($page)
    {
        $this->_setPage($page);
331
        $this->_setExecuted(false);
332
    }
333 334 335 336 337 338 339 340 341 342


    /**
     * _setPage
     *
     * Defines the current page
     *
     * @param $page       current page
     * @return void
     */
343 344 345
    private function _setPage($page)
    {
        $page = intval($page);
zYne's avatar
zYne committed
346
        $this->_page = ($page <= 0) ? 1 : $page;
347
    }
348 349 350 351 352 353 354 355 356


    /**
     * getLastPage
     *
     * Returns the maximum number of itens per page
     *
     * @return int        maximum number of itens per page
     */
357 358
    public function getMaxPerPage()
    {
zYne's avatar
zYne committed
359
        return $this->_maxPerPage;
360 361 362 363
    }


    /**
364 365 366 367 368 369 370
     * setMaxPerPage
     *
     * Defines the maximum number of itens per page and automatically adjust offset and limits
     *
     * @param $max       maximum number of itens per page
     * @return void
     */
371 372 373
    public function setMaxPerPage($max)
    {
        if ($max > 0) {
zYne's avatar
zYne committed
374
            $this->_maxPerPage = $max;
375
        } else if ($max == 0) {
zYne's avatar
zYne committed
376
            $this->_maxPerPage = 25;
377
        } else {
zYne's avatar
zYne committed
378
            $this->_maxPerPage = abs($max);
379
        }
380 381

        $this->_setExecuted(false);
382
    }
383 384


385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
    /**
     * getResultsInPage
     *
     * Returns the number of itens in current page
     *
     * @return int    Number of itens in current page
     */
    public function getResultsInPage()
    {
        $page = $this->getPage();

        if ($page != $this->getLastPage()) {
            return $page * $this->getMaxPerPage();
        }

        $offset = ($this->getPage() - 1) * $this->getMaxPerPage();

        return abs($this->getNumResults() - $offset);
    }


406 407 408
    /**
     * getQuery
     *
409
     * Returns the Doctrine_Query collector object related to the pager
410
     *
411
     * @return Doctrine_Query    Doctrine_Query object related to the pager
412
     */
413 414
    public function getQuery()
    {
zYne's avatar
zYne committed
415
        return $this->_query;
416
    }
417 418 419


    /**
420
     * _setQuery
421
     *
422
     * Defines the collector query to be used by pager
423
     *
424 425
     * @param Doctrine_Query     Accepts either a Doctrine_Query object or a string 
     *                           (which does the Doctrine_Query class creation).
426 427
     * @return void
     */
428
    protected function _setQuery($query)
429 430
    {
        if (is_string($query)) {
zYne's avatar
zYne committed
431
            $query = Doctrine_Query::create()->parseQuery($query);
432
        }
433

zYne's avatar
zYne committed
434
        $this->_query = $query;
435
    }
436 437


438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
    /**
     * getCountQuery
     *
     * Returns the Doctrine_Query object that is used to make the count results to pager
     *
     * @return Doctrine_Query     Doctrine_Query object related to the pager
     */
    public function getCountQuery()
    {
        return ($this->_countQuery !== null) ? $this->_countQuery : $this->_query;
    }


    /**
     * setCountQuery
     *
     * Defines the counter query to be used by pager
     *
456 457 458 459
     * @param Doctrine_Query  Accepts either a Doctrine_Query object or a string 
     *                        (which does the Doctrine_Query class creation).
     * @param array           Optional params to be used by counter Doctrine_Query. 
     *                        If not defined, the params passed to execute method will be used.
460 461
     * @return void
     */
462
    public function setCountQuery($query, $params = null)
463 464 465 466 467 468 469
    {
        if (is_string($query)) {
            $query = Doctrine_Query::create()->parseQuery($query);
        }

        $this->_countQuery = $query;

470 471
        $this->setCountQueryParams($params);

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
        $this->_setExecuted(false);
    }


    /**
     * getCountQueryParams
     *
     * Returns the params to be used by counter Doctrine_Query
     *
     * @return array     Doctrine_Query counter params
     */
    public function getCountQueryParams($defaultParams = array())
    {
        return ($this->_countQueryParams !== null) ? $this->_countQueryParams : $defaultParams;
    }


    /**
     * setCountQueryParams
     *
     * Defines the params to be used by counter Doctrine_Query
     *
     * @param array       Optional params to be used by counter Doctrine_Query. 
     *                    If not defined, the params passed to execute method will be used.
     * @param boolean     Optional argument that append the query param instead of overriding the existent ones.
     * @return void
     */
    public function setCountQueryParams($params = array(), $append = false)
    {
        if ($append && is_array($this->_countQueryParams)) {
            $this->_countQueryParams = array_merge($this->_countQueryParams, $params);
        } else {
504 505 506 507
            if ($params !== null && !is_array($params)) {
                $params = array($params);
            }

508 509 510 511 512 513 514
            $this->_countQueryParams = $params;
        }

        $this->_setExecuted(false);
    }


515 516 517
    /**
     * execute
     *
518 519 520 521 522 523
     * Executes the query, populates the collection and then return it
     *
     * @param $params               Optional parameters to Doctrine_Query::execute
     * @param $hydrationMode        Hydration Mode of Doctrine_Query::execute 
     *                              returned ResultSet. Doctrine::Default is FETCH_RECORD
     * @return Doctrine_Collection  The root collection
524
     */
525 526
    public function execute($params = array(), $hydrationMode = Doctrine::FETCH_RECORD)
    {
527 528 529
        if (!$this->getExecuted()) {
            $this->_initialize($params);
        }
530

531 532
        return $this->getQuery()->execute($params, $hydrationMode);
    }
533
}