leilukin-tumbleblog/includes/class/Paginator.php

425 lines
13 KiB
PHP

<?php
/**
* Class: Paginator
* Paginates over an array.
*/
class Paginator {
# Array: $array
# The original, unmodified data.
public $array;
# Integer: $per_page
# Number of items per page.
public $per_page;
# String: $name
# Name of the $_GET value for the current page.
public $name;
# Boolean: $model
# Should the <$array> items be treated as Models?
# In this case, <$array> should be in the form of array(<ids>, "ModelName")
public $model;
# Integer: $total
# Total number of items to paginate.
public $total;
# Integer: $page
# The current page.
public $page;
# Integer: $pages
# Total number of pages.
public $pages;
# Array: $result
# The result of the pagination.
# @paginated@ is a reference to this.
public $result = array();
# Array: $paginated
# The result of the pagination.
# Reference to @result@. Enables ".paginated" in Twig.
public $paginated = array();
# Array: $names
# An array of the currently-used pagination URL parameters.
public static $names = array();
/**
* Function: __construct
* Prepares an array for pagination.
*
* Parameters:
* $array - The array to paginate.
* $per_page - Number of items per page.
* $name - The $_GET parameter name to determin the current page.
* $model - Is it an array of model placeholders to be initialized?
* $page - Page number to start at.
*
* Notes:
* If $model is true, each $array item shown on the page will
* be initialized as a model of the type specified in $array[1].
* $array[0] is expected to be an array of model placeholders.
*/
public function __construct(
$array,
$per_page = 10,
$name = "page",
$model = null,
$page = null
) {
self::$names[] = $name;
if (!is_array($array))
$array = array($array);
$this->array = $array;
$this->per_page = $per_page;
$this->name = $name;
$this->model = fallback(
$model,
(
count($this->array) == 2 and
is_array($this->array[0]) and
is_string($this->array[1]) and
class_exists($this->array[1])
)
);
if ($model)
list($array, $model_name) = $this->array;
$request = (isset($_GET[$name]) and is_numeric($_GET[$name])) ?
$_GET[$name] :
null ;
$this->total = count($array);
$this->page = intval(oneof($page, $request, 1));
$this->pages = ceil($this->total / $this->per_page);
$this->result = array();
if ($this->page < 1)
$this->page = 1;
$offset = ($this->page - 1) * $this->per_page;
if ($model) {
for ($i = $offset; $i < ($offset + $this->per_page); $i++) {
if (isset($array[$i]))
$this->result[] = new $model_name(
null,
array("read_from" => $array[$i])
);
}
} else {
$this->result = array_slice(
$array,
$offset,
$this->per_page
);
}
$this->paginated =& $this->result;
}
/**
* Function: reslice
* Reslices pagination with a new number of items per page.
*
* Parameters:
* $per_page - Number of items per page.
*/
public function reslice($per_page): self {
return new self(
$this->array,
$per_page,
$this->name,
$this->model,
$this->page
);
}
/**
* Function: next
* Returns the next pagination sequence.
*/
public function next(): self {
return new self(
$this->array,
$this->per_page,
$this->name,
$this->model,
$this->page + 1
);
}
/**
* Function: prev
* Returns the previous pagination sequence.
*/
public function prev(): self {
return new self(
$this->array,
$this->per_page,
$this->name,
$this->model,
$this->page - 1
);
}
/**
* Function: next_page
* Checks whether or not it makes sense to show the Next Page link.
*/
public function next_page(): bool {
return (
$this->pages > 1 and
$this->page < $this->pages
);
}
/**
* Function: prev_page
* Checks whether or not it makes sense to show the Previous Page link.
*/
public function prev_page(): bool {
return (
$this->page > 1 and
$this->page <= $this->pages
);
}
/**
* Function: next_link
* Outputs a link to the next page.
*
* Parameters:
* $text - The text for the link.
* $class - The CSS class for the link.
* $page - Page number to link to.
* $anchor - An anchor target.
*/
public function next_link(
$text = null,
$class = "next_page",
$page = null,
$anchor = ""
): ?string {
if (!$this->next_page())
return null;
if (!empty($anchor))
$anchor = '#'.$anchor;
fallback($text, __("Next"));
return '<a rel="next" class="'.
fix($class, true).
'" id="pagination_next_'.
$this->name.
'" href="'.
$this->next_page_url($page).
fix($anchor, true).
'">'.$text.'</a>';
}
/**
* Function: prev_link
* Outputs a link to the previous page.
*
* Parameters:
* $text - The text for the link.
* $class - The CSS class for the link.
* $page - Page number to link to.
* $anchor - An anchor target.
*/
public function prev_link(
$text = null,
$class = "prev_page",
$page = null,
$anchor = ""
): ?string {
if (!$this->prev_page())
return null;
if (!empty($anchor))
$anchor = '#'.$anchor;
fallback($text, __("Previous"));
return '<a rel="prev" class="'.
fix($class, true).
'" id="pagination_prev_'.
$this->name.
'" href="'.
$this->prev_page_url($page).
fix($anchor, true).
'">'.$text.'</a>';
}
/**
* Function: final_link
* Outputs a link to the final page.
*
* Parameters:
* $text - The text for the link.
* $class - The CSS class for the link.
* $anchor - An anchor target.
*/
public function final_link(
$text = null,
$class = "final_page",
$anchor = ""
): ?string {
if (!$this->pages)
return null;
if (!empty($anchor))
$anchor = '#'.$anchor;
fallback($text, __("Final"));
return '<a rel="next" class="'.
fix($class, true).
'" id="pagination_final_'.
$this->name.
'" href="'.
$this->next_page_url($this->pages).
fix($anchor, true).
'">'.$text.'</a>';
}
/**
* Function: first_link
* Outputs a link to the first page.
*
* Parameters:
* $text - The text for the link.
* $class - The CSS class for the link.
* $anchor - An anchor target.
*/
public function first_link(
$text = null,
$class = "first_page",
$anchor = ""
): ?string {
if (!$this->pages)
return null;
if (!empty($anchor))
$anchor = '#'.$anchor;
fallback($text, __("First"));
return '<a rel="prev" class="'.
fix($class, true).
'" id="pagination_first_'.
$this->name.
'" href="'.
$this->prev_page_url(1).
fix($anchor, true).
'">'.$text.'</a>';
}
/**
* Function: next_page_url
* Returns the URL to the next page.
*
* Parameters:
* $page - Page number to link to.
*/
public function next_page_url($page = null): string {
$config = Config::current();
$route = Route::current();
$request = unfix(self_url());
# Determine how we should append the page to dirty URLs.
$mark = (substr_count($request, "?")) ? "&" : "?" ;
fallback(
$page,
($this->next_page() ? $this->page + 1 : $this->pages)
);
# Generate a URL with the page number appended or replaced.
$url = !isset($_GET[$this->name]) ?
(
($config->clean_urls and $route->controller->clean_urls) ?
rtrim($request, "/")."/".$this->name."/".$page."/" :
$request.$mark.$this->name."=".$page
)
:
(
($config->clean_urls and $route->controller->clean_urls) ?
preg_replace(
"/(\/{$this->name}\/([0-9]+)|$)/",
"/".$this->name."/".$page,
$request,
1
)
:
preg_replace(
"/((\?|&){$this->name}=([0-9]+)|$)/",
"\\2".$this->name."=".$page,
$request,
1
)
)
;
return fix($url, true);
}
/**
* Function: prev_page_url
* Returns the URL to the previous page.
*
* Parameters:
* $page - Page number to link to.
*/
public function prev_page_url($page = null): string {
$config = Config::current();
$route = Route::current();
$request = unfix(self_url());
# Determine how we should append the page to dirty URLs.
$mark = (substr_count($request, "?")) ? "&" : "?" ;
fallback(
$page,
($this->prev_page() ? $this->page - 1 : 1)
);
# Generate a URL with the page number appended or replaced.
$url = !isset($_GET[$this->name]) ?
(
($config->clean_urls and $route->controller->clean_urls) ?
rtrim($request, "/")."/".$this->name."/".$page."/" :
$request.$mark.$this->name."=".$page
)
:
(
($config->clean_urls and $route->controller->clean_urls) ?
preg_replace(
"/(\/{$this->name}\/([0-9]+)|$)/",
"/".$this->name."/".$page,
$request,
1
)
:
preg_replace(
"/((\?|&){$this->name}=([0-9]+)|$)/",
"\\2".$this->name."=".$page,
$request,
1
)
)
;
return fix($url, true);
}
}