webmozart / expression

PHP 的 Specification 模式和逻辑表达式的实现。

1.0.0 2015-12-17 10:43 UTC

This package is auto-updated.

Last update: 2024-09-08 00:39:51 UTC


README

Build Status Build status Latest Stable Version Total Downloads Dependency Status

最新版本: 1.0.0

PHP >= 5.3.9

此库实现了 PHP 的 Specification 模式。您可以使用它通过评估逻辑表达式轻松过滤域服务的结果。

rulerz 相反,此库首先专注于提供可用且高效的 PHP API。可以在此基础上构建一个将字符串表达式转换为 Expression 实例的表达式语言,但当前版本中不包括。

可以实现访问者,将 Expression 对象转换为 Doctrine 查询和类似对象。

安装

使用 Composer 安装此包

$ composer require webmozart/expression

基本用法

在服务类的方法中使用 Expression 接口

use Webmozart\Expression\Expression;

interface PersonRepository
{
    public function findPersons(Expression $expr);
}

从仓库查询人员时,您可以使用 Expr 工厂类创建新的表达式

$expr = Expr::method('getFirstName', Expr::startsWith('Tho'))
    ->andMethod('getAge', Expr::greaterThan(35));
    
$persons = $repository->findPersons($expr);

仓库实现可以使用 evaluate() 方法将个人与标准匹配

class PersonRepositoryImpl implements PersonRepository
{
    private $persons = [];
    
    public function findPersons(Expression $expr)
    {
        return Expr::filter($this->persons, $expr);
    }
}

可以构建 访问者,将表达式转换为其他类型的规范,例如 Doctrine 查询构建器。

域表达式

扩展现有表达式以构建特定领域的表达式

class IsPremium extends Method
{
    public function __construct()
    {
        parent::__construct('isPremium', [], Expr::same(true));
    }
}

class HasPreviousBookings extends Method
{
    public function __construct()
    {
        parent::__construct(
            'getBookings', 
            [], 
            Expr::count(Expr::greaterThan(0))
        );
    }
}

// Check if a customer is premium
if ((new IsPremium())->evaluate($customer)) {
    // ...
}

// Get premium customers with bookings
$customers = $repo->findCustomers(Expr::andX([
    new IsPremium(),
    new HasPreviousBookings(),
]));

以下部分详细介绍了核心表达式。

表达式

Expr 类可以创建以下表达式

选择器

对于数组或对象等复合值,您通常只想匹配该值的部分(如数组键或 getter 的结果)与表达式。您可以使用 选择器 选择评估部分。

当评估数组时,使用 key() 选择器匹配数组键的值

$expr = Expr::key('age', Expr::greaterThan(10));

$expr->evaluate(['age' => 12]);
// => true

每个选择器方法接受一个表达式作为最后一个参数,该表达式用于评估所选值。

当评估对象时,使用 property()method() 来评估属性值和方法调用的结果

$expr = Expr::property('age', Expr::greaterThan(10));

$expr->evaluate(new Person(12));
// => true

$expr = Expr::method('getAge', Expr::greaterThan(10));

$expr->evaluate(new Person(12));
// => true

method() 选择器还接受将被传递给方法的参数。在评估表达式之前传递参数

$expr = Expr::method('getParameter', 'age', Expr::greaterThan(10));

$expr->evaluate(new Person(12));
// => true

您可以嵌套选择器以评估嵌套对象或数组的表达式

$expr = Expr::method('getBirthDate', Expr::method('format', 'Y', Expr::lessThan(2000)));

$expr->evaluate(new Person(12));
// => false

以下表格列出了所有可用的选择器

count() 选择器接受数组和 Countable 对象。

量词

量词应用于集合,并测试表达式是否与一定数量的元素匹配。一个著名的例子是 all-量词

$expr = Expr::all(Expr::method('getAge', Expr::greaterThan(10)));

$expr->evaluate([new Person(12), new Person(11)]);
// => true

量词接受数组和 Traversable 实例。以下表格列出了所有可用的量词

逻辑运算符

您可以使用 not() 取消一个表达式的符号

$expr = Expr::not(Expr::method('getFirstName', Expr::startsWith('Tho')));

您可以使用 "and*" 方法使用 "and" 连接多个表达式

$expr = Expr::method('getFirstName', Expr::startsWith('Tho'))
    ->andMethod('getAge', Expr::greaterThan(35));

对于 "or" 运算符,情况相同

$expr = Expr::method('getFirstName', Expr::startsWith('Tho'))
    ->orMethod('getAge', Expr::greaterThan(35));

您可以在选择器中使用“和”和“或”。

$expr = Expr::method('getAge', Expr::greaterThan(35)->orLessThan(20));

如果您想混合使用“和”和“或”运算符,请使用andX()orX()来添加嵌套表达式。

$expr = Expr::method('getFirstName', Expr::startsWith('Tho'))
    ->andX(
        Expr::method('getAge', Expr::lessThan(14))
            ->orMethod('isReduced', Expr::same(true))
    );

测试

为了确保PHPUnit正确比较Expression对象,您应该在PHPUnit引导文件中将ExpressionComparator注册到PHPUnit中。

// tests/bootstrap.php
use SebastianBergmann\Comparator\Factory;
use Webmozart\Expression\PhpUnit\ExpressionComparator;

require_once __DIR__.'/../vendor/autoload.php';

Factory::getInstance()->register(new ExpressionComparator());

请确保文件在phpunit.xml.dist中正确注册。

<!-- phpunit.xml.dist -->
<?xml version="1.0" encoding="UTF-8"?>

<phpunit bootstrap="tests/bootstrap.php" colors="true">
    <!-- ... -->
</phpunit>

ExpressionComparator确保PHPUnit通过逻辑等价性而不是通过对象相等性来比较不同的Expression实例。例如,以下Expression在逻辑上是等价的,但作为对象并不相等。

// Logically equivalent
$c1 = Expr::notNull()->andSame(35);
$c2 = Expr::same(35)->andNotNull();

$c1 == $c2;
// => false

$c1->equivalentTo($c2);
// => true

// Also logically equivalent
$c1 = Expr::same(35);
$c2 = Expr::oneOf([35]);

$c1 == $c2;
// => false

$c1->equivalentTo($c2);
// => true

表达式转换

在某些情况下,您可能希望将表达式转换为其他表示形式。一个典型的例子是将表达式转换为Doctrine查询。

您可以通过实现自定义的ExpressionVisitor来完成转换。访问者的方法enterExpression()leaveExpression()会为表达式树的每个节点调用。

use Webmozart\Expression\Traversal\ExpressionVisitor;

class QueryBuilderVisitor implements ExpressionVisitor
{
    private $qb;
    
    public function __construct(QueryBuilder $qb)
    {
        $this->qb = $qb;
    }
    
    public function enterExpression(Expression $expr)
    {
        // configure the $qb...
    }
    
    public function leaveExpression(Expression $expr)
    {
        // configure the $qb...
    }
}

使用ExpressionTraverser与您的访问者遍历表达式。

public function expressionToQueryBuilder(Expression $expr)
{
    $qb = new QueryBuilder();
    
    $traverser = new ExpressionTraverser();
    $traverser->addVisitor(new QueryBuilderVisitor($qb));
    $traverser->traverse($expr);
    
    return $qb;
}

作者

贡献

欢迎对包的贡献!

支持

如果您遇到问题,请发送电子邮件至[email protected]或在Twitter上联系@webmozart

许可证

本包的所有内容均受MIT许可证许可。