I am re-thinking data providers and grids for Yii 3. In Yii 2 these were good but we can do better.
Yii 2 bad sides
- These were doing too much: working with request, getting data, validating data etc.
- These were tied to models and active record.
Code so far
Idea
The main idea is to represent data in a simple table form i.e. all items have same structure, each item has same named fields.
In order to support maximum number of data sources, data reader should not read data before read()
is called i.e. we form reading criteria first and then applying it for reading. That allows to efficiently query SQL databases and alike sources with existing query language while not preventing to implement our own storages.
Interfaces
There are three main interface groups:
-
DataProcessorInterface
that allows unifying processing like batch processing. -
DataWriterInterface
that allows writing data. -
DataReaderInteface
and interfaces for its additional features that allow reading data:
-
CountableDataInterface
- allows getting total number of items in data provider. -
FilterableDataInterface
- allows returning subset of items based on criteria. -
SortableDataInterface
- allows sorting by one or multiple fields. -
OffsetableDataInterface
- allows to skip first N items when reading data.
Sorting data
Sorting is similar to the one from Yii 2 except it doesn’t try to work with request:
$sort = (new Sort([ // <--- init with config
'b' => [
'asc' => ['bee' => SORT_ASC],
'desc' => ['bee' => SORT_DESC],
'default' => 'asc',
'label' => 'B',
]
]))
->withOrder([ // <--- apply sorting
'a' => 'desc',
'b' => 'asc',
]);
There’s additional method for getting sorting from strings:
$sort->withOrderString(' -a, b'); // a DESC, b ASC
Sort object is passed to data reader before reading data and it affecting reading as part of criteria.
Filtering
This part I’m a bit stuck with. Currently there’s a set of criteria that are general enough under Reader\Criterion
:
$criteria = new AndAll(
new Compare('test', 42),
new Compare('test2', 34),
new OrAny(
new LessThan('temperature', 10),
new GreaterThan('temperature', 30)
)
);
Since filter is likely to be set from JSON, passed in request parameter, the filter itself is separate and is accepting array:
$filter = new Filter($criteria->toArray());
The filter is passed to data reader and reader should return a subset of data based on it.
But there are problems with current code:
- Extensibility.
- Applying these criteria to concrete data reader isn’t simple.
This part I’d like to get ideas for and help most.
Pagination
There are two pagination types included. Offset, classical one and keyset. Data reader must implement a subset of reader interfaces in order to be used with these. Pagination classes are not working with request directly but providing easy way to set/get data.