xdimedrolx / rulerz
Powerful implementation of the Specification pattern
Fund package maintenance!
Patreon
Requires
- php: >=7.4
- symfony/property-access: ^3.0|^4.0|^5.0|^6.0
- xdimedrolx/hoa-ruler: ^1.0
Requires (Dev)
- ext-json: *
- behat/behat: ~3.0
- dborsatto/phpspec-data-provider-extension: ^1.0
- kphoen/rusty: dev-master
- liip/rmt: ^1.2
- mikey179/vfsstream: ~1.4
- phpspec/phpspec: >=7.0
Suggests
- kphoen/rulerz-spec-builder: If you want a specification builder
- rulerz-php/doctrine-dbal: To execute rules against Doctrine DBAL targets
- rulerz-php/doctrine-orm: To execute rules against Doctrine ORM targets
- rulerz-php/elasticsearch: To execute rules against Elasticsearch targets
- rulerz-php/eloquent: To execute rules against Eloquent targets
- rulerz-php/pomm: To execute rules against Pomm targets
- rulerz-php/solarium: To execute rules against Solarium targets
Replaces
- kphoen/rulerz: >=0.20|dev-master
README
The central idea of Specification is to separate the statement of how to match a candidate, from the candidate object that it is matched against.
Specifications, explained by Eric Evans and Martin Fowler
RulerZ is a PHP implementation of the Specification pattern which puts the emphasis on three main aspects:
- an easy and data-agnostic DSL to define business rules and specifications,
- the ability to check if a candidate satisfies a specification,
- the ability to filter or query any datasource to only retrieve candidates matching a specification.
Introduction
Business rules can be written as text using a dedicated language, very close to SQL, in which case we refer to them as rules or they can be encapsulated in single classes and referred to as specifications.
Once a rule (or a specification) is written, it can be used to check if a single candidate satisfies it or directly to query a datasource.
The following datasources are supported natively:
- array of arrays,
- array of objects.
And support for each one of these is provided by an additional library:
- Doctrine DBAL QueryBuilders: rulerz-php/doctrine-dbal,
- Doctrine ORM QueryBuilders: rulerz-php/doctrine-orm,
- Pomm models: rulerz-php/pomm,
- Elasticsearch (using the official client: rulerz-php/elasticsearch,
- Solr (using the solarium: rulerz-php/solarium,
- Laravel's Eloquent ORM: rulerz-php/eloquent.
Killer feature: when working with Doctrine, Pomm, or Elasticsearch, RulerZ is able to convert rules directly in queries and does not need to fetch data beforehand.
That's cool, but why do I need that?
First of all, you get to express business rules in a dedicated, simple language. Then, these business rules can be encapsulated in specification classes, reused and composed to form more complex rules. Specifications are now reusable and testable. And last but not least, these rules can be used both to check if a candidate satisfies it and to filter any datasource.
If you still need to be conviced, you can read the whole reasoning in this article.
Quick usage
As a quick overview, we propose to see a little example that manipulates a simple rule and several datasources.
1. Write a rule
The rule hereafter describes a "high ranked female player" (basically, a female player having more than 9000 points).
$highRankFemalesRule = 'gender = "F" and points > 9000';
2. Define a datasource
We have the following datasources:
// a Doctrine QueryBuilder $playersQb = $entityManager ->createQueryBuilder() ->select('p') ->from('Entity\Player', 'p'); // or an array of arrays $playersArr = [ ['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500], ['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230], ['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001], ]; // or an array of objects $playersObj = [ new Player('Joe', 'M', 40, 2500), new Player('Moe', 'M', 55, 1230), new Player('Alice', 'F', 27, 9001), ];
3. Use a rule to query a datasource
For any of our datasource, retrieving the results is as simple as calling the
filter
method:
// converts the rule in DQL and makes a single query to the DB $highRankFemales = $rulerz->filter($playersQb, $highRankFemalesRule); // filters the array of arrays $highRankFemales = $rulerz->filter($playersArr, $highRankFemalesRule); // filters the array of objects $highRankFemales = $rulerz->filter($playersObj, $highRankFemalesRule);
3. (bis) Check if a candidate satisfies a rule
Given a candidate, checking if it satisfies a rule boils down to calling the
satisfies
method:
$isHighRankFemale = $rulerz->satisfies($playersObj[0], $highRankFemalesRule);
Going further
Check out the documentation to discover what RulerZ can do for you.
License
This library is under the MIT license.