yiisoft / data Goto Github PK
View Code? Open in Web Editor NEWData providers
Home Page: https://www.yiiframework.com/
License: BSD 3-Clause "New" or "Revised" License
Data providers
Home Page: https://www.yiiframework.com/
License: BSD 3-Clause "New" or "Revised" License
They are currently used in most places (mainly in paginators)
Currently cannot implement own filter. For example, I want a regexp filter, or custom group filter with scoring system.
$filter = new CustomRegexpFilter('name' /* field */, '!^a[g]?!' /* pattern */, 'ui' /* modifier*/);
$reader = (new ArrayDataReader($this->getDataSet()))
->withFilter($filter);
$filter = new CustomScoringGroupFilter(new Filter(), new Filter()...);
$reader = (new ArrayDataReader($this->getDataSet()))
->withFilter($filter);
This function throw exception if i using custom filter. As I look at eg. ArrayDataReader
, I have no chance of implementing a custom filter (for example, inheritance, but all filtering functions are private).
private function matchFilter(array $item, array $filter): bool
Q | A |
---|---|
Version | 1.0.0 under development |
PHP version | - |
Operating system | - |
Currently, paginator classes have no common part, so they cannot be handled uniformly. In addition, implementing a custom paginator is difficult if it comes in this form eg. in the view layer.
I think the first common part should be done with the data needed for paging and the restore from data.
$paginatorData = $paginatior->toParameters();
...
$paginator = $pagainator->withParameters($paginatorData);
Generate data for paging links:
$pages = [];
if($paginator instanceof KeysetPaginator) {
$data = [
'lastValue' => $paginator->getLast(),
...
];
...
} elseif($paginator instanceof OffsetPaginator) {
...
} else {
// custom paginator???
}
...and restoring data to the paginator.
interface Paginator {
function toParamters(int $relativePage): ?array;
function withParameters(aray $data);
}
example
class KeysetPaginator implements Paginator {
public function toParameters(int $relativePage): ?array {
...
}
public function withParameters(array $data): self {
$new = clone $this;
if(isset($data['lastValue'])) {
$new = $new->withLast($data['lastValue']);
}
...
return $new;
}
}
$relativePage
in toParamters($relativePage)
I suggest that the data be retrieved with a relative number of pages so that the paging view can retrieve the data needed for a page from any distance. If the paginator does not support something, it returns NULL.
example:
$pageData = [];
$relativePages = array_merge([
PHP_INT_MIN /* first page */,
PHP_INT_MAX /* last page */,
],
range(-5, 5)
// N-5, N-4, N-3, N-2, N-1,
// 0 (current page),
// N+1, N+2, N+3, N+4, N+5,
);
foreace($relativePages as $relativePage) {
$data = $paginator->toParameters($relativePage);
if($data === null) {
/* not available or not supported */
continue;
}
$pageData[$relativePages] = $data;
}
// forming page data keys and creating eg URL
Required for:
TokenPaginator
#5Q | A |
---|---|
Version | 1.0.0 under development |
PHP version | - |
Operating system | - |
Creeate new class that implement DataProcessorInterface
and use callable parameter to process data.
in yii-demo
modify the following file: src/Blog/Comment/CommentRepository.php
from:
private function getSort(): Sort
{
return Sort::only(['id', 'public', 'created_at', 'post_id', 'user_id'])->withOrder(['id' => 'asc']);
}
to:
private function getSort(): Sort
{
return Sort::only(['id', 'public', 'created_at', 'post_id', 'user_id'])->withOrder(['created_at' => 'desc']);
}
comments are sorted descending order for created_at
fields
Yiisoft\ErrorHandler\Exception\ErrorException: Undefined property: App\Blog\Entity\Comment Cycle ORM Proxy::$created_at in /app/vendor/cycle/orm/src/Mapper/Proxy/EntityProxyTrait.php:28
Stack trace:
#0 /app/vendor/cycle/orm/src/Mapper/Proxy/EntityProxyTrait.php(28): Yiisoft\ErrorHandler\ErrorHandler::Yiisoft\ErrorHandler\{closure}(2, 'Undefined prope...', '/app/vendor/cyc...', 28)
#1 /app/vendor/yiisoft/arrays/src/ArrayHelper.php(255): App\Blog\Entity\Comment Cycle ORM Proxy->__get('created_at')
#2 /app/vendor/yiisoft/arrays/src/ArrayHelper.php(231): Yiisoft\Arrays\ArrayHelper::getRootValue(Object(App\Blog\Entity\Comment Cycle ORM Proxy), 'created_at', NULL)
#3 /app/vendor/yiisoft/data/src/Paginator/KeysetPaginator.php(316): Yiisoft\Arrays\ArrayHelper::getValue(Object(App\Blog\Entity\Comment Cycle ORM Proxy), 'created_at')
#4 /app/vendor/yiisoft/data/src/Paginator/KeysetPaginator.php(262): Yiisoft\Data\Paginator\KeysetPaginator->getValueFromItem(Object(App\Blog\Entity\Comment Cycle ORM Proxy), 'created_at')
#5 /app/vendor/yiisoft/data/src/Paginator/KeysetPaginator.php(174): Yiisoft\Data\Paginator\KeysetPaginator->readData(Object(Yiisoft\Yii\Cycle\Data\Reader\EntityReader), Object(Yiisoft\Data\Reader\Sort))
#6 /app/views/blog/comments/_comments.php(19): Yiisoft\Data\Paginator\KeysetPaginator->read()
Q | A |
---|---|
Version | master |
PHP version | 8.1 |
Operating system | Linux (yii-php docker image) |
We have Readable and Writeable interfaces. But we have no interface for deleting. It might be worth adding.
Q | A |
---|---|
Version | 1.0.? |
PHP version | 8.2 |
Operating system | Windows 11 |
"yiisoft/data": "dev-master as 1.0",
"yiisoft/yii-cycle":"dev-master",
"yiisoft/yii-dataview": "^3.0",
Hi Vjik.
I am experiencing the above error. Composer is updating with the above combination but then the program runs into this error. I understand there are BC issues at the moment with yiisoft\data Is this related to that? I fortunately have a stable vendor folder so still able to develop.
I suggest adding a method for reading a single element in the DataReaderInterface.
This method will work in much the same way as the read()
method with a limit of 1
, but will return not the collection of elements, but the first element.
interface DataReaderInterface
{
public const DEFAULT_LIMIT = 10;
public function withLimit(int $limit);
public function read(): iterable;
public function readOne();
}
This is need for remove confusing with two entities with same name: data processor and filter processor.
If the field does not exist then an undefined index is generated, I assume this is incorrect.
$item[$field] ?? null
=
, <
, <=
, >
, >=
, in
like
I don't know what to do correct properly.
$ php -r 'var_dump(stripos(null, ""));'
bool(false)
$ php -r 'var_dump(stripos(null, null));'
bool(false)
null LIKE null
is trueUndefined index: notExistsField
Q | A |
---|---|
Version | 1.0.0-under development |
PHP version | - |
Operating system | - |
Currently there is getters and setters support in KeysetPaginator. There's no such thing in other data readers.
We could make these explicit i.e. if you need a getter you state it specifically such as getName()
. Note the ()
. We won't check existence of the method via reflection and won't inflect the name. Just call it directly.
Advantages:
Disadvantages:
Alternative is to extract inflector and reflection based magic from KeysetPaginator and use it everywhere.
Advantages:
Disadvantages:
Q | A |
---|---|
Version | 1.0.? |
PHP version | |
Operating system |
These interfaces and functions are incorrectly placed under the "Reader" namespace because they can also be used for Writer
:
src/Reader/Filter
src/Reader/SortableDataInterface.php
src/Reader/FilterableDataInterface.php
src/Reader/Sort.php
DataReaderInterface#withLimit()
Example SQL update:
UPDATE `table` SET values... WHERE <filters> ORDER BY <sorters> LIMIT <limit>
Q | A |
---|---|
Version | 1.0.0 under development |
PHP version | - |
Operating system | - |
After #66 Sort is less usable for non-user input case. You have to specify all fields all the time and it's tedious.
I propose to have two static constructors:
Sort::fields()
for user input with specifying allowed fields.Sort::any()
for non-user input allowing any fields.OffsetPaginator::getCurrentPageSize()
work with DataReaderInterface::count()
but OffsetPaginator::read()
operate with DateReaderInterface::read()
.
If the data source changes between the two operations, count(OffsetPaginator::read()) !== OffsetPaginator::getCurrentPageSize()
may occur.
count(OffsetPaginator::read()) === OffsetPaginator::getCurrentPageSize()
is true
Depending on the circumstances count(OffsetPaginator::read()) === OffsetPaginator::getCurrentPageSize()
is false
Q | A |
---|---|
Version | 3.0.x-dev |
PHP version | - |
Operating system | - |
Currently, the filtered value is treated as $item[$field]
. I'm not really frontend developer, but I think yii2 knew it too:
https://github.com/yiisoft/yii2/blob/f8611d170d77f8b89f24bd80e7f3b6b2a8947380/framework/grid/DataColumn.php#L232
Yii3 also uses <field>.<subfield>
with ArrayHelper::getValue()
Currently not supported.
in #28 issue also affect the implementation.
Q | A |
---|---|
Version | 1.0.0-under development |
PHP version | - |
Operating system | - |
Implementations of PaginatorInterface
is very different. Some methods need in one implementation, and not need in other implementations. Some methods should named different in different implementations. Seems PaginatorInterface
not need.
Suggest:
PaginationInterface
;getFilter(): FilterInterface
that contains pagination logic in the form of a filter;getFilter()
method in reader.Example:
$pagination = (new KeysetPaginator($dataReader))->withPageSize(10);
$filter = new All($myFilter1, $myFilter2, $pagination->getFilter());
$reader = (new IterableDataReader($myDataSet))->withFilter($filter);
$result = $reader->read();
Remove travis if it exists
Currently, it organizes the current page, not the whole items.
data/src/Reader/IterableDataReader.php
Lines 138 to 158 in 370d917
sort after slice
slice after sort
Q | A |
---|---|
Version | 1.0.0 under development |
PHP version | - |
Operating system | - |
If DataReader contains object items then this error is inevitable
Cannot use object of type App\Blog\Entity\Comment as array
src/Paginator/KeysetPaginator.phpat line 271
The current filter set does not allow us to check for null
values; there is no operator between
, which is also often used when working with a collection of data.
I think such operators should be present in this basic package. Can you suggest some more filters?
Yii 2.x pagination base sentence:
Most cloud-based systems (storage, APIs, databases) do not support the total count of items, and only knows where the scanning has finished, so paging can only be done with a next page token.
Example cloud platform services:
Pagination support next page token. I do not think it is necessary to implement the previous page token, but it is worth discussing this topic as well.
In this case, the page to be displayed is only Load more
.
In the absence of a complete batch, ArrayDataProvider requires custom solutions.
Q | A |
---|---|
Version | 2.x |
PHP version | - |
Operating system | - |
Google applies the concept of maxResults
. An alternative to pageSize
, which does not specify a fixed number of elements in the response, but only the maximum number. MaxResults
can vary between 0 and maximum on any page.
Items in the response can be limited to any criteria:
Its application is very diverse:
ignore_user_abort()
function and if it detects connection_aborted
it stops any further running (similar to the console process, the kill signal)I could imagine this feature in KeysetPaginator
.
This roughly strains the boundaries of the layers and requires its correct design. You can think about whether the theme of a separate package, or fits into the framework, but one thing is certain: at worst, any external package should be able to handle a similar concept.
interface StoppableStrategyDataInterface {
public function withStoppableStrategy(StobbaleStrategy $strategy)
}
example strategies
Q | A |
---|---|
Version | 1.0.0 under development |
PHP version | - |
Operating system | - |
I have a question about Sort because when i create object Sort as below:
$sort = Sort::only([
'title' => [
'asc' => ['id' => 'asc', 'title' => 'desc'],
'desc' => ['first_name' => 'desc', 'title' => 'asc'],
'default' => 'desc'
],
'descscription' => [
'asc' => ['id_2' => 'asc', 'title_2' => 'desc'],
'desc' => ['first_name_2' => 'desc', 'title_2' => 'asc'],
'default' => 'desc'
],
])->withOrder(['descscription' => 'asc']);
Function getCriteria return:
Array ( [id_2] => asc [title_2] => desc [first_name] => desc [title] => asc )
Why criteria return rest sorts by default option ? I think when i need only description asc it should return only this sort.
In function getCriteria i found this:
foreach ($config as $field => $fieldConfig) {
$criteria += $fieldConfig[$fieldConfig['default']];
}
Should it work in that way ?
I've checked how it works in yii2 and sort doesn't return rest sorting options by default value.
In yii-cycle it's used getOrder instead of getCriteria.
Could someone explain me how can i create order query in my Doctrine Reader by using this class Sort.
Perhaps in this package is another aproach ?
The KeysetPaginator class needs to be typed and possibly refactored. It is also worth paying attention to tests.
Now we can:
withCriteriaArray()
for set filters or criteria array.Suggest allow pass to constructor mixed array of filters and criteria (as in withCriteriaArray()
) and remove withCriteriaArray()
.
It's more clear name.
Implementations of PaginatorInterface
is very different. Some methods need in one implementation, and not need in other implementations. Some methods should named different in different implementations. Seems PaginatorInterface not need.
For example, KeysetPaginator
contains methods withNextPageToken(string $token)
and withPreviousPageToken(string $token)
, but in OffsetPaginator
instead this methods need one method withCurrentPage(int $page)
.
Yii Data depended packages:
yiisoft/data-db
,yiisoft/yii-cycle
,yiisoft/app-api
,yiisoft/demo
,yiisoft/yii-dataview
.But PaginatorInterface
used in yiisoft/yii-dataview
only. In class BaseListView
user may set paginator via method paginator(PaginatorInterface $value)
, but into class code used implementations KeysetPaginator
and OffsetPaginator
, and pass custom implementation of PaginatorInterface
throws error or will be work incorrect, for example, here:
private function renderSummary(): string
{
if ($this->getPaginator() instanceof KeysetPaginator) {
return '';
}
$pageCount = count($this->getDataReader());
if ($pageCount <= 0) {
return '';
}
/** @var OffsetPaginator $paginator */
$paginator = $this->getPaginator();
// ...
FilterDataValidationHelper
contains common reader methods. assertFilterHandlerIsIterable()
used for iterable reader only and should be moved to Iterable
namespace.
See: #85 (comment)
Data:
Filter:
Output:
In the IterableDataReader class, the default Limit is set to 10 on creation.
I think this is wrong (not expected) behavior.
When i send an array of 20 elements to the IterableDataReader
, i expect the read()
method to read all the values by default.
The default limit should be set by a paginator.
If the user needs a limit directly in the reader, he will do it manually.
Occurred after the PR #13874.
We have application with restful API which uses ActiveDataProvider (extends BaseDataProvider) in Index action to support pagination.
Let's consider we have one API resource /users
with pagination (see http://www.yiiframework.com/doc-2.0/guide-rest-response-formatting.html#data-serializing) and we create two test methods.
/users
resource./users
resource from the second page (query parameter per-page=2
).Since both tests run within a single process calling the first one will initialize BaseDataProvider::$counter=0
and increase it by 1
. Calling of the second test will use $counter=1
and ActiveDataProvider
of the /users
resource will have a different ID now. So it won't accept per-page
parameter because it's waiting for the dp-1-per-page
.
The fact this "dynamic ID" can not be disabled is frustrating.
ID of DataProvider remains consistent name during tests running.
ID of DataProvider changes.
Q | A |
---|---|
Yii version | 2.0.12 |
PHP version | 7.1.6 |
Operating system | MacOS 10.12, CentOS 7.3 |
There may be times when data needs to be scanned as reversed. Currently there is no specific interface defined, but can using with reversed Sort
orders.
$reader->withReverseScanning()->read();
condition of data reader | result |
---|---|
if supported it, or partially supported | scanning in reverse mode |
if not supported it, but supported sorting | reverses sorting immediately before scanning |
If not supported it | throw exception, or implement it at PHP level after reading the data, if possible. |
examples:
SELECT * FROM (
SELECT *
FROM ...
WHERE id > :first
ORDER BY id DESC
) ORDER BY id ASC LIMIT 3 -- this is not real reverse scanning
IteratorDataReader
array_reverse()
function, but for example, if the number of data can be hundreds of millions, can decide to throw an exception based on the withLimit()
. (typically with noSQL)data/src/Paginator/KeysetPaginator.php
Lines 70 to 75 in 992a8bc
Also consider giving Sort
awithInversed()
or withReverseSorting()
method.
Q | A |
---|---|
Version | 1.0.0 under development |
PHP version | - |
Operating system | - |
Currently Sort label isn't used. Either should be used or be removed.
IteratorDataReader
uses ArraySorter::multisort
that uses array_multisort()
function. This function resets numeric keys.
This behavior is undesirable.
Syntax could be something like:
$filter = $aaa->where([
['and', 'id', '>', 3],
['and', ['or', ['temperature', '<', 10], ['temperature', '>', 30]] ],
['and', 'name', like', '%agent%'],
];
See https://yiiframework.ru/forum/viewtopic.php?f=39&t=52964
Q | A |
---|---|
Version | 1.0.? |
PHP version | |
Operating system |
The idea of keyset pagination is simple. Instead of "get me a page 5" you're telling framework get me 10 records since ID = X. On the 1st page X is 0, on the next page it's last ID from the previous page:
SELECT ...
FROM ...
WHERE ...
AND id < ?last_seen_id
ORDER BY id DESC
LIMIT 10
OFFSET 100000 LIMIT 10
has to fetch 100010 records.count(*)
is inefficient. At least when using InnoDB.In many cases there's no need to know exact number of a page you are on, total number of pages and total items count.
Therefore visually it can be what Reddit does:
There's a number of other ways to display data using the same technique:
Which way to use heavily depends on project type. Social networks are fine with infinite scroll, e-commerce is OK with "Load more.
Still, classic pagination is popular and heavily used so it is a bad idea to remove it from the framework.
Implementation spans over several components:
It also causes problems with usage and implementation.
data/src/Reader/DataReaderInterface.php
Line 16 in 1dae24f
For example, the PaginatorInterface
cache is the result itself or not, because the data reader is already doing it.
Repeated reading may require:
specfy exactly
not specified
Q | A |
---|---|
Version | 3.0.x-dev |
PHP version | - |
Operating system | - |
IterableDataReader#data
is iterable
type, but count() support only array
and \Countable
.
$reader = IterableDataReader(new class implements \Iterator { ... });
var_dump($reader->count()); // throw not supported exception
$reader = IterableDataReader(new class implements \Iterator { ... });
var_dump($reader->count());
// PHP Warning: count(): Parameter must be an array or an object that implements Countable in Command line code on line 1
// int(1)
Q | A |
---|---|
Version | 1.0.0 |
PHP version | - |
Operating system | - |
This issue has originally been reported by @az-joss at yiisoft/yii2#16625.
Moved here by @samdark.
I have configured IndexAction
in controller like fallow
public function actions()
{
$actions = ArrayHelper::merge(
parent::actions(),
[
'index' => [
'dataFilter' => [
'class' => ActiveDataFilter::class,
'searchModel' => function () {
return (new \yii\base\DynamicModel([
'tags' => null,
'counties' => null,
]))
->addRule('tags', 'each', ['rule' => ['integer']])
->addRule('counties', 'each', ['rule' => ['integer']]);
},
]
],
]
);
return $actions;
}
As you can see, expect to use this attributes like an array of integers
When I use operator in
in filter param as fallow:
http://somehost.com/endpoint?filter[tags][in][]=5&filter[tags][in][]=6
Validation of DynamicModel
fails because-of passing value not as an array and as each element of an array.
If change DynamicModel
rules definision as fallow:
...
->addRule('tags', 'integer')
->addRule('counties', 'integer');
...
It works fine for IN
condition but not for the simple conditions
I've traced it and found this, where loop over an array goes and validator recives element and not all list as expected..
Q | A |
---|---|
Yii version | 2.0.14 |
PHP version | 7.0.30 |
Operating system | MacOS 10.13.6 |
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.