Skip to content

Commit

Permalink
Merge pull request #814 from hydephp/add-pagination-feature-to-public…
Browse files Browse the repository at this point in the history
…ations

Add pagination feature to publications
  • Loading branch information
caendesilva authored Jan 8, 2023
2 parents e5fc0fc + 1d76f96 commit 114c07e
Show file tree
Hide file tree
Showing 8 changed files with 665 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@php/** @var \Hyde\Framework\Features\Publications\Paginator $paginator */@endphp
<nav class="flex justify-between mt-4">
@if($paginator->previous())
<x-link :href="$paginator->previous()">Prev</x-link>
@else
<span class="opacity-75">Prev</span>
@endif

<div>
@foreach($paginator->getPageLinks() as $pageNumber => $destination)
@if($paginator->currentPage() === $pageNumber)
<strong>{{ $pageNumber }}</strong>
@else
<x-link :href="$destination">{{ $pageNumber }}</x-link>
@endif
@endforeach
</div>

@if($paginator->next())
<x-link :href="$paginator->next()">Next</x-link>
@else
<span class="opacity-75">Next</span>
@endif
</nav>
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
@php/** @var \Hyde\Framework\Features\Publications\Models\PublicationType $publicationType*/@endphp
@extends('hyde::layouts.app')
@section('content')
<main id="content" class="mx-auto max-w-7xl py-16 px-8">
<div class="prose dark:prose-invert">
@php/** @var \Hyde\Framework\Features\Publications\Models\PublicationType $publicationType*/@endphp
<h1>Publications for type {{ $publicationType->name }}</h1>

<ol>
@foreach($publicationType->getPublications() as $publication)
<li>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@php/** @var \Hyde\Framework\Features\Publications\Models\PublicationType $publicationType*/@endphp
@php
$paginator = $publicationType->getPaginator($page->matter('paginatorPage'));
@endphp
@extends('hyde::layouts.app')
@section('content')
<main id="content" class="mx-auto max-w-7xl py-16 px-8">
<div class="prose dark:prose-invert">
<h1>Publications for type {{ $publicationType->name }}</h1>

<ol start="{{ $paginator->firstItemNumberOnPage() }}">
@foreach($paginator->getItemsForPage() as $publication)
<li>
<x-link :href="$publication->getRoute()">{{ $publication->title }}</x-link>
</li>
@endforeach
</ol>

@include('hyde::components.publications.pagination-navigation')
</div>
</main>
@endsection
24 changes: 24 additions & 0 deletions packages/framework/src/Foundation/PageCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Hyde\Pages\HtmlPage;
use Hyde\Pages\MarkdownPage;
use Hyde\Pages\MarkdownPost;
use Hyde\Pages\VirtualPage;
use Illuminate\Support\Collection;

/**
Expand Down Expand Up @@ -126,5 +127,28 @@ protected function generatePublicationListingPageForType(PublicationType $type):
{
$page = new PublicationListPage($type);
$this->put($page->getSourcePath(), $page);

if ($type->usesPagination()) {
$this->generatePublicationPaginatedListingPagesForType($type);
}
}

/**
* @deprecated This method will be removed before merging into master.
*
* @internal This method will be removed before merging into master.
*/
protected function generatePublicationPaginatedListingPagesForType(PublicationType $type): void
{
$paginator = $type->getPaginator();

foreach (range(1, $paginator->totalPages()) as $page) {
$paginator->setCurrentPage($page);
$listingPage = new VirtualPage("{$type->getDirectory()}/page-$page", [
'publicationType' => $type, 'paginatorPage' => $page,
'title' => $type->name.' - Page '.$page,
], view: $type->listTemplate);
$this->put($listingPage->getSourcePath(), $listingPage);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
use function file_get_contents;
use function file_put_contents;
use Hyde\Framework\Concerns\InteractsWithDirectories;
use Hyde\Framework\Features\Publications\Paginator;
use Hyde\Framework\Features\Publications\PublicationService;
use Hyde\Hyde;
use Hyde\Pages\PublicationPage;
use Hyde\Support\Concerns\Serializable;
use Hyde\Support\Contracts\SerializableContract;
use Illuminate\Support\Collection;
Expand Down Expand Up @@ -144,11 +146,25 @@ public function getPublications(): Collection
return PublicationService::getPublicationsForPubType($this);
}

public function getPaginator(int $currentPageNumber = null): Paginator
{
return new Paginator($this->getPublicationsSortedByPaginationField(),
$this->pagination->pageSize,
$currentPageNumber,
$this->getIdentifier()
);
}

public function getListPage(): PublicationListPage
{
return new PublicationListPage($this);
}

public function usesPagination(): bool
{
return ($this->pagination->pageSize > 0) && ($this->pagination->pageSize < $this->getPublications()->count());
}

public function save(?string $path = null): void
{
$path ??= $this->getSchemaFile();
Expand All @@ -165,4 +181,11 @@ protected static function getRelativeDirectoryEntry(string $schemaFile): array
{
return ['directory' => Hyde::pathToRelative(dirname($schemaFile))];
}

protected function getPublicationsSortedByPaginationField(): Collection
{
return $this->getPublications()->sortBy(function (PublicationPage $page): mixed {
return $page->matter($this->pagination->sortField);
}, descending: ! $this->pagination->sortAscending)->values();
}
}
210 changes: 210 additions & 0 deletions packages/framework/src/Framework/Features/Publications/Paginator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<?php

declare(strict_types=1);

namespace Hyde\Framework\Features\Publications;

use function collect;
use Hyde\Hyde;
use Hyde\Support\Models\Route;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Collection;
use InvalidArgumentException;
use function range;
use function sprintf;

/**
* @see \Hyde\Framework\Testing\Feature\PaginatorTest
*/
class Paginator
{
protected Collection $paginatedItems;

protected int $pageSize = 25;
protected int $currentPage = 1;

/**
* Optionally provide a route basename to be used in generating the pagination links.
*/
protected string $routeBasename;

public function __construct(Arrayable|array $items = [], int $pageSize = 25, int $currentPageNumber = null, string $paginationRouteBasename = null)
{
$this->pageSize = $pageSize;

$this->generate(collect($items));

if ($currentPageNumber) {
$this->setCurrentPage($currentPageNumber);
}

if ($paginationRouteBasename) {
$this->routeBasename = $paginationRouteBasename;
}
}

protected function generate(Collection $items): void
{
$this->paginatedItems = $items->chunk($this->perPage());
}

/** Set the current page number. */
public function setCurrentPage(int $currentPage): Paginator
{
$this->validateCurrentPageValue($currentPage);

$this->currentPage = $currentPage;

return $this;
}

/** Get the current page number (which is used as a cursor). */
public function currentPage(): int
{
return $this->currentPage;
}

/** Get the paginated collection */
public function getPaginatedItems(): Collection
{
return $this->paginatedItems;
}

public function getItemsForPage(): Collection
{
return $this->paginatedItems->get($this->currentPage - 1);
}

public function getPageLinks(): array
{
$array = [];
$pageRange = range(1, $this->totalPages());
if ($this->routeBasename) {
foreach ($pageRange as $number) {
$array[$number] = Route::getOrFail("$this->routeBasename/page-$number");
}
} else {
foreach ($pageRange as $number) {
$array[$number] = Hyde::formatLink("page-$number.html");
}
}

return $array;
}

/** The number of items to be shown per page. */
public function perPage(): int
{
return $this->pageSize;
}

/** Get the total number of pages. */
public function totalPages(): int
{
return $this->paginatedItems->count();
}

/** Determine if there are enough items to split into multiple pages. */
public function hasMultiplePages(): bool
{
return $this->totalPages() > 1;
}

/** Get the page number of the last available page. */
public function lastPage(): int
{
return $this->totalPages();
}

/** Determine if there are fewer items after the cursor in the data store. */
public function canNavigateBack(): bool
{
return $this->currentPage > $this->firstPage();
}

/** Determine if there are more items after the cursor in the data store. */
public function canNavigateForward(): bool
{
return $this->currentPage < $this->lastPage();
}

public function previousPageNumber(): false|int
{
if (! $this->canNavigateBack()) {
return false;
}

return $this->currentPage - 1;
}

public function nextPageNumber(): false|int
{
if (! $this->canNavigateForward()) {
return false;
}

return $this->currentPage + 1;
}

public function previous(): false|string|Route
{
if (! $this->canNavigateBack()) {
return false;
}

if (! isset($this->routeBasename)) {
return $this->formatLink(-1);
}

return $this->getRoute(-1);
}

public function next(): false|string|Route
{
if (! $this->canNavigateForward()) {
return false;
}

if (! isset($this->routeBasename)) {
return $this->formatLink(+1);
}

return $this->getRoute(+1);
}

public function firstItemNumberOnPage(): int
{
return (($this->currentPage - 1) * $this->perPage()) + 1;
}

protected function validateCurrentPageValue(int $currentPage): void
{
if ($currentPage < $this->firstPage()) {
throw new InvalidArgumentException('Current page number must be greater than 0.');
}

if ($currentPage > $this->lastPage()) {
throw new InvalidArgumentException('Current page number must be less than or equal to the last page number.');
}
}

protected function formatPageName(int $offset): string
{
return sprintf('page-%d', $this->currentPage + $offset);
}

protected function formatLink(int $offset): string
{
return Hyde::formatLink("{$this->formatPageName($offset)}.html");
}

protected function getRoute(int $offset): ?Route
{
return Route::get("$this->routeBasename/{$this->formatPageName($offset)}");
}

protected function firstPage(): int
{
return 1;
}
}
Loading

0 comments on commit 114c07e

Please sign in to comment.