Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/workflows/bc.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
on:
- pull_request
- push
pull_request:
paths:
- 'src/**'
- '.github/workflows/bc.yml'
- 'composer.json'
push:
branches: ['master']
paths:
- 'src/**'
- '.github/workflows/bc.yml'
- 'composer.json'

name: backwards compatibility

Expand Down
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Yii Data Change Log

## 1.0.2 under development
## 2.0.0 under development

- no changes in this release.
- New #150: Extract `withLimit()` from `ReadableDataInterface` into `LimitableDataInterface` (@vjik)
- Enh #150: `PaginatorInterface` now extends `ReadableDataInterface` (@vjik)

## 1.0.1 January 25, 2023

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ Additional interfaces could be implemented in order to support different paginat

- `CountableDataInterface` - allows getting total number of items in data reader.
- `FilterableDataInterface` - allows returning subset of items based on criteria.
- `LimitableDataInterface` - allows returning limited subset of items.
- `SortableDataInterface` - allows sorting by one or multiple fields.
- `OffsetableDataInterface` - allows to skip first N items when reading data.

Expand Down
17 changes: 16 additions & 1 deletion infection.json.dist
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@
}
},
"mutators": {
"@default": true
"@default": true,
"LogicalAndAllSubExprNegation": {
"ignore": [
"Yiisoft\\Data\\Paginator\\KeysetPaginator::isOnFirstPage"
]
},
"IncrementInteger": {
"ignore": [
"Yiisoft\\Data\\Paginator\\OffsetPaginator::readOne"
]
},
"DecrementInteger": {
"ignore": [
"Yiisoft\\Data\\Paginator\\OffsetPaginator::readOne"
]
}
}
}
2 changes: 2 additions & 0 deletions psalm-php80.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
findUnusedBaselineEntry="true"
findUnusedCode="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
Expand Down
2 changes: 2 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
findUnusedBaselineEntry="true"
findUnusedCode="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
Expand Down
23 changes: 20 additions & 3 deletions src/Paginator/KeysetPaginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Yiisoft\Data\Reader\Filter\LessThan;
use Yiisoft\Data\Reader\Filter\LessThanOrEqual;
use Yiisoft\Data\Reader\FilterableDataInterface;
use Yiisoft\Data\Reader\LimitableDataInterface;
use Yiisoft\Data\Reader\ReadableDataInterface;
use Yiisoft\Data\Reader\Sort;
use Yiisoft\Data\Reader\SortableDataInterface;
Expand Down Expand Up @@ -49,7 +50,7 @@ final class KeysetPaginator implements PaginatorInterface
/**
* Data reader being paginated.
*
* @psalm-var ReadableDataInterface<TKey, TValue>&FilterableDataInterface&SortableDataInterface
* @psalm-var ReadableDataInterface<TKey, TValue>&LimitableDataInterface&FilterableDataInterface&SortableDataInterface
*/
private ReadableDataInterface $dataReader;

Expand Down Expand Up @@ -82,7 +83,7 @@ final class KeysetPaginator implements PaginatorInterface

/**
* @param ReadableDataInterface $dataReader Data reader being paginated.
* @psalm-param ReadableDataInterface<TKey, TValue>&FilterableDataInterface&SortableDataInterface $dataReader
* @psalm-param ReadableDataInterface<TKey, TValue>&LimitableDataInterface&FilterableDataInterface&SortableDataInterface $dataReader
* @psalm-suppress DocblockTypeContradiction Needed to allow validating `$dataReader`
*/
public function __construct(ReadableDataInterface $dataReader)
Expand All @@ -101,6 +102,13 @@ public function __construct(ReadableDataInterface $dataReader)
));
}

if (!$dataReader instanceof LimitableDataInterface) {
throw new InvalidArgumentException(sprintf(
'Data reader should implement "%s" to be used with keyset paginator.',
LimitableDataInterface::class,
));
}

$sort = $dataReader->getSort();

if ($sort === null) {
Expand Down Expand Up @@ -185,6 +193,15 @@ public function read(): iterable
return $this->readCache = $data;
}

public function readOne(): array|object|null
{
foreach ($this->read() as $item) {
return $item;
}

return null;
}

public function getPageSize(): int
{
return $this->pageSize;
Expand Down Expand Up @@ -289,7 +306,7 @@ private function reverseData(array $data): array
}

/**
* @psalm-param ReadableDataInterface<TKey, TValue>&FilterableDataInterface&SortableDataInterface $dataReader
* @psalm-param ReadableDataInterface<TKey, TValue>&LimitableDataInterface&FilterableDataInterface&SortableDataInterface $dataReader
*/
private function previousPageExist(ReadableDataInterface $dataReader, Sort $sort): bool
{
Expand Down
20 changes: 18 additions & 2 deletions src/Paginator/OffsetPaginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Generator;
use InvalidArgumentException;
use Yiisoft\Data\Reader\CountableDataInterface;
use Yiisoft\Data\Reader\LimitableDataInterface;
use Yiisoft\Data\Reader\OffsetableDataInterface;
use Yiisoft\Data\Reader\ReadableDataInterface;
use Yiisoft\Data\Reader\Sort;
Expand Down Expand Up @@ -50,13 +51,13 @@ final class OffsetPaginator implements PaginatorInterface
/**
* Data reader being paginated.
*
* @psalm-var ReadableDataInterface<TKey, TValue>&OffsetableDataInterface&CountableDataInterface
* @psalm-var ReadableDataInterface<TKey, TValue>&LimitableDataInterface&OffsetableDataInterface&CountableDataInterface
*/
private ReadableDataInterface $dataReader;

/**
* @param ReadableDataInterface $dataReader Data reader being paginated.
* @psalm-param ReadableDataInterface<TKey, TValue>&OffsetableDataInterface&CountableDataInterface $dataReader
* @psalm-param ReadableDataInterface<TKey, TValue>&LimitableDataInterface&OffsetableDataInterface&CountableDataInterface $dataReader
* @psalm-suppress DocblockTypeContradiction Needed to allow validating `$dataReader`
*/
public function __construct(ReadableDataInterface $dataReader)
Expand All @@ -75,6 +76,13 @@ public function __construct(ReadableDataInterface $dataReader)
));
}

if (!$dataReader instanceof LimitableDataInterface) {
throw new InvalidArgumentException(sprintf(
'Data reader should implement "%s" in order to be used with offset paginator.',
LimitableDataInterface::class,
));
}

$this->dataReader = $dataReader;
}

Expand Down Expand Up @@ -213,6 +221,14 @@ public function read(): iterable
->read();
}

public function readOne(): array|object|null
{
return $this->dataReader
->withLimit(1)
->withOffset($this->getOffset())
->readOne();
}

public function isOnFirstPage(): bool
{
return $this->currentPage === 1;
Expand Down
5 changes: 4 additions & 1 deletion src/Paginator/PaginatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Yiisoft\Data\Paginator;

use Yiisoft\Data\Reader\ReadableDataInterface;
use Yiisoft\Data\Reader\Sort;

/**
Expand All @@ -14,8 +15,10 @@
*
* @template TKey as array-key
* @template TValue as array|object
*
* @extends ReadableDataInterface<TKey, TValue>
*/
interface PaginatorInterface
interface PaginatorInterface extends ReadableDataInterface
Copy link
Member

@roxblnfk roxblnfk Nov 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that PaginatorInterface::read() and ReaderInterface::read() are compatible in the meaning?

    /**
     * Get iterable for the data set.
     *
     * @return iterable Iterable for the data.
     * @psalm-return iterable<TKey, TValue>
     */
    public function read(): iterable;
    /**
     * Get iterator that could be used to read currently active page items.
     *
     * @throws PaginatorException If page specified is not found.
     *
     * @return iterable Iterator with items for the current page.
     * @psalm-return iterable<TKey, TValue>
     */
    public function read(): iterable;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, this was the main reason why the Paginator didn't implement the DataReaderInterface from the very beginning.
This could lead to confusion and unexpected behavior. One user might expect that passing a Paginator would read all the data, while another user might expect it to read only one page.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand reason. You talk about implementation details. For users of ReadableDataInterface it doesn't matter.

{
/**
* Page size that is used in case it is not set explicitly.
Expand Down
1 change: 1 addition & 0 deletions src/Reader/DataReaderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
interface DataReaderInterface extends
ReadableDataInterface,
LimitableDataInterface,
OffsetableDataInterface,
CountableDataInterface,
SortableDataInterface,
Expand Down
25 changes: 25 additions & 0 deletions src/Reader/LimitableDataInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Data\Reader;

use InvalidArgumentException;

/**
* Data that could be limited.
*/
interface LimitableDataInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add "extends ReadableInterface" there

{
/**
* Get a new instance with limit set.
*
* @param int $limit Limit. 0 means "no limit".
*
* @throws InvalidArgumentException If limit is less than 0.
*
* @return static New instance.
* @psalm-return $this
*/
public function withLimit(int $limit): static;
}
20 changes: 3 additions & 17 deletions src/Reader/ReadableDataInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,14 @@

namespace Yiisoft\Data\Reader;

use InvalidArgumentException;

/**
* Readable data is a data set that could be read up to number of items
* defined by limit either one by one or by getting an iterator.
* Readable data is a data set that could be read up by getting an iterator or reading one item from set.
*
* @template TKey as array-key
* @template TValue as array|object
*/
interface ReadableDataInterface
{
/**
* Get a new instance with limit set.
*
* @param int $limit Limit. 0 means "no limit".
*
* @throws InvalidArgumentException If limit is less than 0.
*
* @return static New instance.
* @psalm-return $this
*/
public function withLimit(int $limit): static;

/**
* Get iterable for the data set.
*
Expand All @@ -36,7 +21,8 @@ public function withLimit(int $limit): static;
public function read(): iterable;

/**
* Get a next item from the data set.
* Get one item from the data set. Which item is returned is up to implementation.
* Note that invoking this method does not impact the data set or its pointer.
*
* @return array|object|null An item or null if there is none.
* @psalm-return TValue|null
Expand Down
Loading