webmozart / expression
PHP 的 Specification 模式和逻辑表达式的实现。
Requires
- php: >=5.3.9
Requires (Dev)
- phpunit/phpunit: ^4.6
- sebastian/version: ^1.0.1
This package is auto-updated.
Last update: 2024-09-08 00:39:51 UTC
README
最新版本: 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许可证许可。