Browse Source

Added PSR-0 compatible pagination module

Borislav Kosun 8 years ago
parent
commit
9758bf71d9

+ 48 - 0
modules/pagination/README.md

@@ -0,0 +1,48 @@
+# Koseven - pagination module
+Pagination module for Koseven
+
+## Getting Started
+2. Enable the module in your bootstrap file:
+
+```php
+/**
+ * Enable modules. Modules are referenced by a relative or absolute path.
+ */
+Kohana::modules(array(
+// ...
+        'pagination'       => MODPATH.'pagination',
+// ...
+));
+  ```
+3. Make sure the settings in `config/pagination.php` are correct for your environment. If not, copy the file to `application/config/pagination.php` and change the values accordingly.
+
+## Usage
+
+```php
+
+class Controller_Products extends Controller_Template
+{
+    public function action_index() {
+
+    	$products_count = ORM::factory('Product')->count_all();
+
+    	$pagination = Pagination::factory(array(
+            'total_items' => $products_count,
+            'items_per_page' => 20,
+        ));
+
+    	$products = ORM::factory('Product')
+    	    ->limit($pagination->items_per_page)
+    	    ->offset($pagination->offset)
+    	    ->find_all();
+
+    	$content = View::factory('index/v_products')
+    	    ->set('products', $products)
+    	    ->set('pagination', $pagination);
+    }
+ }
+
+```
+
+## License
+This package is open-sourced software licensed under the [BSD license](https://koseven.ga/LICENSE.md)

+ 417 - 0
modules/pagination/classes/Kohana/Pagination.php

@@ -0,0 +1,417 @@
+<?php defined('SYSPATH') or die('No direct script access.');
+
+/**
+ * Pagination links generator.
+ *
+ * @package    Koseven/Pagination
+ * @category   Base
+ * @author     Koseven Team
+ * @copyright  (c) 2017 Kohana Team
+ * @license    https://koseven.ga/LICENSE.md
+ */
+
+/**
+ * Pagination links generator.
+ *
+ * @package    Kohana/Pagination
+ * @category   Base
+ * @author     Kohana Team
+ * @copyright  (c) 2008-2009 Kohana Team
+ * @license    http://kohanaphp.com/license.html
+ */
+class Kohana_Pagination {
+
+	// Merged configuration settings
+	protected $config = array(
+		'current_page'      => array('source' => 'query_string', 'key' => 'page'),
+		'total_items'       => 0,
+		'items_per_page'    => 10,
+		'view'              => 'pagination/basic',
+		'auto_hide'         => TRUE,
+		'first_page_in_url' => FALSE,
+	);
+
+	// Current page number
+	protected $current_page;
+
+	// Total item count
+	protected $total_items;
+
+	// How many items to show per page
+	protected $items_per_page;
+
+	// Total page count
+	protected $total_pages;
+
+	// Item offset for the first item displayed on the current page
+	protected $current_first_item;
+
+	// Item offset for the last item displayed on the current page
+	protected $current_last_item;
+
+	// Previous page number; FALSE if the current page is the first one
+	protected $previous_page;
+
+	// Next page number; FALSE if the current page is the last one
+	protected $next_page;
+
+	// First page number; FALSE if the current page is the first one
+	protected $first_page;
+
+	// Last page number; FALSE if the current page is the last one
+	protected $last_page;
+
+	// Query offset
+	protected $offset;
+	
+	// Request object
+	protected $_request;
+	
+	// Route to use for URIs
+	protected $_route;
+	
+	// Parameters to use with Route to create URIs
+	protected $_route_params = array();
+
+	/**
+	 * Creates a new Pagination object.
+	 *
+	 * @param   array  configuration
+	 * @return  Pagination
+	 */
+	public static function factory(array $config = array(), Request $request = NULL)
+	{
+		return new Pagination($config, $request);
+	}
+
+	/**
+	 * Creates a new Pagination object.
+	 *
+	 * @param   array  configuration
+	 * @return  void
+	 */
+	public function __construct(array $config = array(), Request $request = NULL)
+	{
+		// Overwrite system defaults with application defaults
+		$this->config = $this->config_group() + $this->config;
+		
+		// Assing Request
+		if ($request === NULL)
+		{
+			$request = Request::current();
+		}
+		
+		$this->_request = $request;
+
+		// Assign default Route
+		$this->_route = $request->route();
+		
+		// Assign default route params
+		$this->_route_params = $request->param();
+		
+		// Pagination setup
+		$this->setup($config);
+	}
+
+	/**
+	 * Retrieves a pagination config group from the config file. One config group can
+	 * refer to another as its parent, which will be recursively loaded.
+	 *
+	 * @param   string  pagination config group; "default" if none given
+	 * @return  array   config settings
+	 */
+	public function config_group($group = 'default')
+	{
+		// Load the pagination config file
+		$config_file = Kohana::$config->load('pagination');
+
+		// Initialize the $config array
+		$config['group'] = (string) $group;
+
+		// Recursively load requested config groups
+		while (isset($config['group']) AND isset($config_file->{$config['group']}))
+		{
+			// Temporarily store config group name
+			$group = $config['group'];
+			unset($config['group']);
+
+			// Add config group values, not overwriting existing keys
+			$config += $config_file->$group;
+		}
+
+		// Get rid of possible stray config group names
+		unset($config['group']);
+
+		// Return the merged config group settings
+		return $config;
+	}
+
+	/**
+	 * Loads configuration settings into the object and (re)calculates pagination if needed.
+	 * Allows you to update config settings after a Pagination object has been constructed.
+	 *
+	 * @param   array   configuration
+	 * @return  object  Pagination
+	 */
+	public function setup(array $config = array())
+	{
+		if (isset($config['group']))
+		{
+			// Recursively load requested config groups
+			$config += $this->config_group($config['group']);
+		}
+
+		// Overwrite the current config settings
+		$this->config = $config + $this->config;
+
+		// Only (re)calculate pagination when needed
+		if ($this->current_page === NULL
+			OR isset($config['current_page'])
+			OR isset($config['total_items'])
+			OR isset($config['items_per_page']))
+		{
+			// Retrieve the current page number
+			if ( ! empty($this->config['current_page']['page']))
+			{
+				// The current page number has been set manually
+				$this->current_page = (int) $this->config['current_page']['page'];
+			}
+			else
+			{
+				$query_key = $this->config['current_page']['key'];
+						
+				switch ($this->config['current_page']['source'])
+				{
+					case 'query_string':
+						$this->current_page = ($this->_request->query($query_key) !== NULL)
+							? (int) $this->_request->query($query_key)
+							: 1;
+						break;
+
+					case 'route':
+						$this->current_page = (int) $this->_request->param($query_key, 1);
+						break;
+				}
+			}
+
+			// Calculate and clean all pagination variables
+			$this->total_items        = (int) max(0, $this->config['total_items']);
+			$this->items_per_page     = (int) max(1, $this->config['items_per_page']);
+			$this->total_pages        = (int) ceil($this->total_items / $this->items_per_page);
+			$this->current_page       = (int) min(max(1, $this->current_page), max(1, $this->total_pages));
+			$this->current_first_item = (int) min((($this->current_page - 1) * $this->items_per_page) + 1, $this->total_items);
+			$this->current_last_item  = (int) min($this->current_first_item + $this->items_per_page - 1, $this->total_items);
+			$this->previous_page      = ($this->current_page > 1) ? $this->current_page - 1 : FALSE;
+			$this->next_page          = ($this->current_page < $this->total_pages) ? $this->current_page + 1 : FALSE;
+			$this->first_page         = ($this->current_page === 1) ? FALSE : 1;
+			$this->last_page          = ($this->current_page >= $this->total_pages) ? FALSE : $this->total_pages;
+			$this->offset             = (int) (($this->current_page - 1) * $this->items_per_page);
+		}
+
+		// Chainable method
+		return $this;
+	}
+
+	/**
+	 * Generates the full URL for a certain page.
+	 *
+	 * @param   integer  page number
+	 * @return  string   page URL
+	 */
+	public function url($page = 1)
+	{
+		// Clean the page number
+		$page = max(1, (int) $page);
+
+		// No page number in URLs to first page
+		if ($page === 1 AND ! $this->config['first_page_in_url'])
+		{
+			$page = NULL;
+		}
+
+		switch ($this->config['current_page']['source'])
+		{
+			case 'query_string':
+			
+				return URL::site($this->_route->uri($this->_route_params).
+					$this->query(array($this->config['current_page']['key'] => $page)));
+
+			case 'route':
+			
+				return URL::site($this->_route->uri(array_merge($this->_route_params, 
+					array($this->config['current_page']['key'] => $page))).$this->query());
+		}
+
+		return '#';
+	}
+
+	/**
+	 * Checks whether the given page number exists.
+	 *
+	 * @param   integer  page number
+	 * @return  boolean
+	 * @since   3.0.7
+	 */
+	public function valid_page($page)
+	{
+		// Page number has to be a clean integer
+		if ( ! Valid::digit($page))
+			return FALSE;
+
+		return $page > 0 AND $page <= $this->total_pages;
+	}
+
+	/**
+	 * Renders the pagination links.
+	 *
+	 * @param   mixed   string of the view to use, or a Kohana_View object
+	 * @return  string  pagination output (HTML)
+	 */
+	public function render($view = NULL)
+	{
+		// Automatically hide pagination whenever it is superfluous
+		if ($this->config['auto_hide'] === TRUE AND $this->total_pages <= 1)
+			return '';
+
+		if ($view === NULL)
+		{
+			// Use the view from config
+			$view = $this->config['view'];
+		}
+
+		if ( ! $view instanceof View)
+		{
+			// Load the view file
+			$view = View::factory($view);
+		}
+
+		// Pass on the whole Pagination object
+		return $view->set(get_object_vars($this))->set('page', $this)->render();
+	}
+	
+	
+	/**
+	 * Request setter / getter
+	 * 
+	 * @param	Request
+	 * @return	Request	If used as getter
+	 * @return	$this	Chainable as setter
+	 */
+	public function request(Request $request = NULL)
+	{
+		if ($request === NULL)
+			return $this->_request;
+			
+		$this->_request = $request;
+		
+		return $this;
+	}
+	
+	/**
+	 * Route setter / getter
+	 * 
+	 * @param	Route
+	 * @return	Route	Route if used as getter
+	 * @return	$this	Chainable as setter
+	 */
+	public function route(Route $route = NULL)
+	{
+		if ($route === NULL)
+			return $this->_route;
+			
+		$this->_route = $route;
+		
+		return $this;
+	}
+	
+	/**
+	 * Route parameters setter / getter
+	 * 
+	 * @param	array	Route parameters to set
+	 * @return	array	Route parameters if used as getter
+	 * @return	$this	Chainable as setter
+	 */
+	public function route_params(array $route_params = NULL)
+	{
+		if ($route_params === NULL)
+			return $this->_route_params;
+			
+		$this->_route_params = $route_params;
+		
+		return $this;
+	}
+	
+	/**
+	 * URL::query() replacement for Pagination use only
+	 * 
+	 * @param	array	Parameters to override
+	 * @return	string
+	 */
+	public function query(array $params = NULL)
+	{
+		if ($params === NULL)
+		{
+			// Use only the current parameters
+			$params = $this->_request->query();
+		}
+		else
+		{
+			// Merge the current and new parameters
+			$params = array_merge($this->_request->query(), $params);
+		}
+		
+		if (empty($params))
+		{
+			// No query parameters
+			return '';
+		}
+		
+		// Note: http_build_query returns an empty string for a params array with only NULL values
+		$query = http_build_query($params, '', '&');
+		
+		// Don't prepend '?' to an empty string
+		return ($query === '') ? '' : ('?'.$query);
+	}
+
+	/**
+	 * Renders the pagination links.
+	 *
+	 * @return  string  pagination output (HTML)
+	 */
+	public function __toString()
+	{
+		try
+		{
+			return $this->render();
+		}
+		catch(Exception $e)
+		{
+			Kohana_Exception::handler($e);
+			return '';
+		}
+	}
+
+	/**
+	 * Returns a Pagination property.
+	 *
+	 * @param   string  property name
+	 * @return  mixed   Pagination property; NULL if not found
+	 */
+	public function __get($key)
+	{
+		return isset($this->$key) ? $this->$key : NULL;
+	}
+
+	/**
+	 * Updates a single config setting, and recalculates pagination if needed.
+	 *
+	 * @param   string  config key
+	 * @param   mixed   config value
+	 * @return  void
+	 */
+	public function __set($key, $value)
+	{
+		$this->setup(array($key => $value));
+	}
+
+} // End Pagination

+ 3 - 0
modules/pagination/classes/Pagination.php

@@ -0,0 +1,3 @@
+<?php defined('SYSPATH') or die('No direct script access.');
+
+class Pagination extends Kohana_Pagination {}

+ 15 - 0
modules/pagination/config/pagination.php

@@ -0,0 +1,15 @@
+<?php defined('SYSPATH') or die('No direct script access.');
+
+return array(
+
+	// Application defaults
+	'default' => array(
+		'current_page'      => array('source' => 'query_string', 'key' => 'page'), // source: "query_string" or "route"
+		'total_items'       => 0,
+		'items_per_page'    => 10,
+		'view'              => 'pagination/basic',
+		'auto_hide'         => TRUE,
+		'first_page_in_url' => FALSE,
+	),
+
+);

+ 37 - 0
modules/pagination/views/pagination/basic.php

@@ -0,0 +1,37 @@
+<p class="pagination">
+
+	<?php if ($first_page !== FALSE): ?>
+		<a href="<?php echo HTML::chars($page->url($first_page)) ?>" rel="first"><?php echo __('First') ?></a>
+	<?php else: ?>
+		<?php echo __('First') ?>
+	<?php endif ?>
+
+	<?php if ($previous_page !== FALSE): ?>
+		<a href="<?php echo HTML::chars($page->url($previous_page)) ?>" rel="prev"><?php echo __('Previous') ?></a>
+	<?php else: ?>
+		<?php echo __('Previous') ?>
+	<?php endif ?>
+
+	<?php for ($i = 1; $i <= $total_pages; $i++): ?>
+
+		<?php if ($i == $current_page): ?>
+			<strong><?php echo $i ?></strong>
+		<?php else: ?>
+			<a href="<?php echo HTML::chars($page->url($i)) ?>"><?php echo $i ?></a>
+		<?php endif ?>
+
+	<?php endfor ?>
+
+	<?php if ($next_page !== FALSE): ?>
+		<a href="<?php echo HTML::chars($page->url($next_page)) ?>" rel="next"><?php echo __('Next') ?></a>
+	<?php else: ?>
+		<?php echo __('Next') ?>
+	<?php endif ?>
+
+	<?php if ($last_page !== FALSE): ?>
+		<a href="<?php echo HTML::chars($page->url($last_page)) ?>" rel="last"><?php echo __('Last') ?></a>
+	<?php else: ?>
+		<?php echo __('Last') ?>
+	<?php endif ?>
+
+</p><!-- .pagination -->

+ 94 - 0
modules/pagination/views/pagination/floating.php

@@ -0,0 +1,94 @@
+<?php
+/*
+	First Previous 1 2 3 ... 22 23 24 25 26 [27] 28 29 30 31 32 ... 48 49 50 Next Last
+*/
+
+// Number of page links in the begin and end of whole range
+$count_out = ( ! empty($config['count_out'])) ? (int) $config['count_out'] : 3;
+// Number of page links on each side of current page
+$count_in = ( ! empty($config['count_in'])) ? (int) $config['count_in'] : 5;
+
+// Beginning group of pages: $n1...$n2
+$n1 = 1;
+$n2 = min($count_out, $total_pages);
+
+// Ending group of pages: $n7...$n8
+$n7 = max(1, $total_pages - $count_out + 1);
+$n8 = $total_pages;
+
+// Middle group of pages: $n4...$n5
+$n4 = max($n2 + 1, $current_page - $count_in);
+$n5 = min($n7 - 1, $current_page + $count_in);
+$use_middle = ($n5 >= $n4);
+
+// Point $n3 between $n2 and $n4
+$n3 = (int) (($n2 + $n4) / 2);
+$use_n3 = ($use_middle && (($n4 - $n2) > 1));
+
+// Point $n6 between $n5 and $n7
+$n6 = (int) (($n5 + $n7) / 2);
+$use_n6 = ($use_middle && (($n7 - $n5) > 1));
+
+// Links to display as array(page => content)
+$links = array();
+
+// Generate links data in accordance with calculated numbers
+for ($i = $n1; $i <= $n2; $i++)
+{
+	$links[$i] = $i;
+}
+if ($use_n3)
+{
+	$links[$n3] = '&hellip;';
+}
+for ($i = $n4; $i <= $n5; $i++)
+{
+	$links[$i] = $i;
+}
+if ($use_n6)
+{
+	$links[$n6] = '&hellip;';
+}
+for ($i = $n7; $i <= $n8; $i++)
+{
+	$links[$i] = $i;
+}
+
+?>
+<p class="pagination">
+
+	<?php if ($first_page !== FALSE): ?>
+		<a href="<?php echo HTML::chars($page->url($first_page)) ?>" rel="first"><?php echo __('First') ?></a>
+	<?php else: ?>
+		<?php echo __('First') ?>
+	<?php endif ?>
+
+	<?php if ($previous_page !== FALSE): ?>
+		<a href="<?php echo HTML::chars($page->url($previous_page)) ?>" rel="prev"><?php echo __('Previous') ?></a>
+	<?php else: ?>
+		<?php echo __('Previous') ?>
+	<?php endif ?>
+
+	<?php foreach ($links as $number => $content): ?>
+
+		<?php if ($number === $current_page): ?>
+			<strong><?php echo $content ?></strong>
+		<?php else: ?>
+			<a href="<?php echo HTML::chars($page->url($number)) ?>"><?php echo $content ?></a>
+		<?php endif ?>
+
+	<?php endforeach ?>
+
+	<?php if ($next_page !== FALSE): ?>
+		<a href="<?php echo HTML::chars($page->url($next_page)) ?>" rel="next"><?php echo __('Next') ?></a>
+	<?php else: ?>
+		<?php echo __('Next') ?>
+	<?php endif ?>
+
+	<?php if ($last_page !== FALSE): ?>
+		<a href="<?php echo HTML::chars($page->url($last_page)) ?>" rel="last"><?php echo __('Last') ?></a>
+	<?php else: ?>
+		<?php echo __('Last') ?>
+	<?php endif ?>
+
+</p><!-- .pagination -->