数据提供者、分页及相关抽象

dev-master 2024-09-25 08:11 UTC

This package is auto-updated.

Last update: 2024-09-25 14:32:11 UTC


README

Yii

Yii 数据


Latest Stable Version Total Downloads Build status Code Coverage Mutation testing badge static analysis type-coverage psalm-level

该包提供通用数据抽象。目标是隐藏读取、写入和处理数据操作中的存储方面。

特性包括

  • 具有计数、排序、限制和偏移、读取条件过滤器以及后过滤器的数据读取器抽象。
  • 包括偏移和键集实现的分页抽象。
  • 数据写入器抽象。
  • 数据处理器抽象。

要求

  • PHP 8.1 或更高版本。

安装

该包可以使用 Composer 安装。

composer require yiisoft/data

通用用法

概念

  • 每个数据集由项目组成。
  • 每个项目有多个命名字段。
  • 数据集中的所有项目都具有相同的结构。

读取数据

数据读取器的目的是从数据库、数组或 API 等存储中读取数据,并将其转换为简单的字段 => 值迭代器。

$reader = new MyDataReader(...);
$result = $reader->read(); 

结果是 iterable,因此可以在其上使用 foreach。如果您需要一个数组,可以按以下方式实现

// using is foreach
foreach ($result as $item) {
    // ...
}

// preparing array
$dataArray = $result instanceof \Traversable ? iterator_to_array($result, true) : (array)$result;

限制读取的项目数量

您可以在迭代器中限制项目的数量

$reader = (new MyDataReader(...))->withLimit(10);
foreach ($reader->read() as $item) {
    // ...
}

计算项目总数

要获取实现 CountableDataInterface 的数据读取器中的项目总数

$reader = new MyDataReader(...);
$total = count($reader);

过滤

数据过滤可以分两步完成

  1. 形成获取数据的条件。这是通过“过滤器”完成的。
  2. 通过迭代和检查每个项目来后过滤数据。这是通过带有过滤器的 IterableDataReader 完成的。

尽可能使用条件,因为通常它提供了更好的性能。

要在实现 FilterableDataInterface 的数据读取器中过滤数据,您需要向 withFilter() 方法提供过滤器

$filter = new All(
    new GreaterThan('id', 3),
    new Like('name', 'agent')
);

$reader = (new MyDataReader(...))
    ->withFilter($filter);

$data = $reader->read();

过滤器可以组合

  • 所有
  • 任何
  • 介于
  • 等于
  • 等于空
  • 大于
  • 大于等于
  • ILike
  • 小于
  • 小于等于
  • 不是

使用数组进行过滤

所有任何 过滤器都有 withCriteriaArray() 方法,允许您使用数组定义过滤器。

$dataReader->withFilter((new All())->withCriteriaArray([
    ['=', 'id', 88],
    [
       'or',
       [
          ['=', 'color', 'red'],
          ['=', 'state', 1],
       ]
    ]
]));

实现自己的过滤器

要拥有自己的过滤器

  • 至少实现 FilterInterface,它包括
    • getOperator() 方法,它返回表示过滤操作的字符串。
    • toArray() 方法,它返回包含过滤参数的数组。
  • 如果您想为特定的数据读取器类型创建过滤器处理程序,则需要至少实现 FilterHandlerInterface。它有一个单个的 getOperator() 方法,该方法返回表示过滤操作的字符串。此外,每个数据读取器都指定一个扩展接口,用于处理或构建操作。例如,IterableDataFilter 定义了 IterableFilterHandlerInterface,它包含执行过滤操作的附加 match() 方法。

您可以使用 withFilterHandlers() 方法将您自己的过滤器处理程序添加到数据读取器中。您可以将任何过滤器处理程序添加到读取器中。如果读取器无法使用过滤器,则过滤器将被忽略。

// own filter for filtering
class OwnNotTwoFilter implenents FilterInterface
{
    private $field;

    public function __construct($field)
    {
        $this->field = $field;
    }
    public static function getOperator(): string
    {
        return 'my!2';
    }
    public function toArray(): array
    {
        return [static::getOperator(), $this->field];
    }
}

// own iterable filter handler for matching
class OwnIterableNotTwoFilterHandler implements IterableFilterHandlerInterface
{
    public function getOperator(): string
    {
        return OwnNotTwoFilter::getOperator();
    }

    public function match(array $item, array $arguments, array $filterHandlers): bool
    {
        [$field] = $arguments;
        return $item[$field] != 2;
    }
}

// and using it on a data reader
$filter = new All(
    new LessThan('id', 8),
    new OwnNotTwoFilter('id'),
);

$reader = (new MyDataReader(...))
    ->withFilter($filter)
    ->withFilterHandlers(
        new OwnIterableNotTwoFilter()
        new OwnSqlNotTwoFilter()    // for SQL
        // and for any supported readers...
    );

$data = $reader->read();

排序

要在实现SortableDataInterface的数据读取器中对数据进行排序,您需要向withSort()方法提供一个排序对象。

$sorting = Sort::only([
    'id',
    'name'
]);

$sorting = $sorting->withOrder(['name' => 'asc']);
// or $sorting = $sorting->withOrderString('name');

$reader = (new MyDataReader(...))
    ->withSort($sorting);

$data = $reader->read();

Sort的目标是将逻辑字段排序映射到实际数据集字段排序,并形成数据读取器的标准。逻辑字段是用户操作的字段。实际字段是在数据集中实际存在的字段。这种映射有助于您需要根据一个逻辑字段进行排序,而实际上该字段在底层数据集中由多个字段组成。例如,您为用户提供一个包含实际数据集中姓氏和名字字段的用户名。

要获取Sort实例,您可以使用Sort::only()Sort::any()。使用Sort::only()时,会忽略用户指定的无配置的逻辑字段的顺序。Sort::any()直接使用用户指定的无配置的逻辑字段名和顺序。

无论哪种方式,您都需要传递一个配置数组,指定哪些逻辑字段应该是可排序的,并且可选地提供这些字段如何映射到实际字段顺序的详细信息。

要应用当前顺序,请使用withOrder(),您提供一个数组,其中键对应于逻辑字段名,值对应于顺序(ascdesc)。或者可以使用withOrderString()。在这种情况下,顺序表示为一个包含逗号分隔的逻辑字段名的单个字符串。如果名称以-开头,则顺序方向设置为desc

跳过一些项

如果您需要从实现OffsetableDataInterface的数据读取器中跳过一些项。

$reader = (new MyDataReader(...))->withOffset(10);

实现您自己的数据读取器

要拥有您自己的数据读取器,您至少需要实现DataReaderInteface。它有一个单一的read()方法,该方法返回表示一组项的可迭代对象。

可以实现其他接口来支持不同的分页类型、排序和筛选。

  • CountableDataInterface - 允许获取数据读取器中的项目总数。
  • FilterableDataInterface - 允许根据标准返回项目子集。
  • LimitableDataInterface - 允许返回有限的项目子集。
  • SortableDataInterface - 允许按一个或多个字段进行排序。
  • OffsetableDataInterface - 允许在读取数据时跳过前N个项。

请注意,在实现这些方法时,应该只定义在后续的read()中使用的标准,而不是修改数据。

分页

分页允许获取有限的数据子集,这对于按页显示项目以及在大数据集上获得可接受的性能都很有用。

提供了两种分页类型:传统的偏移分页和键集分页。

偏移分页

偏移分页是一种常见的分页方法,它选择OFFSET + LIMIT项,然后跳过OFFSET项。

优点

  • 可以获得总页数
  • 可以到达特定的页面
  • 数据可以无序
  • 考虑了数据读取器中设置的限制

缺点

  • 性能随着页码的增加而下降
  • 数据中间的插入或删除会使结果不一致

使用方法如下

$reader = (new MyDataReader(...));

$paginator = (new OffsetPaginator($dataReader))
            ->withPageSize(10)
            ->withCurrentPage(2);


$total = $paginator->getTotalPages();
$data = $paginator->read();

键集分页

键集分页是一种替代的分页方法,适用于无限滚动和“加载更多”。它选择LIMIT个键字段值大于或小于(根据排序)指定值的项。

优点

  • 性能不依赖于页码
  • 无论插入和删除如何,结果都是一致的

缺点

  • 无法获得总页数
  • 无法访问特定页面,只有“上一页”和“下一页”
  • 数据不能是无序的
  • 数据读取器中设置的限制导致异常

使用方法如下

$sort = Sort::only(['id', 'name'])->withOrderString('id');

$dataReader = (new MyDataReader(...))
    ->withSort($sort);

$paginator = (new KeysetPaginator($dataReader))
    ->withPageSize(10)
    ->withToken(PageToken::next('13'));

当使用withNextPageToken()获取下一页时,使用最后显示的项目显示的第一页ID(或用于分页的其他字段名)

写入数据

$writer = new MyDataWriter(...);
$writer->write($arrayOfItems);

处理数据

$processor = new MyDataProcessor(...);
$processor->process($arrayOfItems);

文档

如果您需要帮助或有疑问,可以到Yii 论坛。您还可以查看其他Yii 社区资源

许可协议

Yii 数据是免费软件。它根据BSD许可证发布。有关更多信息,请参阅LICENSE

Yii 软件维护。

支持项目

Open Collective

关注更新

Official website Twitter Telegram Facebook Slack