remipelhate/distil

0.2.0 2019-11-05 14:42 UTC

This package is auto-updated.

Last update: 2024-09-19 19:29:35 UTC


README

Build Status StyleCI Licence

Distil 提供了一个简单的 API,用于描述应满足结果集的变量条件数量。例如,这使得通过小值对象动态构建查询变得轻而易举。

入门指南

简单示例

让我们考虑以下场景:你有一个查询对象 GetPosts,默认情况下返回你的博客的所有帖子。然而,在某些地方,你只需要检索特定作者的帖子。查询本身是相同的,你只需要删除由除给定作者以外的任何人创建的帖子。为此,查询对象可以接受可能包含或可能不包含“作者”标准的标准。

$authorId = 369;

// Author is an implementation of Distil\Criterion which acts as a Criteria factory.
$criteria = Author::criteria($authorId);

$posts = $getPostsQuery->get($criteria);

查询对象内部可以根据给定的标准向查询添加语句。Distil 仅描述标准,你可以按任何方式解释它们。你完全掌握控制权。

use Distil\Criteria;

final class GetPosts
{
    public function get(Criteria $criteria)
    {
        ...
        
        if ($criteria->has('author')) {
            $query->where('creator_id', '=', $criteria->get('author')->value());
        }
    }
}

当需要将多个标准传递给单个查询时,这种方法非常有用。想象一下,我们会使用那个 GetPosts 查询对象通过 API 暴露那些帖子,并允许用户按作者、发布日期等进行筛选,并最终进行不同的排序。

$criteria = new Distil\Criteria();

if (isset($requestData['author'])) {
    $criteria->add(Author::fromString($requestData['author']));
}

if (isset($requestData['sort'])) {
    $criteria->add(new Sort($requestData['sort']));
}

...

$posts = $getPostsQuery->get($criteria);

要求

  • PHP >= 7.1

安装

您可以通过 Composer 安装此包

$ composer require remipelhate/distil

概念

标准

标准 是一组条件,描述了应该包含在结果集中的哪些记录(过滤、限制等)或如何呈现(例如,排序)。Distil 将这些单个条件在 Distil\Criteria 集合中表示为 Distil\Criterion 实例(参见下一章)。

添加标准

让我们重新考虑文档顶部的示例。假设我们想要检索 ID 为 1 的作者的所有帖子,并按发布日期升序排序。您可以在构造时传递标准实例。

$criteria = new Distil\Criteria(new Author($authorId), new Sort('publish_date'));

… 或者通过 add() 方法流畅地传递

$criteria = new Distil\Criteria();

$criteria->add(new Author($authorId))
    ->add(new Sort('publish_date'));

集合中的每个标准实例都通过名称唯一。 add() 不允许用具有相同名称的另一个实例覆盖集合中的实例。如果您确实需要覆盖一个,则可以使用 set() 方法。

$criteria = new Distil\Criteria(new Author(1));

$criteria->add(new Author(2)); // This will throw an Exception.
$criteria->set(new Author(2)); // This will overwrite new Author(1) with new Author(2)

获取标准

您可以通过名称检查它是否包含标准实例

$criteria = new Distil\Criteria(new Author(1));

$criteria->has('published'); // returns false
$criteria->has('author'); // returns true

… 并获取它

$criteria = new Distil\Criteria(new Author(1));

$criteria->get('published'); // returns null
$criteria->get('author'); // returns the Author instance

数组访问

Distil\Criteria 实现了 PHP 的 ArrayAccess 接口,这意味着您可以像处理数组一样与之交互。

$criteria[] = new Author(1); // Acts as set()
$author = $criteria['author']; // Acts as get(), but throws an error if the name doesn't exist

标准

标准 是一个结果集应遵循的单个条件。将其视为过滤器、限制、排序等。Distil 将这些表示为小型值对象(通过唯一名称标识),这些对象围绕单个值包装。这些对象必须实现 Distil\Criterion 接口。

示例

再次,让我们回到文档顶部的示例,并为查询对象创建那个 author 过滤器。这个过滤器包含作者 ID

use Distil\Criterion;

final class Author implements Criterion
{
    const NAME = 'author';

    public function __construct(int $id)
    {
        $this->id = $id;    
    }
    
    public function name(): string
    {
        return self::NAME;
    }
    
    public function value(): int
    {
        return $this->id;
    }
}

类型化标准

Distil 随附一套严格类型的抽象类,您可以使用它们为您的标准实现添加一些默认行为。

  • Distil\Types\BooleanCriterion - 包装布尔值。
  • Distil\Types\DateTimeCriterion - 包装 PHP 的 DateTimeInterface 实例,并可选地接受日期时间格式。
  • Distil\Types\IntegerCriterion - 包装整数值。
  • Distil\Types\ListCriterion - 包装数组值。
  • Distil\Types\StringCriterion - 包装字符串值。

这些都可以

  • 通过fromString命名构造函数从字符串值构建(记住,这些类型的默认构造函数都是严格类型的)。这在从URI查询字符串值实例化Criterion实例时尤其有用。
  • 从字符串关键词构建(参见关键词)。
  • 转换为字符串。
  • 充当Distil\Criteria工厂(参见Criteria 工厂)。

因此,我们可以将上面的Author过滤器简化如下

use Distil\Types\IntegerCriterion;

final class Author extends IntegerCriterion
{
    const NAME = 'author';
    
    public function name(): string
    {
        return self::NAME;
    }
}

关键词

当从字符串创建Distil\Criterion时,您不能总是将该字符串值转换为适当的数据类型。因此,Distil 允许您为某些特定值定义关键词。

处理关键词值

让我们通过一个示例来说明这一点,并创建一个Limit过滤器。该过滤器包含的值可以是整数或null(即没有限制)

use Distil\Criterion;

final class Limit implements Criterion
{
    public function __construct(?int $value)
    {
        $this->value = $value;
    }
    
    public static function fromString(string $value): self
    {
        return new self((int) $value);
    }

    public function name(): string
    {
        return 'limit';
    }
    
    public function value(): ?int
    {
        return $this->value;
    }
}

如果我们将此作为公共API上的过滤器提供,我们希望能够将limit=unlimited解析为new Limit(null)。"unlimited"不能自然地转换为null值,因此我们需要将其注册为关键词。为此,我们的限制条件需要实现Distil\Keywords\HasKeywords接口,该接口要求您定义一个keywords()方法

use Distil\Criterion;
use Distil\Keywords\HasKeywords;
use Distil\Keywords\Keyword;

final class Limit implements Criterion, HasKeywords
{
    ...
    
    public static function fromString(string $value): self
    {
        $value = (new Keyword(self::class, $value))->value();
        
        return new self($value ? (int) $value : $value);
    }
    
    public static function keywords(): array
    {
        return ['unlimited' => null];
    }
}

注意在fromString()构造函数中使用的Distil\Keywords\Keyword类。它是一个小型值对象,在构造时接受条件类名称和给定的字符串值。使用keywords()方法,它可以检查给定的字符串值是否为另一个值的标识。如果是,将返回实际值

new Limit(null) == Limit::fromString('unlimited'); // true

现在,假设您想要能够将条件值转换回字符串。使用Distil\Keywords\Value类,您可以轻松地导出值的标识

use Distil\Criterion;
use Distil\Keywords\HasKeywords;
use Distil\Keywords\Keyword;

final class Limit implements Criterion, HasKeywords
{
    ...
    
    public static function keywords(): array
    {
        return ['unlimited' => null];
    }
    
    public function __toString(): string
    {
        return (new Value($this, $this->value))->keyword() ?: (string) $this->value;
    }
}

如果值关联了标识,将返回该标识。如果没有,我们将得到null

(string) Limit::fromString('unlimited') === 'unlimited'; // true

注意:这里仅使用Limit作为示例。它实际上是现成的。请确保查看常见条件部分。

类型化条件的关键词

类型化条件部分所述,任何扩展Distil\Types之一的条件实例在从字符串创建或转换为字符串时都会自动处理关键词。这意味着您只需实现Distil\Keywords\HasKeywords接口,无需重写fromString()__toString()方法。

此外,任何Distil\Types\BooleanCriterion的实例将自动处理字符串值"true"和"false"。

常见标准

Distil提供了一些常用条件

Limit

Distil\Common\Limit是一个包装整数或null值的条件实现。它不扩展条件类型,但具有它们的功能

  • 它可以通过fromString命名构造函数从字符串值构建。
  • 当从字符串值构建时,它接受"unlimited"关键词(映射到null)。
  • 它可以转换为字符串。
  • 它可以用作条件工厂。

此外,Limit有一个默认值(为10)。因此,您可以在没有任何参数的情况下实例化它

$limit = new Distil\Common\Limit();

$limit->value(); // Returns 10, its default value

Sort

Distil\Common\SortDistil\Types\ListCriterion的扩展。它接受应按其排序的字段或属性的列表

$sort = new Distil\Common\Sort('-name', 'created_at');

注意"-"符号在"name"属性前使用,以指示结果集应按该属性降序排序。

它的 value() 方法简单地返回一个包含这些排序字段的数组,但您也可以以小的 Distil\Common\SortField 值对象的形式检索它们

use Distil\Common\SortField;

$sort = new Distil\Common\Sort('-name');

$sort->sortFields() == [new SortField('name', SortField::DESC)];

工厂

条件工厂

任何扩展了 Distil\Types 之一的条件实例也可以用作 Distil\Criteria 工厂

new Distil\Criteria(new Author($authorId));

// can be rewritten as...

Author::criteria($authorId);

在底层,这个命名的构造函数将它的参数委托给条件的默认构造函数,并将自身传递给一个新的 Distil\Criteria 实例。

如果您想在不扩展任何可用的条件类型的情况下使用条件作为条件工厂,您可以使用 Distil\ActsAsCriteriaFactory 特性。

条件工厂

在某些情况下,您可能希望通过名称实例化一个条件。以下是从文档顶部示例中提取的片段

$criteria = new Distil\Criteria();

if (isset($requestData['sort'])) {
    $criteria->add(new Sort($requestData['sort']));
}

if (isset($requestData['limit'])) {
    $criteria->add(Limit::fromString($requestData['limit']));
}

...

而不是在可能使用排序和限制条件的每个控制器中这样做,您可以在 Distil\CriterionFactory 中为它们注册解析器。解析器是一个类名或可调用对象(例如命名构造函数、匿名函数等),它实际上会实例化条件

// Note that these resolvers could be injected into the factory through your IoC container.
$factory = new Distil\CriterionFactory([
    'sort' => Distil\Common\Sort::class,
    'limit' => Distil\Common\Limit::class.'::fromString',
    'foo' => function () {
        // Some logic to resolve the foo criterion...
    },
]);
$criteria = new Distil\Criteria();

foreach ($requestData as $name => $value) {
    $criteria->add($factory->createByName($name, $value)),
}

贡献

有关详细信息,请参阅贡献文件

变更日志

您可以在我们的变更日志文件中查看每个版本的更改列表

许可

MIT 许可证。有关更多信息,请参阅许可文件