beatswitch/distil

0.2.0 2019-11-05 14:42 UTC

This package is auto-updated.

Last update: 2024-09-19 19:01:43 UTC


README

Build Status StyleCI Licence

Distil 为 PHP 提供了一个简单的 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 之一的 Criterion 实例也可以用作 Distil\Criteria 工厂。

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

// can be rewritten as...

Author::criteria($authorId);

在内部,这个命名构造函数将它的参数委托给 Criterion 的默认构造函数,并将自己传递给一个新的 Distil\Criteria 实例。

如果您想在不需要扩展任何可用 Criterion 类型的情况下,将 Criterion 用作 Criteria 工厂,可以使用 Distil\ActsAsCriteriaFactory 特性。

CriterionFactory

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

$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 中为它们注册解析器。解析器可以是类名或可调用对象(例如命名构造函数、匿名函数等),它实际创建了 Criterion。

// 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 许可证。有关更多信息,请参阅 许可证文件