vendor/pagerfanta/pagerfanta/src/Pagerfanta/Pagerfanta.php line 477

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Pagerfanta package.
  4.  *
  5.  * (c) Pablo Díez <pablodip@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Pagerfanta;
  11. use OutOfBoundsException;
  12. use Pagerfanta\Adapter\AdapterInterface;
  13. use Pagerfanta\Exception\LogicException;
  14. use Pagerfanta\Exception\NotBooleanException;
  15. use Pagerfanta\Exception\NotIntegerException;
  16. use Pagerfanta\Exception\NotIntegerMaxPerPageException;
  17. use Pagerfanta\Exception\LessThan1MaxPerPageException;
  18. use Pagerfanta\Exception\NotIntegerCurrentPageException;
  19. use Pagerfanta\Exception\LessThan1CurrentPageException;
  20. use Pagerfanta\Exception\OutOfRangeCurrentPageException;
  21. /**
  22.  * Represents a paginator.
  23.  *
  24.  * @author Pablo Díez <pablodip@gmail.com>
  25.  */
  26. class Pagerfanta implements \Countable, \IteratorAggregatePagerfantaInterface
  27. {
  28.     private $adapter;
  29.     private $allowOutOfRangePages;
  30.     private $normalizeOutOfRangePages;
  31.     private $maxPerPage;
  32.     private $currentPage;
  33.     private $nbResults;
  34.     private $currentPageResults;
  35.     /**
  36.      * @param AdapterInterface $adapter An adapter.
  37.      */
  38.     public function __construct(AdapterInterface $adapter)
  39.     {
  40.         $this->adapter $adapter;
  41.         $this->allowOutOfRangePages false;
  42.         $this->normalizeOutOfRangePages false;
  43.         $this->maxPerPage 10;
  44.         $this->currentPage 1;
  45.     }
  46.     /**
  47.      * Returns the adapter.
  48.      *
  49.      * @return AdapterInterface The adapter.
  50.      */
  51.     public function getAdapter()
  52.     {
  53.         return $this->adapter;
  54.     }
  55.     /**
  56.      * Sets whether or not allow out of range pages.
  57.      *
  58.      * @param Boolean $value
  59.      *
  60.      * @return self
  61.      */
  62.     public function setAllowOutOfRangePages($value)
  63.     {
  64.         $this->allowOutOfRangePages $this->filterBoolean($value);
  65.         return $this;
  66.     }
  67.     /**
  68.      * Returns whether or not allow out of range pages.
  69.      *
  70.      * @return Boolean
  71.      */
  72.     public function getAllowOutOfRangePages()
  73.     {
  74.         return $this->allowOutOfRangePages;
  75.     }
  76.     /**
  77.      * Sets whether or not normalize out of range pages.
  78.      *
  79.      * @param Boolean $value
  80.      *
  81.      * @return self
  82.      */
  83.     public function setNormalizeOutOfRangePages($value)
  84.     {
  85.         $this->normalizeOutOfRangePages $this->filterBoolean($value);
  86.         return $this;
  87.     }
  88.     /**
  89.      * Returns whether or not normalize out of range pages.
  90.      *
  91.      * @return Boolean
  92.      */
  93.     public function getNormalizeOutOfRangePages()
  94.     {
  95.         return $this->normalizeOutOfRangePages;
  96.     }
  97.     private function filterBoolean($value)
  98.     {
  99.         if (!is_bool($value)) {
  100.             throw new NotBooleanException();
  101.         }
  102.         return $value;
  103.     }
  104.     /**
  105.      * Sets the max per page.
  106.      *
  107.      * Tries to convert from string and float.
  108.      *
  109.      * @param integer $maxPerPage
  110.      *
  111.      * @return self
  112.      *
  113.      * @throws NotIntegerMaxPerPageException If the max per page is not an integer even converting.
  114.      * @throws LessThan1MaxPerPageException  If the max per page is less than 1.
  115.      */
  116.     public function setMaxPerPage($maxPerPage)
  117.     {
  118.         $this->maxPerPage $this->filterMaxPerPage($maxPerPage);
  119.         $this->resetForMaxPerPageChange();
  120.         return $this;
  121.     }
  122.     private function filterMaxPerPage($maxPerPage)
  123.     {
  124.         $maxPerPage $this->toInteger($maxPerPage);
  125.         $this->checkMaxPerPage($maxPerPage);
  126.         return $maxPerPage;
  127.     }
  128.     private function checkMaxPerPage($maxPerPage)
  129.     {
  130.         if (!is_int($maxPerPage)) {
  131.             throw new NotIntegerMaxPerPageException();
  132.         }
  133.         if ($maxPerPage 1) {
  134.             throw new LessThan1MaxPerPageException();
  135.         }
  136.     }
  137.     private function resetForMaxPerPageChange()
  138.     {
  139.         $this->currentPageResults null;
  140.         $this->nbResults null;
  141.     }
  142.     /**
  143.      * Returns the max per page.
  144.      *
  145.      * @return integer
  146.      */
  147.     public function getMaxPerPage()
  148.     {
  149.         return $this->maxPerPage;
  150.     }
  151.     /**
  152.      * Sets the current page.
  153.      *
  154.      * Tries to convert from string and float.
  155.      *
  156.      * @param integer $currentPage
  157.      *
  158.      * @return self
  159.      *
  160.      * @throws NotIntegerCurrentPageException If the current page is not an integer even converting.
  161.      * @throws LessThan1CurrentPageException  If the current page is less than 1.
  162.      * @throws OutOfRangeCurrentPageException If It is not allowed out of range pages and they are not normalized.
  163.      */
  164.     public function setCurrentPage($currentPage)
  165.     {
  166.         $this->useDeprecatedCurrentPageBooleanArguments(func_get_args());
  167.         $this->currentPage $this->filterCurrentPage($currentPage);
  168.         $this->resetForCurrentPageChange();
  169.         return $this;
  170.     }
  171.     private function useDeprecatedCurrentPageBooleanArguments($arguments)
  172.     {
  173.         $this->useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments);
  174.         $this->useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments);
  175.     }
  176.     private function useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments)
  177.     {
  178.         $index 1;
  179.         $method 'setAllowOutOfRangePages';
  180.         $this->useDeprecatedBooleanArgument($arguments$index$method);
  181.     }
  182.     private function useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments)
  183.     {
  184.         $index 2;
  185.         $method 'setNormalizeOutOfRangePages';
  186.         $this->useDeprecatedBooleanArgument($arguments$index$method);
  187.     }
  188.     private function useDeprecatedBooleanArgument($arguments$index$method)
  189.     {
  190.         if (isset($arguments[$index])) {
  191.             $this->$method($arguments[$index]);
  192.         }
  193.     }
  194.     private function filterCurrentPage($currentPage)
  195.     {
  196.         $currentPage $this->toInteger($currentPage);
  197.         $this->checkCurrentPage($currentPage);
  198.         $currentPage $this->filterOutOfRangeCurrentPage($currentPage);
  199.         return $currentPage;
  200.     }
  201.     private function checkCurrentPage($currentPage)
  202.     {
  203.         if (!is_int($currentPage)) {
  204.             throw new NotIntegerCurrentPageException();
  205.         }
  206.         if ($currentPage 1) {
  207.             throw new LessThan1CurrentPageException();
  208.         }
  209.     }
  210.     private function filterOutOfRangeCurrentPage($currentPage)
  211.     {
  212.         if ($this->notAllowedCurrentPageOutOfRange($currentPage)) {
  213.             return $this->normalizeOutOfRangeCurrentPage($currentPage);
  214.         }
  215.         return $currentPage;
  216.     }
  217.     private function notAllowedCurrentPageOutOfRange($currentPage)
  218.     {
  219.         return !$this->getAllowOutOfRangePages() &&
  220.                $this->currentPageOutOfRange($currentPage);
  221.     }
  222.     private function currentPageOutOfRange($currentPage)
  223.     {
  224.         return $currentPage && $currentPage $this->getNbPages();
  225.     }
  226.     /**
  227.      * @param int $currentPage
  228.      *
  229.      * @return int
  230.      *
  231.      * @throws OutOfRangeCurrentPageException If the page should not be normalized
  232.      */
  233.     private function normalizeOutOfRangeCurrentPage($currentPage)
  234.     {
  235.         if ($this->getNormalizeOutOfRangePages()) {
  236.             return $this->getNbPages();
  237.         }
  238.         throw new OutOfRangeCurrentPageException(sprintf('Page "%d" does not exist. The currentPage must be inferior to "%d"'$currentPage$this->getNbPages()));
  239.     }
  240.     private function resetForCurrentPageChange()
  241.     {
  242.         $this->currentPageResults null;
  243.     }
  244.     /**
  245.      * Returns the current page.
  246.      *
  247.      * @return integer
  248.      */
  249.     public function getCurrentPage()
  250.     {
  251.         return $this->currentPage;
  252.     }
  253.     /**
  254.      * Returns the results for the current page.
  255.      *
  256.      * @return array|\Traversable
  257.      */
  258.     public function getCurrentPageResults()
  259.     {
  260.         if ($this->notCachedCurrentPageResults()) {
  261.             $this->currentPageResults $this->getCurrentPageResultsFromAdapter();
  262.         }
  263.         return $this->currentPageResults;
  264.     }
  265.     private function notCachedCurrentPageResults()
  266.     {
  267.         return $this->currentPageResults === null;
  268.     }
  269.     private function getCurrentPageResultsFromAdapter()
  270.     {
  271.         $offset $this->calculateOffsetForCurrentPageResults();
  272.         $length $this->getMaxPerPage();
  273.         return $this->adapter->getSlice($offset$length);
  274.     }
  275.     private function calculateOffsetForCurrentPageResults()
  276.     {
  277.         return ($this->getCurrentPage() - 1) * $this->getMaxPerPage();
  278.     }
  279.     /**
  280.      * Calculates the current page offset start
  281.      *
  282.      * @return int
  283.      */
  284.     public function getCurrentPageOffsetStart()
  285.     {
  286.         return $this->getNbResults() ?
  287.                $this->calculateOffsetForCurrentPageResults() + :
  288.                0;
  289.     }
  290.     /**
  291.      * Calculates the current page offset end
  292.      *
  293.      * @return int
  294.      */
  295.     public function getCurrentPageOffsetEnd()
  296.     {
  297.         return $this->hasNextPage() ?
  298.                $this->getCurrentPage() * $this->getMaxPerPage() :
  299.                $this->getNbResults();
  300.     }
  301.     /**
  302.      * Returns the number of results.
  303.      *
  304.      * @return integer
  305.      */
  306.     public function getNbResults()
  307.     {
  308.         if ($this->notCachedNbResults()) {
  309.             $this->nbResults $this->getAdapter()->getNbResults();
  310.         }
  311.         return $this->nbResults;
  312.     }
  313.     private function notCachedNbResults()
  314.     {
  315.         return $this->nbResults === null;
  316.     }
  317.     /**
  318.      * Returns the number of pages.
  319.      *
  320.      * @return integer
  321.      */
  322.     public function getNbPages()
  323.     {
  324.         $nbPages $this->calculateNbPages();
  325.         if ($nbPages == 0) {
  326.             return $this->minimumNbPages();
  327.         }
  328.         return $nbPages;
  329.     }
  330.     private function calculateNbPages()
  331.     {
  332.         return (int) ceil($this->getNbResults() / $this->getMaxPerPage());
  333.     }
  334.     private function minimumNbPages()
  335.     {
  336.         return 1;
  337.     }
  338.     /**
  339.      * Returns if the number of results is higher than the max per page.
  340.      *
  341.      * @return Boolean
  342.      */
  343.     public function haveToPaginate()
  344.     {
  345.         return $this->getNbResults() > $this->maxPerPage;
  346.     }
  347.     /**
  348.      * Returns whether there is previous page or not.
  349.      *
  350.      * @return Boolean
  351.      */
  352.     public function hasPreviousPage()
  353.     {
  354.         return $this->currentPage 1;
  355.     }
  356.     /**
  357.      * Returns the previous page.
  358.      *
  359.      * @return integer
  360.      *
  361.      * @throws LogicException If there is no previous page.
  362.      */
  363.     public function getPreviousPage()
  364.     {
  365.         if (!$this->hasPreviousPage()) {
  366.             throw new LogicException('There is no previous page.');
  367.         }
  368.         return $this->currentPage 1;
  369.     }
  370.     /**
  371.      * Returns whether there is next page or not.
  372.      *
  373.      * @return Boolean
  374.      */
  375.     public function hasNextPage()
  376.     {
  377.         return $this->currentPage $this->getNbPages();
  378.     }
  379.     /**
  380.      * Returns the next page.
  381.      *
  382.      * @return integer
  383.      *
  384.      * @throws LogicException If there is no next page.
  385.      */
  386.     public function getNextPage()
  387.     {
  388.         if (!$this->hasNextPage()) {
  389.             throw new LogicException('There is no next page.');
  390.         }
  391.         return $this->currentPage 1;
  392.     }
  393.     /**
  394.      * Implements the \Countable interface.
  395.      *
  396.      * Return integer The number of results.
  397.      */
  398.     public function count()
  399.     {
  400.         return $this->getNbResults();
  401.     }
  402.     /**
  403.      * Implements the \IteratorAggregate interface.
  404.      *
  405.      * Returns an \ArrayIterator instance with the current results.
  406.      */
  407.     public function getIterator()
  408.     {
  409.         $results $this->getCurrentPageResults();
  410.         if ($results instanceof \Iterator) {
  411.             return $results;
  412.         }
  413.         if ($results instanceof \IteratorAggregate) {
  414.             return $results->getIterator();
  415.         }
  416.         return new \ArrayIterator($results);
  417.     }
  418.     private function toInteger($value)
  419.     {
  420.         if ($this->needsToIntegerConversion($value)) {
  421.             return (int) $value;
  422.         }
  423.         return $value;
  424.     }
  425.     private function needsToIntegerConversion($value)
  426.     {
  427.         return (is_string($value) || is_float($value)) && (int) $value == $value;
  428.     }
  429.     /**
  430.      * Get page number of the item at specified position (1-based index)
  431.      *
  432.      * @param integer $position
  433.      *
  434.      * @return integer
  435.      */
  436.     public function getPageNumberForItemAtPosition($position)
  437.     {
  438.         if (!is_int($position)) {
  439.             throw new NotIntegerException();
  440.         }
  441.         if ($this->getNbResults() < $position) {
  442.             throw new OutOfBoundsException(sprintf(
  443.                 'Item requested at position %d, but there are only %d items.',
  444.                 $position,
  445.                 $this->getNbResults()
  446.             ));
  447.         }
  448.         return (int) ceil($position/$this->getMaxPerPage());
  449.     }
  450. }