From 3051d8d25caafd517a66857fe571a539d5f6a22a Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 26 Nov 2024 18:15:20 +0300 Subject: [PATCH 01/11] Implement --- src/BaseListView.php | 89 +++++++++------ src/Pagination/BasePagination.php | 101 ++---------------- src/Pagination/KeysetPagination.php | 16 +-- src/Pagination/OffsetPagination.php | 15 ++- src/Pagination/PaginationContext.php | 5 + src/Pagination/PaginationContextTrait.php | 30 ++++++ src/Pagination/PaginationControlInterface.php | 20 ++++ .../PaginatorNotSetException.php | 2 +- .../PaginatorNotSupportedException.php | 20 ++++ tests/GridView/BaseTest.php | 3 - tests/GridView/ImmutableTest.php | 3 +- tests/ListView/BaseTest.php | 14 +-- tests/Pagination/BaseTest.php | 75 ------------- tests/Pagination/ExceptionTest.php | 5 +- tests/Pagination/ImmutableTest.php | 24 ----- tests/Pagination/KeysetPaginationTest.php | 28 ++--- tests/Pagination/OffsetPaginationTest.php | 4 +- 17 files changed, 194 insertions(+), 260 deletions(-) create mode 100644 src/Pagination/PaginationContextTrait.php create mode 100644 src/Pagination/PaginationControlInterface.php rename src/{Exception => Pagination}/PaginatorNotSetException.php (95%) create mode 100644 src/Pagination/PaginatorNotSupportedException.php delete mode 100644 tests/Pagination/BaseTest.php diff --git a/src/BaseListView.php b/src/BaseListView.php index 866ceca6a..1ebc44c99 100644 --- a/src/BaseListView.php +++ b/src/BaseListView.php @@ -40,6 +40,9 @@ use Yiisoft\Yii\DataView\Pagination\KeysetPagination; use Yiisoft\Yii\DataView\Pagination\OffsetPagination; use Yiisoft\Yii\DataView\Pagination\PaginationContext; +use Yiisoft\Yii\DataView\Pagination\PaginationControlInterface; + +use Yiisoft\Yii\DataView\Pagination\PaginatorNotSupportedException; use function array_key_exists; use function array_slice; @@ -48,7 +51,6 @@ use function in_array; use function is_array; use function is_int; -use function is_string; /** * @psalm-type UrlArguments = array @@ -126,7 +128,7 @@ abstract class BaseListView extends Widget private array $offsetPaginationConfig = []; private array $keysetPaginationConfig = []; - private string|OffsetPagination|KeysetPagination|null $pagination = null; + private PaginationControlInterface|null $paginationControl = null; protected ?ReadableDataInterface $dataReader = null; private string $toolbar = ''; @@ -553,10 +555,10 @@ final public function pageSizeControl(?PageSizeControlInterface $widget): static return $new; } - public function pagination(string|KeysetPagination|OffsetPagination|null $pagination): static + public function paginationControl(PaginationControlInterface|null $widget): static { $new = clone $this; - $new->pagination = $pagination; + $new->paginationControl = $widget; return $new; } @@ -781,48 +783,67 @@ protected function getOverrideOrderFields(): array private function renderPagination(): string { - $preparedDataReader = $this->preparedDataReader; - if (!$preparedDataReader instanceof PaginatorInterface || !$preparedDataReader->isPaginationRequired()) { + $dataReader = $this->preparedDataReader; + if (!$dataReader instanceof PaginatorInterface || !$dataReader->isPaginationRequired()) { return ''; } - if (is_string($this->pagination)) { - return $this->pagination; - } - - if ($this->pagination === null) { - if ($preparedDataReader instanceof OffsetPaginator) { - $pagination = OffsetPagination::widget(config: $this->offsetPaginationConfig); - } elseif ($preparedDataReader instanceof KeysetPaginator) { - $pagination = KeysetPagination::widget(config: $this->keysetPaginationConfig); + if ($this->paginationControl === null) { + if ($dataReader instanceof OffsetPaginator) { + $widget = OffsetPagination::widget(config: $this->offsetPaginationConfig); + } elseif ($dataReader instanceof KeysetPaginator) { + $widget = KeysetPagination::widget(config: $this->keysetPaginationConfig); } else { return ''; } + try { + $widget = $widget->withPaginator($dataReader); + } catch (PaginatorNotSupportedException) { + return ''; + } } else { - $pagination = $this->pagination; + $widget = $this->paginationControl->withPaginator($dataReader); } - if ($pagination instanceof OffsetPagination && $preparedDataReader instanceof OffsetPaginator) { - $pagination = $pagination->paginator($preparedDataReader); - } elseif ($pagination instanceof KeysetPagination && $preparedDataReader instanceof KeysetPaginator) { - $pagination = $pagination->paginator($preparedDataReader); + if ($this->urlCreator === null) { + $nextUrlPattern = '#page=' . PaginationContext::URL_PLACEHOLDER; + $previousUrlPattern = '#previous-page=' . PaginationContext::URL_PLACEHOLDER; + $defaultUrl = '#'; } else { - return ''; - } - - if ($this->urlCreator !== null) { - $pagination = $pagination->urlCreator($this->urlCreator); + $pageSize = $this->getPageSizeValueForUrl($dataReader); + $sort = $this->getSortValueForUrl($dataReader); + $nextUrlPattern = call_user_func_array( + $this->urlCreator, + UrlParametersFactory::create( + PageToken::next(PaginationContext::URL_PLACEHOLDER), + $pageSize, + $sort, + $this->urlConfig + ), + ); + $previousUrlPattern = call_user_func_array( + $this->urlCreator, + UrlParametersFactory::create( + PageToken::previous(PaginationContext::URL_PLACEHOLDER), + $pageSize, + $sort, + $this->urlConfig + ), + ); + $defaultUrl = call_user_func_array( + $this->urlCreator, + UrlParametersFactory::create(null, $pageSize, $sort, $this->urlConfig), + ); } $context = new PaginationContext( $this->getOverrideOrderFields(), + $nextUrlPattern, + $previousUrlPattern, + $defaultUrl, ); - return $pagination - ->context($context) - ->defaultPageSize($this->getDefaultPageSize()) - ->urlConfig($this->urlConfig) - ->render(); + return $widget->withContext($context)->render(); } private function renderPageSize(): string @@ -1003,6 +1024,14 @@ private function createDefaultTranslator(): Translator return $translator; } + private function getPageSizeValueForUrl(PaginatorInterface $paginator): ?string + { + $pageSize = $paginator->getPageSize(); + return $pageSize === $this->getDefaultPageSize() + ? null + : (string) $pageSize; + } + private function getSortValueForUrl(PaginatorInterface $paginator): ?string { $sort = $this->getSort($paginator); diff --git a/src/Pagination/BasePagination.php b/src/Pagination/BasePagination.php index 6a1c1d17e..ae9b5a34c 100644 --- a/src/Pagination/BasePagination.php +++ b/src/Pagination/BasePagination.php @@ -6,31 +6,16 @@ use InvalidArgumentException; use Yiisoft\Data\Paginator\PageToken; -use Yiisoft\Data\Paginator\PaginatorInterface; -use Yiisoft\Data\Reader\OrderHelper; use Yiisoft\Html\Html; use Yiisoft\Widget\Widget; use Yiisoft\Yii\DataView\BaseListView; -use Yiisoft\Yii\DataView\UrlConfig; -use Yiisoft\Yii\DataView\UrlParametersFactory; - -use function array_key_exists; -use function call_user_func_array; /** * @psalm-import-type UrlCreator from BaseListView */ -abstract class BasePagination extends Widget +abstract class BasePagination extends Widget implements PaginationControlInterface { - private ?PaginationContext $context = null; - - /** - * @psalm-var UrlCreator|null - */ - private $urlCreator; - private UrlConfig $urlConfig; - - private int $defaultPageSize = PaginatorInterface::DEFAULT_PAGE_SIZE; + use PaginationContextTrait; /** * @psalm-var non-empty-string|null @@ -56,13 +41,6 @@ abstract class BasePagination extends Widget private ?string $disabledItemClass = null; private ?string $disabledLinkClass = null; - final public function context(?PaginationContext $context): static - { - $new = clone $this; - $new->context = $context; - return $new; - } - final public function listTag(?string $tag): static { if ($tag === '') { @@ -189,30 +167,6 @@ final public function render(): string return $result; } - /** - * @psalm-param UrlCreator|null $urlCreator - */ - public function urlCreator(?callable $urlCreator): static - { - $new = clone $this; - $new->urlCreator = $urlCreator; - return $new; - } - - final public function defaultPageSize(int $size): static - { - $new = clone $this; - $new->defaultPageSize = $size; - return $new; - } - - public function urlConfig(UrlConfig $config): static - { - $new = clone $this; - $new->urlConfig = $config; - return $new; - } - /** * Creates the URL suitable for pagination with the specified page number. This method is mainly called by pagers * when creating URLs used to perform pagination. @@ -222,22 +176,16 @@ public function urlConfig(UrlConfig $config): static */ protected function createUrl(PageToken $pageToken): string { - if ($this->urlCreator === null) { - return '#' . $pageToken->value; + $context = $this->getContext(); + + if ($this->isFirstPage($pageToken)) { + return $context->defaultUrl; } - $paginator = $this->getPaginator(); - $pageSize = $paginator->getPageSize(); - $sort = $this->getSort($paginator, $this->context); - - return call_user_func_array( - $this->urlCreator, - UrlParametersFactory::create( - $this->isFirstPage($pageToken) ? null : $pageToken, - $pageSize === $this->defaultPageSize ? null : $pageSize, - empty($sort) ? null : $sort, - $this->urlConfig, - ) + return str_replace( + PaginationContext::URL_PLACEHOLDER, + $pageToken->value, + $pageToken->isPrevious ? $context->previousUrlPattern : $context->nextUrlPattern, ); } @@ -246,34 +194,5 @@ protected function createUrl(PageToken $pageToken): string */ abstract protected function getItems(): array; - abstract protected function getPaginator(): PaginatorInterface; - abstract protected function isFirstPage(PageToken $token): bool; - - private function getSort(PaginatorInterface $paginator, ?PaginationContext $context): ?string - { - if (!$paginator->isSortable()) { - return null; - } - - $sort = $paginator->getSort(); - if ($sort === null) { - return null; - } - - if ($context === null) { - return $sort->getOrderAsString(); - } - - $order = []; - $overrideOrderFields = array_flip($context->overrideOrderFields); - foreach ($sort->getOrder() as $name => $value) { - $key = array_key_exists($name, $overrideOrderFields) - ? $overrideOrderFields[$name] - : $name; - $order[$key] = $value; - } - - return OrderHelper::arrayToString($order); - } } diff --git a/src/Pagination/KeysetPagination.php b/src/Pagination/KeysetPagination.php index 6e471f83b..5c405c077 100644 --- a/src/Pagination/KeysetPagination.php +++ b/src/Pagination/KeysetPagination.php @@ -7,7 +7,7 @@ use Stringable; use Yiisoft\Data\Paginator\KeysetPaginator; use Yiisoft\Data\Paginator\PageToken; -use Yiisoft\Yii\DataView\Exception; +use Yiisoft\Data\Paginator\PaginatorInterface; final class KeysetPagination extends BasePagination { @@ -16,8 +16,12 @@ final class KeysetPagination extends BasePagination private string|Stringable $labelPrevious = '⟨'; private string|Stringable $labelNext = '⟩'; - public function paginator(KeysetPaginator $paginator): self + public function withPaginator(PaginatorInterface $paginator): static { + if (!$paginator instanceof KeysetPaginator) { + throw new PaginatorNotSupportedException($paginator); + } + $new = clone $this; $new->paginator = $paginator; return $new; @@ -45,13 +49,13 @@ protected function getItems(): array ]; } - protected function getPaginator(): KeysetPaginator + protected function isFirstPage(PageToken $token): bool { - return $this->paginator ?? throw new Exception\PaginatorNotSetException(); + return false; } - protected function isFirstPage(PageToken $token): bool + private function getPaginator(): KeysetPaginator { - return false; + return $this->paginator ?? throw new PaginatorNotSetException(); } } diff --git a/src/Pagination/OffsetPagination.php b/src/Pagination/OffsetPagination.php index d7e3a71c9..30c12148d 100644 --- a/src/Pagination/OffsetPagination.php +++ b/src/Pagination/OffsetPagination.php @@ -8,6 +8,7 @@ use Stringable; use Yiisoft\Data\Paginator\PageToken; use Yiisoft\Data\Paginator\OffsetPaginator; +use Yiisoft\Data\Paginator\PaginatorInterface; use Yiisoft\Yii\DataView\Exception; use function max; @@ -24,8 +25,12 @@ final class OffsetPagination extends BasePagination private int $maxNavLinkCount = 10; - public function paginator(OffsetPaginator $paginator): self + public function withPaginator(PaginatorInterface $paginator): static { + if (!$paginator instanceof OffsetPaginator) { + throw new PaginatorNotSupportedException($paginator); + } + $new = clone $this; $new->paginator = $paginator; return $new; @@ -149,13 +154,13 @@ protected function getItems(): array return $items; } - protected function getPaginator(): OffsetPaginator + protected function isFirstPage(PageToken $token): bool { - return $this->paginator ?? throw new Exception\PaginatorNotSetException(); + return $token->value === '1'; } - protected function isFirstPage(PageToken $token): bool + private function getPaginator(): OffsetPaginator { - return $token->value === '1'; + return $this->paginator ?? throw new PaginatorNotSetException(); } } diff --git a/src/Pagination/PaginationContext.php b/src/Pagination/PaginationContext.php index 94a253796..b761d6987 100644 --- a/src/Pagination/PaginationContext.php +++ b/src/Pagination/PaginationContext.php @@ -6,6 +6,8 @@ final class PaginationContext { + public const URL_PLACEHOLDER = 'YII-DATAVIEW-PAGE-PLACEHOLDER'; + /** * @internal * @@ -13,6 +15,9 @@ final class PaginationContext */ public function __construct( public readonly array $overrideOrderFields, + public readonly string $nextUrlPattern, + public readonly string $previousUrlPattern, + public readonly string $defaultUrl, ) { } } diff --git a/src/Pagination/PaginationContextTrait.php b/src/Pagination/PaginationContextTrait.php new file mode 100644 index 000000000..c46571062 --- /dev/null +++ b/src/Pagination/PaginationContextTrait.php @@ -0,0 +1,30 @@ +context = $context; + return $new; + } + + final protected function getContext(): PaginationContext + { + if ($this->context === null) { + throw new LogicException('Context is not set.'); + } + return $this->context; + } +} diff --git a/src/Pagination/PaginationControlInterface.php b/src/Pagination/PaginationControlInterface.php new file mode 100644 index 000000000..1f5f9b8d2 --- /dev/null +++ b/src/Pagination/PaginationControlInterface.php @@ -0,0 +1,20 @@ +id('w1-grid') ->dataReader($paginator) - ->offsetPaginationConfig([ - 'urlCreator()' => [new SimplePaginationUrlCreator()], - ]) ->tableAttributes(['class' => 'table table-striped table-bordered']) ->render(); diff --git a/tests/GridView/ImmutableTest.php b/tests/GridView/ImmutableTest.php index aba49c176..bc3726129 100644 --- a/tests/GridView/ImmutableTest.php +++ b/tests/GridView/ImmutableTest.php @@ -11,6 +11,7 @@ use Yiisoft\Factory\NotFoundException; use Yiisoft\Yii\DataView; use Yiisoft\Yii\DataView\Column\DataColumn; +use Yiisoft\Yii\DataView\Pagination\OffsetPagination; use Yiisoft\Yii\DataView\Tests\Support\TestTrait; final class ImmutableTest extends TestCase @@ -33,7 +34,7 @@ public function testBaseListView(): void $this->assertNotSame($baseListView, $baseListView->headerAttributes([])); $this->assertNotSame($baseListView, $baseListView->id('')); $this->assertNotSame($baseListView, $baseListView->layout('')); - $this->assertNotSame($baseListView, $baseListView->pagination('')); + $this->assertNotSame($baseListView, $baseListView->paginationControl(OffsetPagination::widget())); $this->assertNotSame($baseListView, $baseListView->dataReader($this->createOffsetPaginator($this->data, 10))); $this->assertNotSame($baseListView, $baseListView->summaryTemplate('')); $this->assertNotSame($baseListView, $baseListView->summaryAttributes([])); diff --git a/tests/ListView/BaseTest.php b/tests/ListView/BaseTest.php index 4e9dd7557..1749dde16 100644 --- a/tests/ListView/BaseTest.php +++ b/tests/ListView/BaseTest.php @@ -233,12 +233,12 @@ public function testOffsetPaginationConfig(): void
Page 1 of 2
@@ -275,7 +275,7 @@ public function testKeysetPaginationConfig(): void diff --git a/tests/Pagination/BaseTest.php b/tests/Pagination/BaseTest.php deleted file mode 100644 index 9512a183a..000000000 --- a/tests/Pagination/BaseTest.php +++ /dev/null @@ -1,75 +0,0 @@ - 1, 'name' => 'John', 'age' => 20], - ['id' => 2, 'name' => 'Mary', 'age' => 21], - ['id' => 3, 'name' => 'Samdark', 'age' => 35], - ['id' => 4, 'name' => 'joe', 'age' => 41], - ['id' => 5, 'name' => 'Alexey', 'age' => 32], - ]; - - /** - * @throws InvalidConfigException - */ - protected function setUp(): void - { - parent::setUp(); - - $container = new Container(ContainerConfig::create()); - WidgetFactory::initialize($container, [UrlGeneratorInterface::class => Mock::urlGenerator()]); - } - - /** - * @throws InvalidConfigException - * @throws NotFoundException - * @throws NotInstantiableException - * @throws CircularReferenceException - */ - public function testRenderWithUrlQueryParametersWithoutUrlName(): void - { - Assert::equalsWithoutLE( - << - - - 1 - 2 - 3 - 4 - 5 - - - - HTML, - OffsetPagination::widget() - ->paginator($this->createOffsetPaginator($this->data, 1)) - ->urlCreator(new SimplePaginationUrlCreator()) - ->urlConfig(new UrlConfig(queryParameters: ['filter' => 'test'])) - ->render(), - ); - } -} diff --git a/tests/Pagination/ExceptionTest.php b/tests/Pagination/ExceptionTest.php index 137ee6616..d5557789b 100644 --- a/tests/Pagination/ExceptionTest.php +++ b/tests/Pagination/ExceptionTest.php @@ -45,8 +45,11 @@ protected function setUp(): void */ public function testCurrentPageOutOfRange(): void { + $paginator = $this->createOffsetPaginator($this->data, 2, 4); + $widget = OffsetPagination::widget()->withPaginator($paginator); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Current page must be less than or equal to total pages.'); - OffsetPagination::widget()->paginator($this->createOffsetPaginator($this->data, 2, 4))->render(); + $widget->render(); } } diff --git a/tests/Pagination/ImmutableTest.php b/tests/Pagination/ImmutableTest.php index 245712dfc..8c5e6b4a0 100644 --- a/tests/Pagination/ImmutableTest.php +++ b/tests/Pagination/ImmutableTest.php @@ -5,37 +5,13 @@ namespace Yiisoft\Yii\DataView\Tests\Pagination; use PHPUnit\Framework\TestCase; -use Yiisoft\Data\Paginator\PageToken; -use Yiisoft\Data\Paginator\PaginatorInterface; -use Yiisoft\Yii\DataView\Pagination\BasePagination; use Yiisoft\Yii\DataView\Pagination\OffsetPagination; use Yiisoft\Yii\DataView\Tests\Support\TestTrait; -use Yiisoft\Yii\DataView\UrlConfig; final class ImmutableTest extends TestCase { use TestTrait; - public function testBasePagination(): void - { - $basePagination = new class () extends BasePagination { - protected function getPaginator(): PaginatorInterface - { - } - - protected function getItems(): array - { - return []; - } - - protected function isFirstPage(PageToken $token): bool - { - return false; - } - }; - $this->assertNotSame($basePagination, $basePagination->urlConfig(new UrlConfig())); - } - public function testOffsetPagination(): void { $offsetPagination = OffsetPagination::widget(); diff --git a/tests/Pagination/KeysetPaginationTest.php b/tests/Pagination/KeysetPaginationTest.php index 254ceadff..088dcf66e 100644 --- a/tests/Pagination/KeysetPaginationTest.php +++ b/tests/Pagination/KeysetPaginationTest.php @@ -11,7 +11,7 @@ use Yiisoft\Definitions\Exception\NotInstantiableException; use Yiisoft\Factory\NotFoundException; use Yiisoft\Yii\DataView\Column\DataColumn; -use Yiisoft\Yii\DataView\Exception\PaginatorNotSetException; +use Yiisoft\Yii\DataView\Pagination\PaginatorNotSetException; use Yiisoft\Yii\DataView\GridView; use Yiisoft\Yii\DataView\Pagination\KeysetPagination; use Yiisoft\Yii\DataView\Tests\Support\Assert; @@ -64,7 +64,7 @@ public function testRenderPaginatorEmptyData(): void GridView::widget() ->id('w1-grid') ->dataReader($keysetPaginator) - ->pagination(KeysetPagination::widget()->paginator($keysetPaginator)->render()) + ->paginationControl(KeysetPagination::widget()) ->render(), ); } @@ -114,7 +114,7 @@ public function testRenderPaginationLinks(): void HTML, @@ -126,7 +126,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->pagination(KeysetPagination::widget()->paginator($keysetPaginator)->render()) + ->paginationControl(KeysetPagination::widget()->withPaginator($keysetPaginator)) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); @@ -173,8 +173,8 @@ public function testRenderPaginationLinks(): void HTML, @@ -186,7 +186,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->pagination(KeysetPagination::widget()->paginator($keysetPaginator)->render()) + ->paginationControl(KeysetPagination::widget()) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); @@ -218,7 +218,7 @@ public function testRenderPaginationLinks(): void @@ -231,7 +231,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->pagination(KeysetPagination::widget()->paginator($keysetPaginator)->render()) + ->paginationControl(KeysetPagination::widget()) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); @@ -278,8 +278,8 @@ public function testRenderPaginationLinks(): void HTML, @@ -291,7 +291,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->pagination(KeysetPagination::widget()->paginator($keysetPaginator)->render()) + ->paginationControl(KeysetPagination::widget()) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); @@ -339,7 +339,7 @@ public function testRenderPaginationLinks(): void HTML, @@ -351,7 +351,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->pagination(KeysetPagination::widget()->paginator($keysetPaginator)->render()) + ->paginationControl(KeysetPagination::widget()) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); diff --git a/tests/Pagination/OffsetPaginationTest.php b/tests/Pagination/OffsetPaginationTest.php index a15520837..03e37fe65 100644 --- a/tests/Pagination/OffsetPaginationTest.php +++ b/tests/Pagination/OffsetPaginationTest.php @@ -9,7 +9,7 @@ use Yiisoft\Definitions\Exception\InvalidConfigException; use Yiisoft\Definitions\Exception\NotInstantiableException; use Yiisoft\Factory\NotFoundException; -use Yiisoft\Yii\DataView\Exception\PaginatorNotSetException; +use Yiisoft\Yii\DataView\Pagination\PaginatorNotSetException; use Yiisoft\Yii\DataView\GridView; use Yiisoft\Yii\DataView\Pagination\OffsetPagination; use Yiisoft\Yii\DataView\Tests\Support\Assert; @@ -47,7 +47,7 @@ public function testRenderPaginatorEmptyData(): void GridView::widget() ->id('w1-grid') ->dataReader($offsetPaginator) - ->pagination(OffsetPagination::widget()->paginator($offsetPaginator)->render()) + ->paginationControl(OffsetPagination::widget()) ->render(), ); } From 268706c233a3aab63bc5206b47aa92582235509f Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Tue, 26 Nov 2024 15:15:48 +0000 Subject: [PATCH 02/11] Apply fixes from StyleCI --- src/Pagination/OffsetPagination.php | 1 - tests/GridView/BaseTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Pagination/OffsetPagination.php b/src/Pagination/OffsetPagination.php index 30c12148d..a2efcd692 100644 --- a/src/Pagination/OffsetPagination.php +++ b/src/Pagination/OffsetPagination.php @@ -9,7 +9,6 @@ use Yiisoft\Data\Paginator\PageToken; use Yiisoft\Data\Paginator\OffsetPaginator; use Yiisoft\Data\Paginator\PaginatorInterface; -use Yiisoft\Yii\DataView\Exception; use function max; use function min; diff --git a/tests/GridView/BaseTest.php b/tests/GridView/BaseTest.php index 3e2afe1d8..dd64d1a97 100644 --- a/tests/GridView/BaseTest.php +++ b/tests/GridView/BaseTest.php @@ -18,7 +18,6 @@ use Yiisoft\Data\Paginator\OffsetPaginator; use Yiisoft\Data\Reader\Iterable\IterableDataReader; use Yiisoft\Data\Reader\Sort; -use Yiisoft\Yii\DataView\Tests\Support\SimplePaginationUrlCreator; final class BaseTest extends TestCase { From 7e1e287b8138705610e0861c7d6b89d3a6c6d97e Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Wed, 27 Nov 2024 14:58:46 +0300 Subject: [PATCH 03/11] Add `PaginationContext::createUrl()` --- src/Pagination/BasePagination.php | 29 ---------------------------- src/Pagination/KeysetPagination.php | 10 +++------- src/Pagination/OffsetPagination.php | 11 +++++++++-- src/Pagination/PaginationContext.php | 15 ++++++++++++++ 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/src/Pagination/BasePagination.php b/src/Pagination/BasePagination.php index ae9b5a34c..683995898 100644 --- a/src/Pagination/BasePagination.php +++ b/src/Pagination/BasePagination.php @@ -5,14 +5,9 @@ namespace Yiisoft\Yii\DataView\Pagination; use InvalidArgumentException; -use Yiisoft\Data\Paginator\PageToken; use Yiisoft\Html\Html; use Yiisoft\Widget\Widget; -use Yiisoft\Yii\DataView\BaseListView; -/** - * @psalm-import-type UrlCreator from BaseListView - */ abstract class BasePagination extends Widget implements PaginationControlInterface { use PaginationContextTrait; @@ -167,32 +162,8 @@ final public function render(): string return $result; } - /** - * Creates the URL suitable for pagination with the specified page number. This method is mainly called by pagers - * when creating URLs used to perform pagination. - * - * @param PageToken $pageToken Token for the page. - * @return string Created URL. - */ - protected function createUrl(PageToken $pageToken): string - { - $context = $this->getContext(); - - if ($this->isFirstPage($pageToken)) { - return $context->defaultUrl; - } - - return str_replace( - PaginationContext::URL_PLACEHOLDER, - $pageToken->value, - $pageToken->isPrevious ? $context->previousUrlPattern : $context->nextUrlPattern, - ); - } - /** * @return PaginationItem[] */ abstract protected function getItems(): array; - - abstract protected function isFirstPage(PageToken $token): bool; } diff --git a/src/Pagination/KeysetPagination.php b/src/Pagination/KeysetPagination.php index 5c405c077..1df0f7929 100644 --- a/src/Pagination/KeysetPagination.php +++ b/src/Pagination/KeysetPagination.php @@ -29,6 +29,7 @@ public function withPaginator(PaginatorInterface $paginator): static protected function getItems(): array { + $context = $this->getContext(); $paginator = $this->getPaginator(); $previousToken = $paginator->getPreviousToken(); $nextToken = $paginator->getNextToken(); @@ -36,24 +37,19 @@ protected function getItems(): array return [ new PaginationItem( label: $this->labelPrevious, - url: $previousToken === null ? null : $this->createUrl($previousToken), + url: $previousToken === null ? null : $context->createUrl($previousToken), isCurrent: false, isDisabled: $previousToken === null, ), new PaginationItem( label: $this->labelNext, - url: $nextToken === null ? null : $this->createUrl($nextToken), + url: $nextToken === null ? null : $context->createUrl($nextToken), isCurrent: false, isDisabled: $nextToken === null, ), ]; } - protected function isFirstPage(PageToken $token): bool - { - return false; - } - private function getPaginator(): KeysetPaginator { return $this->paginator ?? throw new PaginatorNotSetException(); diff --git a/src/Pagination/OffsetPagination.php b/src/Pagination/OffsetPagination.php index 30c12148d..e98347fc9 100644 --- a/src/Pagination/OffsetPagination.php +++ b/src/Pagination/OffsetPagination.php @@ -154,9 +154,16 @@ protected function getItems(): array return $items; } - protected function isFirstPage(PageToken $token): bool + /** + * @param PageToken $pageToken Token for the page. + * @return string Created URL. + */ + private function createUrl(PageToken $pageToken): string { - return $token->value === '1'; + $context = $this->getContext(); + return $pageToken->value === '1' + ? $context->defaultUrl + : $context->createUrl($pageToken); } private function getPaginator(): OffsetPaginator diff --git a/src/Pagination/PaginationContext.php b/src/Pagination/PaginationContext.php index b761d6987..42f1cd55c 100644 --- a/src/Pagination/PaginationContext.php +++ b/src/Pagination/PaginationContext.php @@ -4,6 +4,8 @@ namespace Yiisoft\Yii\DataView\Pagination; +use Yiisoft\Data\Paginator\PageToken; + final class PaginationContext { public const URL_PLACEHOLDER = 'YII-DATAVIEW-PAGE-PLACEHOLDER'; @@ -20,4 +22,17 @@ public function __construct( public readonly string $defaultUrl, ) { } + + /** + * @param PageToken $pageToken Token for the page. + * @return string Created URL. + */ + public function createUrl(PageToken $pageToken): string + { + return str_replace( + self::URL_PLACEHOLDER, + urlencode($pageToken->value), + $pageToken->isPrevious ? $this->previousUrlPattern : $this->nextUrlPattern, + ); + } } From 1a0898942ba3eee856ca3aa6277480205283c222 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Wed, 27 Nov 2024 12:00:01 +0000 Subject: [PATCH 04/11] Apply fixes from StyleCI --- src/Pagination/KeysetPagination.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Pagination/KeysetPagination.php b/src/Pagination/KeysetPagination.php index 1df0f7929..1016ac7a2 100644 --- a/src/Pagination/KeysetPagination.php +++ b/src/Pagination/KeysetPagination.php @@ -6,7 +6,6 @@ use Stringable; use Yiisoft\Data\Paginator\KeysetPaginator; -use Yiisoft\Data\Paginator\PageToken; use Yiisoft\Data\Paginator\PaginatorInterface; final class KeysetPagination extends BasePagination From a1712e1135cc6f8eb014b18a06aa368a95b1e75d Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Wed, 27 Nov 2024 18:13:06 +0300 Subject: [PATCH 05/11] improve `OffsetPagination` --- src/Pagination/OffsetPagination.php | 213 +++++++++++++++++++++--- src/Pagination/OffsetPaginationItem.php | 21 +++ 2 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 src/Pagination/OffsetPaginationItem.php diff --git a/src/Pagination/OffsetPagination.php b/src/Pagination/OffsetPagination.php index 26f74c92f..509d60e6e 100644 --- a/src/Pagination/OffsetPagination.php +++ b/src/Pagination/OffsetPagination.php @@ -9,14 +9,43 @@ use Yiisoft\Data\Paginator\PageToken; use Yiisoft\Data\Paginator\OffsetPaginator; use Yiisoft\Data\Paginator\PaginatorInterface; +use Yiisoft\Html\Html; +use Yiisoft\Widget\Widget; use function max; use function min; -final class OffsetPagination extends BasePagination +final class OffsetPagination extends Widget implements PaginationControlInterface { + use PaginationContextTrait; + private OffsetPaginator|null $paginator = null; + /** + * @psalm-var non-empty-string|null + */ + private ?string $containerTag = 'nav'; + private array $containerAttributes = []; + + /** + * @psalm-var non-empty-string|null + */ + private ?string $listTag = null; + private array $listAttributes = []; + + /** + * @psalm-var non-empty-string|null + */ + private ?string $itemTag = null; + private array $itemAttributes = []; + + private array $linkAttributes = []; + + private ?string $currentItemClass = null; + private ?string $currentLinkClass = null; + private ?string $disabledItemClass = null; + private ?string $disabledLinkClass = null; + private string|Stringable|null $labelPrevious = '⟨'; private string|Stringable|null $labelNext = '⟩'; private string|Stringable|null $labelFirst = '⟪'; @@ -35,6 +64,95 @@ public function withPaginator(PaginatorInterface $paginator): static return $new; } + public function containerTag(?string $tag): self + { + if ($tag === '') { + throw new InvalidArgumentException('Tag name cannot be empty.'); + } + + $new = clone $this; + $new->containerTag = $tag; + return $new; + } + + public function containerAttributes(array $attributes): self + { + $new = clone $this; + $new->containerAttributes = $attributes; + return $new; + } + + public function listTag(?string $tag): self + { + if ($tag === '') { + throw new InvalidArgumentException('Tag name cannot be empty.'); + } + + $new = clone $this; + $new->listTag = $tag; + return $new; + } + + public function listAttributes(array $attributes): self + { + $new = clone $this; + $new->listAttributes = $attributes; + return $new; + } + + public function itemTag(?string $tag): self + { + if ($tag === '') { + throw new InvalidArgumentException('Tag name cannot be empty.'); + } + + $new = clone $this; + $new->itemTag = $tag; + return $new; + } + + public function itemAttributes(array $attributes): self + { + $new = clone $this; + $new->itemAttributes = $attributes; + return $new; + } + + public function linkAttributes(array $attributes): self + { + $new = clone $this; + $new->linkAttributes = $attributes; + return $new; + } + + public function currentItemClass(?string $class): self + { + $new = clone $this; + $new->currentItemClass = $class; + return $new; + } + + public function currentLinkClass(?string $class): self + { + $new = clone $this; + $new->currentLinkClass = $class; + return $new; + } + + public function disabledItemClass(?string $class): self + { + $new = clone $this; + $new->disabledItemClass = $class; + return $new; + } + + public function disabledLinkClass(?string $class): self + { + $new = clone $this; + $new->disabledLinkClass = $class; + return $new; + } + public function labelPrevious(string|Stringable|null $label): self { $new = clone $this; @@ -72,30 +190,68 @@ public function maxNavLinkCount(int $value): self { $new = clone $this; $new->maxNavLinkCount = $value; - return $new; } - /** - * @psalm-return array - */ - protected function getPageRange(int $currentPage, int $totalPages): array + final public function render(): string { - $beginPage = max(1, $currentPage - (int) ($this->maxNavLinkCount / 2)); + $result = ''; - if (($endPage = $beginPage + $this->maxNavLinkCount - 1) >= $totalPages) { - $endPage = $totalPages; - $beginPage = max(1, $endPage - $this->maxNavLinkCount + 1); + if ($this->containerTag !== null) { + $result .= Html::openTag($this->containerTag, $this->containerAttributes) . "\n"; + } + if ($this->listTag !== null) { + $result .= Html::openTag($this->listTag, $this->listAttributes) . "\n"; } - if ($totalPages !== 0 && $currentPage > $totalPages) { - throw new InvalidArgumentException('Current page must be less than or equal to total pages.'); + $renderedItems = []; + foreach ($this->getItems() as $item) { + $html = ''; + + if ($this->itemTag !== null) { + $attributes = $this->itemAttributes; + if ($item->isDisabled) { + Html::addCssClass($attributes, $this->disabledItemClass); + } + if ($item->isCurrent) { + Html::addCssClass($attributes, $this->currentItemClass); + } + $html .= Html::openTag($this->itemTag, $attributes); + } + + $linkAttributes = $this->linkAttributes; + if ($item->isDisabled) { + Html::addCssClass($linkAttributes, $this->disabledLinkClass); + } + if ($item->isCurrent) { + Html::addCssClass($linkAttributes, $this->currentLinkClass); + } + $html .= Html::a($item->label, $item->url, $linkAttributes); + + if ($this->itemTag !== null) { + $html .= Html::closeTag($this->itemTag); + } + + $renderedItems[] = $html; + } + if (!empty($renderedItems)) { + $result .= implode("\n", $renderedItems); } - return [$beginPage, $endPage]; + if ($this->listTag !== null) { + $result .= "\n" . Html::closeTag($this->listTag); + } + if ($this->containerTag !== null) { + $result .= "\n" . Html::closeTag($this->containerTag); + } + + return $result; } - protected function getItems(): array + /** + * @psalm-return list + */ + private function getItems(): array { $paginator = $this->getPaginator(); $currentPage = $paginator->getCurrentPage(); @@ -105,7 +261,7 @@ protected function getItems(): array $items = []; if ($this->labelFirst !== null) { - $items[] = new PaginationItem( + $items[] = new OffsetPaginationItem( label: $this->labelFirst, url: $this->createUrl(PageToken::next('1')), isCurrent: false, @@ -114,7 +270,7 @@ protected function getItems(): array } if ($this->labelPrevious !== null) { - $items[] = new PaginationItem( + $items[] = new OffsetPaginationItem( label: $this->labelPrevious, url: $this->createUrl(PageToken::next((string) max($currentPage - 1, 1))), isCurrent: false, @@ -124,7 +280,7 @@ protected function getItems(): array $page = $beginPage; do { - $items[] = new PaginationItem( + $items[] = new OffsetPaginationItem( label: (string) $page, url: $this->createUrl(PageToken::next((string) $page)), isCurrent: $page === $currentPage, @@ -133,7 +289,7 @@ protected function getItems(): array } while (++$page <= $endPage); if ($this->labelNext !== null) { - $items[] = new PaginationItem( + $items[] = new OffsetPaginationItem( label: $this->labelNext, url: $this->createUrl(PageToken::next((string) min($currentPage + 1, $totalPages))), isCurrent: false, @@ -142,7 +298,7 @@ protected function getItems(): array } if ($this->labelLast !== null) { - $items[] = new PaginationItem( + $items[] = new OffsetPaginationItem( label: $this->labelLast, url: $this->createUrl(PageToken::next((string) $totalPages)), isCurrent: false, @@ -153,6 +309,25 @@ protected function getItems(): array return $items; } + /** + * @psalm-return array + */ + private function getPageRange(int $currentPage, int $totalPages): array + { + $beginPage = max(1, $currentPage - (int) ($this->maxNavLinkCount / 2)); + + if (($endPage = $beginPage + $this->maxNavLinkCount - 1) >= $totalPages) { + $endPage = $totalPages; + $beginPage = max(1, $endPage - $this->maxNavLinkCount + 1); + } + + if ($totalPages !== 0 && $currentPage > $totalPages) { + throw new InvalidArgumentException('Current page must be less than or equal to total pages.'); + } + + return [$beginPage, $endPage]; + } + /** * @param PageToken $pageToken Token for the page. * @return string Created URL. diff --git a/src/Pagination/OffsetPaginationItem.php b/src/Pagination/OffsetPaginationItem.php new file mode 100644 index 000000000..8ad3d3265 --- /dev/null +++ b/src/Pagination/OffsetPaginationItem.php @@ -0,0 +1,21 @@ + Date: Wed, 27 Nov 2024 15:13:39 +0000 Subject: [PATCH 06/11] Apply fixes from StyleCI --- src/Pagination/OffsetPagination.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pagination/OffsetPagination.php b/src/Pagination/OffsetPagination.php index 509d60e6e..d94323751 100644 --- a/src/Pagination/OffsetPagination.php +++ b/src/Pagination/OffsetPagination.php @@ -193,7 +193,7 @@ public function maxNavLinkCount(int $value): self return $new; } - final public function render(): string + public function render(): string { $result = ''; From a9728505c21b8146b4b9a93c2e7ae5e7cc68f454 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 28 Nov 2024 17:41:05 +0300 Subject: [PATCH 07/11] More refactor --- config/widgets-themes.php | 1 - src/Pagination/BasePagination.php | 169 ---------------------------- src/Pagination/KeysetPagination.php | 167 ++++++++++++++++++++++++--- src/Pagination/OffsetPagination.php | 99 ++++++++-------- src/Pagination/PaginationItem.php | 21 ---- tests/ListView/BaseTest.php | 1 - 6 files changed, 199 insertions(+), 259 deletions(-) delete mode 100644 src/Pagination/BasePagination.php delete mode 100644 src/Pagination/PaginationItem.php diff --git a/config/widgets-themes.php b/config/widgets-themes.php index 6698b64ef..5453ab30e 100644 --- a/config/widgets-themes.php +++ b/config/widgets-themes.php @@ -66,7 +66,6 @@ 'itemTag()' => ['li'], 'itemAttributes()' => [['class' => 'page-item']], 'linkAttributes()' => [['class' => 'page-link']], - 'currentItemClass()' => ['active'], 'disabledItemClass()' => ['disabled'], ], InputPageSize::class => [ diff --git a/src/Pagination/BasePagination.php b/src/Pagination/BasePagination.php deleted file mode 100644 index 683995898..000000000 --- a/src/Pagination/BasePagination.php +++ /dev/null @@ -1,169 +0,0 @@ -listTag = $tag; - return $new; - } - - final public function listAttributes(array $attributes): static - { - $new = clone $this; - $new->listAttributes = $attributes; - return $new; - } - - final public function itemTag(?string $tag): static - { - if ($tag === '') { - throw new InvalidArgumentException('Tag name cannot be empty.'); - } - - $new = clone $this; - $new->itemTag = $tag; - return $new; - } - - final public function itemAttributes(array $attributes): static - { - $new = clone $this; - $new->itemAttributes = $attributes; - return $new; - } - - final public function linkAttributes(array $attributes): static - { - $new = clone $this; - $new->linkAttributes = $attributes; - return $new; - } - - final public function currentItemClass(?string $class): static - { - $new = clone $this; - $new->currentItemClass = $class; - return $new; - } - - final public function currentLinkClass(?string $class): static - { - $new = clone $this; - $new->currentLinkClass = $class; - return $new; - } - - final public function disabledItemClass(?string $class): static - { - $new = clone $this; - $new->disabledItemClass = $class; - return $new; - } - - final public function disabledLinkClass(?string $class): static - { - $new = clone $this; - $new->disabledLinkClass = $class; - return $new; - } - - final public function render(): string - { - $result = ''; - - if ($this->containerTag !== null) { - $result .= Html::openTag($this->containerTag, $this->containerAttributes) . "\n"; - } - if ($this->listTag !== null) { - $result .= Html::openTag($this->listTag, $this->listAttributes) . "\n"; - } - - $renderedItems = []; - foreach ($this->getItems() as $item) { - $html = ''; - - if ($this->itemTag !== null) { - $attributes = $this->itemAttributes; - if ($item->isDisabled) { - Html::addCssClass($attributes, $this->disabledItemClass); - } - if ($item->isCurrent) { - Html::addCssClass($attributes, $this->currentItemClass); - } - $html .= Html::openTag($this->itemTag, $attributes); - } - - $linkAttributes = $this->linkAttributes; - if ($item->isDisabled) { - Html::addCssClass($linkAttributes, $this->disabledLinkClass); - } - if ($item->isCurrent) { - Html::addCssClass($linkAttributes, $this->currentLinkClass); - } - $html .= Html::a($item->label, $item->url, $linkAttributes); - - if ($this->itemTag !== null) { - $html .= Html::closeTag($this->itemTag); - } - - $renderedItems[] = $html; - } - if (!empty($renderedItems)) { - $result .= implode("\n", $renderedItems); - } - - if ($this->listTag !== null) { - $result .= "\n" . Html::closeTag($this->listTag); - } - if ($this->containerTag !== null) { - $result .= "\n" . Html::closeTag($this->containerTag); - } - - return $result; - } - - /** - * @return PaginationItem[] - */ - abstract protected function getItems(): array; -} diff --git a/src/Pagination/KeysetPagination.php b/src/Pagination/KeysetPagination.php index 1016ac7a2..5b15c79b0 100644 --- a/src/Pagination/KeysetPagination.php +++ b/src/Pagination/KeysetPagination.php @@ -4,14 +4,41 @@ namespace Yiisoft\Yii\DataView\Pagination; +use InvalidArgumentException; use Stringable; use Yiisoft\Data\Paginator\KeysetPaginator; use Yiisoft\Data\Paginator\PaginatorInterface; +use Yiisoft\Html\Html; +use Yiisoft\Widget\Widget; -final class KeysetPagination extends BasePagination +final class KeysetPagination extends Widget implements PaginationControlInterface { + use PaginationContextTrait; + private KeysetPaginator|null $paginator = null; + /** + * @psalm-var non-empty-string|null + */ + private ?string $containerTag = 'nav'; + private array $containerAttributes = []; + + /** + * @psalm-var non-empty-string|null + */ + private ?string $listTag = null; + private array $listAttributes = []; + + /** + * @psalm-var non-empty-string|null + */ + private ?string $itemTag = null; + private array $itemAttributes = []; + private ?string $disabledItemClass = null; + + private array $linkAttributes = []; + private ?string $disabledLinkClass = null; + private string|Stringable $labelPrevious = '⟨'; private string|Stringable $labelNext = '⟩'; @@ -26,27 +53,135 @@ public function withPaginator(PaginatorInterface $paginator): static return $new; } - protected function getItems(): array + public function containerTag(?string $tag): self + { + if ($tag === '') { + throw new InvalidArgumentException('Tag name cannot be empty.'); + } + + $new = clone $this; + $new->containerTag = $tag; + return $new; + } + + public function containerAttributes(array $attributes): self + { + $new = clone $this; + $new->containerAttributes = $attributes; + return $new; + } + + public function listTag(?string $tag): self + { + if ($tag === '') { + throw new InvalidArgumentException('Tag name cannot be empty.'); + } + + $new = clone $this; + $new->listTag = $tag; + return $new; + } + + public function listAttributes(array $attributes): self + { + $new = clone $this; + $new->listAttributes = $attributes; + return $new; + } + + public function itemTag(?string $tag): self + { + if ($tag === '') { + throw new InvalidArgumentException('Tag name cannot be empty.'); + } + + $new = clone $this; + $new->itemTag = $tag; + return $new; + } + + public function itemAttributes(array $attributes): self { + $new = clone $this; + $new->itemAttributes = $attributes; + return $new; + } + + public function disabledItemClass(?string $class): self + { + $new = clone $this; + $new->disabledItemClass = $class; + return $new; + } + + public function linkAttributes(array $attributes): self + { + $new = clone $this; + $new->linkAttributes = $attributes; + return $new; + } + + public function disabledLinkClass(?string $class): self + { + $new = clone $this; + $new->disabledLinkClass = $class; + return $new; + } + + public function render(): string + { + $result = ''; + + if ($this->containerTag !== null) { + $result .= Html::openTag($this->containerTag, $this->containerAttributes) . "\n"; + } + if ($this->listTag !== null) { + $result .= Html::openTag($this->listTag, $this->listAttributes) . "\n"; + } + $context = $this->getContext(); $paginator = $this->getPaginator(); $previousToken = $paginator->getPreviousToken(); $nextToken = $paginator->getNextToken(); + $result .= $this->renderItem( + $this->labelPrevious, + $previousToken === null ? null : $context->createUrl($previousToken), + $previousToken === null, + ) + . "\n" + . $this->renderItem( + $this->labelNext, + $nextToken === null ? null : $context->createUrl($nextToken), + $nextToken === null, + ); + + if ($this->listTag !== null) { + $result .= "\n" . Html::closeTag($this->listTag); + } + if ($this->containerTag !== null) { + $result .= "\n" . Html::closeTag($this->containerTag); + } - return [ - new PaginationItem( - label: $this->labelPrevious, - url: $previousToken === null ? null : $context->createUrl($previousToken), - isCurrent: false, - isDisabled: $previousToken === null, - ), - new PaginationItem( - label: $this->labelNext, - url: $nextToken === null ? null : $context->createUrl($nextToken), - isCurrent: false, - isDisabled: $nextToken === null, - ), - ]; + return $result; + } + + private function renderItem(string|Stringable $label, ?string $url, bool $isDisabled): Stringable + { + $linkAttributes = $this->linkAttributes; + if ($isDisabled) { + Html::addCssClass($linkAttributes, $this->disabledLinkClass); + } + $link = Html::a($label, $url, $linkAttributes); + + if ($this->itemTag === null) { + return $link; + } + + $attributes = $this->itemAttributes; + if ($isDisabled) { + Html::addCssClass($attributes, $this->disabledItemClass); + } + return Html::tag($this->itemTag, $link, $attributes); } private function getPaginator(): KeysetPaginator diff --git a/src/Pagination/OffsetPagination.php b/src/Pagination/OffsetPagination.php index 509d60e6e..fea9f383b 100644 --- a/src/Pagination/OffsetPagination.php +++ b/src/Pagination/OffsetPagination.php @@ -38,12 +38,11 @@ final class OffsetPagination extends Widget implements PaginationControlInterfac */ private ?string $itemTag = null; private array $itemAttributes = []; + private ?string $currentItemClass = null; + private ?string $disabledItemClass = null; private array $linkAttributes = []; - - private ?string $currentItemClass = null; private ?string $currentLinkClass = null; - private ?string $disabledItemClass = null; private ?string $disabledLinkClass = null; private string|Stringable|null $labelPrevious = '⟨'; @@ -118,31 +117,31 @@ public function itemAttributes(array $attributes): self return $new; } - public function linkAttributes(array $attributes): self + public function currentItemClass(?string $class): self { $new = clone $this; - $new->linkAttributes = $attributes; + $new->currentItemClass = $class; return $new; } - public function currentItemClass(?string $class): self + public function disabledItemClass(?string $class): self { $new = clone $this; - $new->currentItemClass = $class; + $new->disabledItemClass = $class; return $new; } - public function currentLinkClass(?string $class): self + public function linkAttributes(array $attributes): self { $new = clone $this; - $new->currentLinkClass = $class; + $new->linkAttributes = $attributes; return $new; } - public function disabledItemClass(?string $class): self + public function currentLinkClass(?string $class): self { $new = clone $this; - $new->disabledItemClass = $class; + $new->currentLinkClass = $class; return $new; } @@ -193,7 +192,7 @@ public function maxNavLinkCount(int $value): self return $new; } - final public function render(): string + public function render(): string { $result = ''; @@ -204,40 +203,13 @@ final public function render(): string $result .= Html::openTag($this->listTag, $this->listAttributes) . "\n"; } - $renderedItems = []; - foreach ($this->getItems() as $item) { - $html = ''; - - if ($this->itemTag !== null) { - $attributes = $this->itemAttributes; - if ($item->isDisabled) { - Html::addCssClass($attributes, $this->disabledItemClass); - } - if ($item->isCurrent) { - Html::addCssClass($attributes, $this->currentItemClass); - } - $html .= Html::openTag($this->itemTag, $attributes); - } - - $linkAttributes = $this->linkAttributes; - if ($item->isDisabled) { - Html::addCssClass($linkAttributes, $this->disabledLinkClass); - } - if ($item->isCurrent) { - Html::addCssClass($linkAttributes, $this->currentLinkClass); - } - $html .= Html::a($item->label, $item->url, $linkAttributes); - - if ($this->itemTag !== null) { - $html .= Html::closeTag($this->itemTag); - } - - $renderedItems[] = $html; - } - if (!empty($renderedItems)) { - $result .= implode("\n", $renderedItems); + $items = $this->renderItems(); + if ($items === []) { + return ''; } + $result .= implode("\n", $items); + if ($this->listTag !== null) { $result .= "\n" . Html::closeTag($this->listTag); } @@ -249,9 +221,9 @@ final public function render(): string } /** - * @psalm-return list + * @return list */ - private function getItems(): array + private function renderItems(): array { $paginator = $this->getPaginator(); $currentPage = $paginator->getCurrentPage(); @@ -261,7 +233,7 @@ private function getItems(): array $items = []; if ($this->labelFirst !== null) { - $items[] = new OffsetPaginationItem( + $items[] = $this->renderItem( label: $this->labelFirst, url: $this->createUrl(PageToken::next('1')), isCurrent: false, @@ -270,7 +242,7 @@ private function getItems(): array } if ($this->labelPrevious !== null) { - $items[] = new OffsetPaginationItem( + $items[] = $this->renderItem( label: $this->labelPrevious, url: $this->createUrl(PageToken::next((string) max($currentPage - 1, 1))), isCurrent: false, @@ -280,7 +252,7 @@ private function getItems(): array $page = $beginPage; do { - $items[] = new OffsetPaginationItem( + $items[] = $this->renderItem( label: (string) $page, url: $this->createUrl(PageToken::next((string) $page)), isCurrent: $page === $currentPage, @@ -289,7 +261,7 @@ private function getItems(): array } while (++$page <= $endPage); if ($this->labelNext !== null) { - $items[] = new OffsetPaginationItem( + $items[] = $this->renderItem( label: $this->labelNext, url: $this->createUrl(PageToken::next((string) min($currentPage + 1, $totalPages))), isCurrent: false, @@ -298,7 +270,7 @@ private function getItems(): array } if ($this->labelLast !== null) { - $items[] = new OffsetPaginationItem( + $items[] = $this->renderItem( label: $this->labelLast, url: $this->createUrl(PageToken::next((string) $totalPages)), isCurrent: false, @@ -309,6 +281,31 @@ private function getItems(): array return $items; } + private function renderItem(string|Stringable $label, string $url, bool $isCurrent, bool $isDisabled): Stringable + { + $linkAttributes = $this->linkAttributes; + if ($isDisabled) { + Html::addCssClass($linkAttributes, $this->disabledLinkClass); + } + if ($isCurrent) { + Html::addCssClass($linkAttributes, $this->currentLinkClass); + } + $link = Html::a($label, $url, $linkAttributes); + + if ($this->itemTag === null) { + return $link; + } + + $attributes = $this->itemAttributes; + if ($isDisabled) { + Html::addCssClass($attributes, $this->disabledItemClass); + } + if ($isCurrent) { + Html::addCssClass($attributes, $this->currentItemClass); + } + return Html::tag($this->itemTag, $link, $attributes); + } + /** * @psalm-return array */ diff --git a/src/Pagination/PaginationItem.php b/src/Pagination/PaginationItem.php deleted file mode 100644 index 342aee51f..000000000 --- a/src/Pagination/PaginationItem.php +++ /dev/null @@ -1,21 +0,0 @@ - ['li'], 'itemAttributes()' => [['class' => 'page-item']], 'linkAttributes()' => [['class' => 'page-link']], - 'currentItemClass()' => ['active'], 'disabledItemClass()' => ['disabled'], ]) ->render(), From 6a67f7f207bc1161fe4b16dc6323b3710c7efe2f Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 28 Nov 2024 14:42:04 +0000 Subject: [PATCH 08/11] Apply fixes from StyleCI --- src/Pagination/KeysetPagination.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Pagination/KeysetPagination.php b/src/Pagination/KeysetPagination.php index 5b15c79b0..881533521 100644 --- a/src/Pagination/KeysetPagination.php +++ b/src/Pagination/KeysetPagination.php @@ -144,10 +144,10 @@ public function render(): string $previousToken = $paginator->getPreviousToken(); $nextToken = $paginator->getNextToken(); $result .= $this->renderItem( - $this->labelPrevious, - $previousToken === null ? null : $context->createUrl($previousToken), - $previousToken === null, - ) + $this->labelPrevious, + $previousToken === null ? null : $context->createUrl($previousToken), + $previousToken === null, + ) . "\n" . $this->renderItem( $this->labelNext, From 3684e25d5eb1351bf198bd19364908fed0c95bd2 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 28 Nov 2024 17:43:22 +0300 Subject: [PATCH 09/11] fix --- src/BaseListView.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/BaseListView.php b/src/BaseListView.php index 1ebc44c99..e5c9f1e13 100644 --- a/src/BaseListView.php +++ b/src/BaseListView.php @@ -41,7 +41,6 @@ use Yiisoft\Yii\DataView\Pagination\OffsetPagination; use Yiisoft\Yii\DataView\Pagination\PaginationContext; use Yiisoft\Yii\DataView\Pagination\PaginationControlInterface; - use Yiisoft\Yii\DataView\Pagination\PaginatorNotSupportedException; use function array_key_exists; From 9ec1ded511abdf2e609d2ffa6968d6c219ad53fc Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 28 Nov 2024 17:45:24 +0300 Subject: [PATCH 10/11] Remove `OffsetPaginationItem` --- src/Pagination/OffsetPaginationItem.php | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/Pagination/OffsetPaginationItem.php diff --git a/src/Pagination/OffsetPaginationItem.php b/src/Pagination/OffsetPaginationItem.php deleted file mode 100644 index 8ad3d3265..000000000 --- a/src/Pagination/OffsetPaginationItem.php +++ /dev/null @@ -1,21 +0,0 @@ - Date: Sun, 8 Dec 2024 17:55:21 +0300 Subject: [PATCH 11/11] Rename "control" to "widget" --- src/BaseListView.php | 32 +++++++++---------- src/PageSize/InputPageSize.php | 2 +- src/PageSize/PageSizeContextTrait.php | 2 +- ...erface.php => PageSizeWidgetInterface.php} | 2 +- src/PageSize/SelectPageSize.php | 2 +- src/Pagination/KeysetPagination.php | 2 +- src/Pagination/OffsetPagination.php | 2 +- src/Pagination/PaginationContextTrait.php | 2 +- ...face.php => PaginationWidgetInterface.php} | 2 +- tests/GridView/ImmutableTest.php | 2 +- tests/Pagination/KeysetPaginationTest.php | 12 +++---- tests/Pagination/OffsetPaginationTest.php | 2 +- 12 files changed, 32 insertions(+), 32 deletions(-) rename src/PageSize/{PageSizeControlInterface.php => PageSizeWidgetInterface.php} (78%) rename src/Pagination/{PaginationControlInterface.php => PaginationWidgetInterface.php} (88%) diff --git a/src/BaseListView.php b/src/BaseListView.php index e5c9f1e13..a0a67fd48 100644 --- a/src/BaseListView.php +++ b/src/BaseListView.php @@ -35,12 +35,12 @@ use Yiisoft\Yii\DataView\Exception\DataReaderNotSetException; use Yiisoft\Yii\DataView\PageSize\InputPageSize; use Yiisoft\Yii\DataView\PageSize\PageSizeContext; -use Yiisoft\Yii\DataView\PageSize\PageSizeControlInterface; +use Yiisoft\Yii\DataView\PageSize\PageSizeWidgetInterface; use Yiisoft\Yii\DataView\PageSize\SelectPageSize; use Yiisoft\Yii\DataView\Pagination\KeysetPagination; use Yiisoft\Yii\DataView\Pagination\OffsetPagination; use Yiisoft\Yii\DataView\Pagination\PaginationContext; -use Yiisoft\Yii\DataView\Pagination\PaginationControlInterface; +use Yiisoft\Yii\DataView\Pagination\PaginationWidgetInterface; use Yiisoft\Yii\DataView\Pagination\PaginatorNotSupportedException; use function array_key_exists; @@ -91,8 +91,8 @@ abstract class BaseListView extends Widget */ private ?string $pageSizeTag = 'div'; private array $pageSizeAttributes = []; - private ?string $pageSizeTemplate = 'Results per page {control}'; - private PageSizeControlInterface|null $pageSizeControl = null; + private ?string $pageSizeTemplate = 'Results per page {widget}'; + private PageSizeWidgetInterface|null $pageSizeWidget = null; /** * A name for {@see CategorySource} used with translator ({@see TranslatorInterface}) by default. @@ -127,7 +127,7 @@ abstract class BaseListView extends Widget private array $offsetPaginationConfig = []; private array $keysetPaginationConfig = []; - private PaginationControlInterface|null $paginationControl = null; + private PaginationWidgetInterface|null $paginationWidget = null; protected ?ReadableDataInterface $dataReader = null; private string $toolbar = ''; @@ -538,7 +538,7 @@ final public function pageSizeAttributes(array $values): static * * The following tokens will be replaced with the corresponding values: * - * - `{control}` — page size control. + * - `{widget}` — page size widget. */ final public function pageSizeTemplate(?string $template): static { @@ -547,17 +547,17 @@ final public function pageSizeTemplate(?string $template): static return $new; } - final public function pageSizeControl(?PageSizeControlInterface $widget): static + final public function pageSizeWidget(?PageSizeWidgetInterface $widget): static { $new = clone $this; - $new->pageSizeControl = $widget; + $new->pageSizeWidget = $widget; return $new; } - public function paginationControl(PaginationControlInterface|null $widget): static + public function paginationWidget(PaginationWidgetInterface|null $widget): static { $new = clone $this; - $new->paginationControl = $widget; + $new->paginationWidget = $widget; return $new; } @@ -787,7 +787,7 @@ private function renderPagination(): string return ''; } - if ($this->paginationControl === null) { + if ($this->paginationWidget === null) { if ($dataReader instanceof OffsetPaginator) { $widget = OffsetPagination::widget(config: $this->offsetPaginationConfig); } elseif ($dataReader instanceof KeysetPaginator) { @@ -801,7 +801,7 @@ private function renderPagination(): string return ''; } } else { - $widget = $this->paginationControl->withPaginator($dataReader); + $widget = $this->paginationWidget->withPaginator($dataReader); } if ($this->urlCreator === null) { @@ -856,7 +856,7 @@ private function renderPageSize(): string return ''; } - if ($this->pageSizeControl === null) { + if ($this->pageSizeWidget === null) { if ($this->pageSizeConstraint === false || is_int($this->pageSizeConstraint)) { $widget = InputPageSize::widget(); } elseif (is_array($this->pageSizeConstraint)) { @@ -865,7 +865,7 @@ private function renderPageSize(): string return ''; } } else { - $widget = $this->pageSizeControl; + $widget = $this->pageSizeWidget; } if ($this->urlCreator === null) { @@ -895,11 +895,11 @@ private function renderPageSize(): string $urlPattern, $defaultUrl, ); - $control = $widget->withContext($context)->render(); + $renderedWidget = $widget->withContext($context)->render(); $content = $this->translator->translate( $this->pageSizeTemplate, - ['control' => $control], + ['widget' => $renderedWidget], $this->translationCategory, ); diff --git a/src/PageSize/InputPageSize.php b/src/PageSize/InputPageSize.php index 4a778b732..07e56e61b 100644 --- a/src/PageSize/InputPageSize.php +++ b/src/PageSize/InputPageSize.php @@ -7,7 +7,7 @@ use Yiisoft\Html\Html; use Yiisoft\Widget\Widget; -final class InputPageSize extends Widget implements PageSizeControlInterface +final class InputPageSize extends Widget implements PageSizeWidgetInterface { use PageSizeContextTrait; diff --git a/src/PageSize/PageSizeContextTrait.php b/src/PageSize/PageSizeContextTrait.php index 684ff74c4..3d4f7aee8 100644 --- a/src/PageSize/PageSizeContextTrait.php +++ b/src/PageSize/PageSizeContextTrait.php @@ -7,7 +7,7 @@ use LogicException; /** - * @psalm-require-implements PageSizeControlInterface + * @psalm-require-implements PageSizeWidgetInterface */ trait PageSizeContextTrait { diff --git a/src/PageSize/PageSizeControlInterface.php b/src/PageSize/PageSizeWidgetInterface.php similarity index 78% rename from src/PageSize/PageSizeControlInterface.php rename to src/PageSize/PageSizeWidgetInterface.php index bd5a2e547..30e578948 100644 --- a/src/PageSize/PageSizeControlInterface.php +++ b/src/PageSize/PageSizeWidgetInterface.php @@ -6,7 +6,7 @@ use Stringable; -interface PageSizeControlInterface extends Stringable +interface PageSizeWidgetInterface extends Stringable { public function withContext(PageSizeContext $context): static; diff --git a/src/PageSize/SelectPageSize.php b/src/PageSize/SelectPageSize.php index 7adcfce2f..a76530eef 100644 --- a/src/PageSize/SelectPageSize.php +++ b/src/PageSize/SelectPageSize.php @@ -10,7 +10,7 @@ use function count; use function is_array; -final class SelectPageSize extends Widget implements PageSizeControlInterface +final class SelectPageSize extends Widget implements PageSizeWidgetInterface { use PageSizeContextTrait; diff --git a/src/Pagination/KeysetPagination.php b/src/Pagination/KeysetPagination.php index 881533521..9930afe2a 100644 --- a/src/Pagination/KeysetPagination.php +++ b/src/Pagination/KeysetPagination.php @@ -11,7 +11,7 @@ use Yiisoft\Html\Html; use Yiisoft\Widget\Widget; -final class KeysetPagination extends Widget implements PaginationControlInterface +final class KeysetPagination extends Widget implements PaginationWidgetInterface { use PaginationContextTrait; diff --git a/src/Pagination/OffsetPagination.php b/src/Pagination/OffsetPagination.php index fea9f383b..c794b7af5 100644 --- a/src/Pagination/OffsetPagination.php +++ b/src/Pagination/OffsetPagination.php @@ -15,7 +15,7 @@ use function max; use function min; -final class OffsetPagination extends Widget implements PaginationControlInterface +final class OffsetPagination extends Widget implements PaginationWidgetInterface { use PaginationContextTrait; diff --git a/src/Pagination/PaginationContextTrait.php b/src/Pagination/PaginationContextTrait.php index c46571062..987d99282 100644 --- a/src/Pagination/PaginationContextTrait.php +++ b/src/Pagination/PaginationContextTrait.php @@ -7,7 +7,7 @@ use LogicException; /** - * @psalm-require-implements PaginationControlInterface + * @psalm-require-implements PaginationWidgetInterface */ trait PaginationContextTrait { diff --git a/src/Pagination/PaginationControlInterface.php b/src/Pagination/PaginationWidgetInterface.php similarity index 88% rename from src/Pagination/PaginationControlInterface.php rename to src/Pagination/PaginationWidgetInterface.php index 1f5f9b8d2..9b2fff0df 100644 --- a/src/Pagination/PaginationControlInterface.php +++ b/src/Pagination/PaginationWidgetInterface.php @@ -7,7 +7,7 @@ use Stringable; use Yiisoft\Data\Paginator\PaginatorInterface; -interface PaginationControlInterface extends Stringable +interface PaginationWidgetInterface extends Stringable { /** * @throws PaginatorNotSupportedException If the paginator is not supported. diff --git a/tests/GridView/ImmutableTest.php b/tests/GridView/ImmutableTest.php index bc3726129..fa73c30fd 100644 --- a/tests/GridView/ImmutableTest.php +++ b/tests/GridView/ImmutableTest.php @@ -34,7 +34,7 @@ public function testBaseListView(): void $this->assertNotSame($baseListView, $baseListView->headerAttributes([])); $this->assertNotSame($baseListView, $baseListView->id('')); $this->assertNotSame($baseListView, $baseListView->layout('')); - $this->assertNotSame($baseListView, $baseListView->paginationControl(OffsetPagination::widget())); + $this->assertNotSame($baseListView, $baseListView->paginationWidget(OffsetPagination::widget())); $this->assertNotSame($baseListView, $baseListView->dataReader($this->createOffsetPaginator($this->data, 10))); $this->assertNotSame($baseListView, $baseListView->summaryTemplate('')); $this->assertNotSame($baseListView, $baseListView->summaryAttributes([])); diff --git a/tests/Pagination/KeysetPaginationTest.php b/tests/Pagination/KeysetPaginationTest.php index 088dcf66e..86a15043d 100644 --- a/tests/Pagination/KeysetPaginationTest.php +++ b/tests/Pagination/KeysetPaginationTest.php @@ -64,7 +64,7 @@ public function testRenderPaginatorEmptyData(): void GridView::widget() ->id('w1-grid') ->dataReader($keysetPaginator) - ->paginationControl(KeysetPagination::widget()) + ->paginationWidget(KeysetPagination::widget()) ->render(), ); } @@ -126,7 +126,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->paginationControl(KeysetPagination::widget()->withPaginator($keysetPaginator)) + ->paginationWidget(KeysetPagination::widget()->withPaginator($keysetPaginator)) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); @@ -186,7 +186,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->paginationControl(KeysetPagination::widget()) + ->paginationWidget(KeysetPagination::widget()) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); @@ -231,7 +231,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->paginationControl(KeysetPagination::widget()) + ->paginationWidget(KeysetPagination::widget()) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); @@ -291,7 +291,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->paginationControl(KeysetPagination::widget()) + ->paginationWidget(KeysetPagination::widget()) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); @@ -351,7 +351,7 @@ public function testRenderPaginationLinks(): void ) ->id('w1-grid') ->dataReader($keysetPaginator) - ->paginationControl(KeysetPagination::widget()) + ->paginationWidget(KeysetPagination::widget()) ->layout('{items}' . PHP_EOL . '{pager}') ->render(), ); diff --git a/tests/Pagination/OffsetPaginationTest.php b/tests/Pagination/OffsetPaginationTest.php index 03e37fe65..53d85dc0a 100644 --- a/tests/Pagination/OffsetPaginationTest.php +++ b/tests/Pagination/OffsetPaginationTest.php @@ -47,7 +47,7 @@ public function testRenderPaginatorEmptyData(): void GridView::widget() ->id('w1-grid') ->dataReader($offsetPaginator) - ->paginationControl(OffsetPagination::widget()) + ->paginationWidget(OffsetPagination::widget()) ->render(), ); }