<?php
declare(strict_types=1);
namespace Domain\ValueObject;
// @responsibility: Pagination Value Object (immutable)
use Domain\Constants;
final class Pagination
{
public function __construct(
public readonly int $page,
public readonly int $limit,
public readonly int $offset,
public readonly int $totalCount = 0
) {
}
/**
* Create pagination from page and limit values.
*
* @param int $page Current page number (1-based)
* @param int $limit Items per page
* @param int $maxLimit Maximum allowed limit
*/
public static function create(int $page = 1, int $limit = 50, int $maxLimit = 100): self
{
$page = max(1, $page);
$limit = min($maxLimit, max(1, $limit));
return new self($page, $limit, ($page - 1) * $limit);
}
/**
* Create new instance with total count set.
*/
public function withTotal(int $count): self
{
return new self($this->page, $this->limit, $this->offset, $count);
}
/**
* Calculate total number of pages.
*/
public function totalPages(): int
{
if ($this->totalCount === 0) {
return 0;
}
return (int) ceil($this->totalCount / $this->limit);
}
/**
* Check if there is a next page.
*/
public function hasNextPage(): bool
{
return $this->page < $this->totalPages();
}
/**
* Check if there is a previous page.
*/
public function hasPrevPage(): bool
{
return $this->page > 1;
}
/**
* Get next page number (or current if at last page).
*/
public function nextPage(): int
{
return $this->hasNextPage() ? $this->page + 1 : $this->page;
}
/**
* Get previous page number (or 1 if at first page).
*/
public function prevPage(): int
{
return $this->hasPrevPage() ? $this->page - 1 : 1;
}
/**
* Get range of visible page numbers for pagination UI.
*
* @param int $range Number of pages to show around current page
* @return array<int>
*/
public function getPageRange(int $range = 2): array
{
$totalPages = $this->totalPages();
if ($totalPages === 0) {
return [];
}
$start = max(1, $this->page - $range);
$end = min($totalPages, $this->page + $range);
return range($start, $end);
}
/**
* Convert to array for template usage.
*
* @return array{page: int, limit: int, offset: int, totalCount: int, totalPages: int, hasNext: bool, hasPrev: bool}
*/
public function toArray(): array
{
return [
'page' => $this->page,
'limit' => $this->limit,
'offset' => $this->offset,
'totalCount' => $this->totalCount,
'totalPages' => $this->totalPages(),
'hasNext' => $this->hasNextPage(),
'hasPrev' => $this->hasPrevPage(),
];
}
}