[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/view/control/ -> AphrontPagerView.php (source)

   1  <?php
   2  
   3  final class AphrontPagerView extends AphrontView {
   4  
   5    private $offset;
   6    private $pageSize = 100;
   7  
   8    private $count;
   9    private $hasMorePages;
  10  
  11    private $uri;
  12    private $pagingParameter;
  13    private $surroundingPages = 2;
  14    private $enableKeyboardShortcuts;
  15  
  16    final public function setPageSize($page_size) {
  17      $this->pageSize = max(1, $page_size);
  18      return $this;
  19    }
  20  
  21    final public function setOffset($offset) {
  22      $this->offset = max(0, $offset);
  23      return $this;
  24    }
  25  
  26    final public function getOffset() {
  27      return $this->offset;
  28    }
  29  
  30    final public function getPageSize() {
  31      return $this->pageSize;
  32    }
  33  
  34    final public function setCount($count) {
  35      $this->count = $count;
  36      return $this;
  37    }
  38  
  39    final public function setHasMorePages($has_more) {
  40      $this->hasMorePages = $has_more;
  41      return $this;
  42    }
  43  
  44    final public function setURI(PhutilURI $uri, $paging_parameter) {
  45      $this->uri = $uri;
  46      $this->pagingParameter = $paging_parameter;
  47      return $this;
  48    }
  49  
  50    final public function readFromRequest(AphrontRequest $request) {
  51      $this->uri = $request->getRequestURI();
  52      $this->pagingParameter = 'offset';
  53      $this->offset = $request->getInt($this->pagingParameter);
  54      return $this;
  55    }
  56  
  57    final public function willShowPagingControls() {
  58      return $this->hasMorePages;
  59    }
  60  
  61    final public function setSurroundingPages($pages) {
  62      $this->surroundingPages = max(0, $pages);
  63      return $this;
  64    }
  65  
  66    private function computeCount() {
  67      if ($this->count !== null) {
  68        return $this->count;
  69      }
  70      return $this->getOffset()
  71        + $this->getPageSize()
  72        + ($this->hasMorePages ? 1 : 0);
  73    }
  74  
  75    private function isExactCountKnown() {
  76      return $this->count !== null;
  77    }
  78  
  79    /**
  80     * A common paging strategy is to select one extra record and use that to
  81     * indicate that there's an additional page (this doesn't give you a
  82     * complete page count but is often faster than counting the total number
  83     * of items). This method will take a result array, slice it down to the
  84     * page size if necessary, and call setHasMorePages() if there are more than
  85     * one page of results.
  86     *
  87     *    $results = queryfx_all(
  88     *      $conn,
  89     *      'SELECT ... LIMIT %d, %d',
  90     *      $pager->getOffset(),
  91     *      $pager->getPageSize() + 1);
  92     *    $results = $pager->sliceResults($results);
  93     *
  94     * @param   list  Result array.
  95     * @return  list  One page of results.
  96     */
  97    public function sliceResults(array $results) {
  98      if (count($results) > $this->getPageSize()) {
  99        $results = array_slice($results, 0, $this->getPageSize(), true);
 100        $this->setHasMorePages(true);
 101      }
 102      return $results;
 103    }
 104  
 105    public function setEnableKeyboardShortcuts($enable) {
 106      $this->enableKeyboardShortcuts = $enable;
 107      return $this;
 108    }
 109  
 110    public function render() {
 111      if (!$this->uri) {
 112        throw new Exception(
 113          pht('You must call setURI() before you can call render().'));
 114      }
 115  
 116      require_celerity_resource('aphront-pager-view-css');
 117  
 118      $page = (int)floor($this->getOffset() / $this->getPageSize());
 119      $last = ((int)ceil($this->computeCount() / $this->getPageSize())) - 1;
 120      $near = $this->surroundingPages;
 121  
 122      $min = $page - $near;
 123      $max = $page + $near;
 124  
 125      // Limit the window size to no larger than the number of available pages.
 126      if ($max - $min > $last) {
 127        $max = $min + $last;
 128        if ($max == $min) {
 129          return phutil_tag('div', array('class' => 'aphront-pager-view'), '');
 130        }
 131      }
 132  
 133      // Slide the window so it is entirely over displayable pages.
 134      if ($min < 0) {
 135        $max += 0 - $min;
 136        $min += 0 - $min;
 137      }
 138  
 139      if ($max > $last) {
 140        $min -= $max - $last;
 141        $max -= $max - $last;
 142      }
 143  
 144  
 145      // Build up a list of <index, label, css-class> tuples which describe the
 146      // links we'll display, then render them all at once.
 147  
 148      $links = array();
 149  
 150      $prev_index = null;
 151      $next_index = null;
 152  
 153      if ($min > 0) {
 154        $links[] = array(0, pht('First'), null);
 155      }
 156  
 157      if ($page > 0) {
 158        $links[] = array($page - 1, pht('Prev'), null);
 159        $prev_index = $page - 1;
 160      }
 161  
 162      for ($ii = $min; $ii <= $max; $ii++) {
 163        $links[] = array($ii, $ii + 1, ($ii == $page) ? 'current' : null);
 164      }
 165  
 166      if ($page < $last && $last > 0) {
 167        $links[] = array($page + 1, pht('Next'), null);
 168        $next_index = $page + 1;
 169      }
 170  
 171      if ($max < ($last - 1)) {
 172        $links[] = array($last, pht('Last'), null);
 173      }
 174  
 175      $base_uri = $this->uri;
 176      $parameter = $this->pagingParameter;
 177  
 178      if ($this->enableKeyboardShortcuts) {
 179        $pager_links = array();
 180        $pager_index = array(
 181          'prev' => $prev_index,
 182          'next' => $next_index,
 183        );
 184        foreach ($pager_index as $key => $index) {
 185          if ($index !== null) {
 186            $display_index = $this->getDisplayIndex($index);
 187            $pager_links[$key] = (string)$base_uri->alter(
 188              $parameter,
 189              $display_index);
 190          }
 191        }
 192        Javelin::initBehavior('phabricator-keyboard-pager', $pager_links);
 193      }
 194  
 195      // Convert tuples into rendered nodes.
 196      $rendered_links = array();
 197      foreach ($links as $link) {
 198        list($index, $label, $class) = $link;
 199        $display_index = $this->getDisplayIndex($index);
 200        $link = $base_uri->alter($parameter, $display_index);
 201        $rendered_links[] = phutil_tag(
 202          'a',
 203          array(
 204            'href' => $link,
 205            'class' => $class,
 206          ),
 207          $label);
 208      }
 209  
 210      return phutil_tag(
 211        'div',
 212        array('class' => 'aphront-pager-view'),
 213        $rendered_links);
 214    }
 215  
 216    private function getDisplayIndex($page_index) {
 217      $page_size = $this->getPageSize();
 218      // Use a 1-based sequence for display so that the number in the URI is
 219      // the same as the page number you're on.
 220      if ($page_index == 0) {
 221        // No need for the first page to say page=1.
 222        $display_index = null;
 223      } else {
 224        $display_index = $page_index * $page_size;
 225      }
 226      return $display_index;
 227    }
 228  
 229  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1