koshuang / expression
PHP 的规范模式实现和逻辑表达式。
Requires
- php: >=8.1
Requires (Dev)
- phpstan/phpstan: ^1.8
- phpunit/phpunit: ^9.5
- sebastian/version: ^3.0
- spatie/phpunit-watcher: ^1.23
This package is auto-updated.
Last update: 2024-09-30 01:32:43 UTC
README
最新版本: 2.0.0
PHP >= 8.1
这是从 https://github.com/webmozart/expression 分支出来的版本。已升级以支持 PHP 8.1。
这个库实现了 PHP 的 规范模式。您可以使用它通过评估逻辑表达式轻松过滤领域服务的查询结果。
与 rulerz 相反,这个库首先专注于提供可用的和高效的 PHP API。可以在上面构建一个将字符串表达式转换为 Expression
实例的表达式语言,但当前版本不包括在内。
可以实现访问者,将 Expression
对象转换为 Doctrine 查询等类似对象。
安装
使用 Composer 安装包
$ composer require koshuang/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
对象。
量词
量词应用于集合,并测试表达式是否匹配一定数量的元素。其中一个著名的量词是所有量词
$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));
您可以在选择器中使用and/or
$expr = Expr::method('getAge', Expr::greaterThan(35)->orLessThan(20));
如果您想混合使用"and"和"or"运算符,请使用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
// 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; }
作者
贡献
对包的贡献总是受欢迎的!
- 报告问题
- 您可以在包的Git仓库中获取源代码。
支持
如果您遇到问题,请发送邮件至bschussek@gmail.com或在Twitter上@webmozart。
许可证
本包所有内容均受MIT许可证许可。