Coder Social home page Coder Social logo

k-phoen / rulerz Goto Github PK

View Code? Open in Web Editor NEW
871.0 47.0 96.0 701 KB

Powerful implementation of the Specification pattern in PHP

License: MIT License

PHP 91.59% Pascal 2.96% Makefile 0.21% Gherkin 5.25%
php specifications rules rules-engine library business-rules ruler

rulerz's Introduction

RulerZ Build Status Scrutinizer Code Quality

⚠️ This project is no longer maintained, reach out to me if you are interested in becoming maintainer ⚠️

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:

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.

rulerz's People

Contributors

bobdercole avatar claudusd avatar esserj avatar estelsmith avatar grummfy avatar hywan avatar iainmckay avatar ikwattro avatar j0k3r avatar jdecool avatar jdeniau avatar jhuet avatar k-phoen avatar loicbourg avatar mikaelrandy avatar mnapoli avatar nclshart avatar pioneer32 avatar pitchart avatar royopa avatar syrm avatar yvoyer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rulerz's Issues

Understanding of brackets placement

I need to sync my understanding of how and why rulerz place brackets. Below you can see several examples.
Condidtion:
$rules = '1 = 1 and 1 = 1 and 1 = 1 or 1 = 1';
Result in query:
WHERE ((1 = 1 AND (1 = 1 AND (1 = 1 OR 1 = 1))))

Condition:
(1 = 1) and ((1 = 1) or (1 = 1) or (1 = 1)) and (1 = 1 or 1 = 1 or 1 = 1)
Result:
((1 = 1 AND ((1 = 1 OR (1 = 1 OR 1 = 1)) AND (1 = 1 OR (1 = 1 OR 1 = 1)))))

In the second example, it increases the deep of Doctrine query. In result: simple condition becomes enormous.
So, I have a situation where I need only a lot of conditions joined with OR, but after adding brackets it becomes 256 nested level error.
I don't understand it. Is it always add brackets? How should I avoid this? Why it so? What is the main idea of it? I didn't find any mention of this case in documentation. Thank for your answer, your library is really great.

Positional parameters numbering not starting from 0 if Parser used multiple times for DoctrineORMVisitor

First of all, let me say that I like this library very much! I'm just getting to know it (after a nice workshop on PHPCon Poland) but it is something that I was trying to pursue in some of my own projects for building database queries. Your version is much more generic superior to what I was able to achieve though. Kudos to you!

I've been playing around with master version (together with RulerZBundle) and positional parameters (as I need to apply same specification few times) when I noticed that if I use Parser twice in a single request (e.g. by filtering with different specifications on two different targets) positional parameters are not numbered correctly in case of a second and following queries. Assuming that the first query has three such parameters I'm getting ?0, ?1 and ?2 as expected, but for the second query I'm getting ?3, which causes exception when Doctrine tries to execute the query (as the parameters array has only key equal to 0).

The line responsible for this is https://github.com/malef/rulerz/blob/master/src/Parser/Parser.php#L184 - I suppose a new Parser instance should be created each time, or the counter should be reset. At the moment I've just started diving into the details of this library so I can't propose a proper PR with fix yet, but maybe I will do it in the future.

At the same time I would also like to know your opinion on replacing parameter names with some unique strings instead of using positional parameters. The latter solution probably won't work if RulerZ will receive a queryBuilder instance for which some positional parameters are already used, as it starts numbering from 0, effectively overriding them. In solutions I was trying to implement I was trying to isolate as much as possible from parameters that may have been added before, usually by adding some value provided by uniqid. (It was mostly because different specifications were comming from different parts of applications which were not aware of each other.) I've tried it also with this library while using version 0.19.1 (by generating unique parameter names inside each instance of Specification) and it allowed me to repeat same specification multiple times, but I'm pretty sure it messes up caching and probably some other things. Do you think this approach would be feasible in case of this library or do you want to take another route?

Alias Conflict with Doctrine Auto Join

Hello,

I am trying to filter entities with the DoctrineOrm target. I've ran into a very particular issue where the auto join code attempts re-use an already defined alias, resulting in a Doctrine QueryException. I am using the latest version (0.20.4) of RulerZ. Here are the conditions in which this occurs:

  • The entity has two associations.
  • The associations themselves have additional associations.
  • The additional associations are of the same entity.
  • The additional association names are the same. I'm not sure if this is relevant.
  • The filter involves the additional associations.

Here is a simple example of the association map:

  • User [of type User]
    • Groups [of type Group]
      • Owner [of type Person]
    • Places [of type Place]
      • Owner [of type Person]

When I filter on the user group owner id and the user place owner id, the auto join code assigns both associations the same alias.

I've narrowed the culprit down to this line. I found that adding more specificity to the alias name resolves the issue. For example, I've done the following as a workaround:

$alias = sprintf('_%d_%s', count($this->knownEntities, COUNT_RECURSIVE), $dimension);

Let me know your thoughts. I can open a pull request, but I'm not sure if this is the best fix.

AbstractCompiler don't work with class declared with ::class

If you declare your traits like that :

protected function getExecutorTraits()
    {
        return [
            FilterTrait::class,
            FilterBasedSatisfaction::class
        ];
    }

AbstractCompiler compile wrong class.
Ligne 25 should be replaced from :

            return "\t" . 'use ' . $trait . ';';

to :

            return "\t" . 'use \\' . ltrim($trait, '\\') . ';';

Issue with the FileCompiler and Elasticsearch

Hi, here is the code generated by the FileCompiler when calling filter() for the following rule price = 30 ($rulerz->filter($elasticsearch, 'price = 30', [], $context)):

<?php
namespace RulerZ\Compiled\Executor;

use RulerZ\Executor\Executor;

class Executor_3f155154 implements Executor
{
        use \RulerZ\Executor\Elasticsearch\ElasticsearchFilterTrait;
        use \RulerZ\Executor\Polyfill\FilterBasedSatisfaction;

    protected function execute($target, array $operators, array $parameters)
    {
        return Array;
    }
}

As you can see, there's an error: execute() returns Array (the compiled rules seem to be an array instead of a string).

I believe (I might be wrong) that this is because the elasticsearch implementation returns arrays (instead of strings) for operators.

Am I doing something wrong here? Here is the configuration of RulerZ:

$compiler = new RulerZ\Compiler\FileCompiler(new RulerZ\Parser\HoaParser);
$rulerz = new RulerZ\RulerZ($compiler, [
    new RulerZ\Compiler\Target\Elasticsearch\ElasticsearchVisitor,
])

Add support for filtering Doctrine PersistentCollection instances by query

If I have an X-to-many lazy-loaded association in Doctrine and Doctrine’s Criteria functionality to filter the associated entities, it will build and execute a query including the filters. If the associated entities were already loaded, it will filter the entities in PHP rather than running another query.

It would be super useful to have this ability in RulerZ, rather than have it load all associated entities then filter them as it does currently.

Using spaces in identifiers

We use RulerZ to filter arrays and databases, sometimes with the same rule(s) and have no control over how the identifiers/indexes/columns are formatted.

The data we receive can have columns such as First Name or Category - A (Level 1).

I've read through the documentation and code and there doesn't appear to be any support for identifiers with spaces.

Would it be possible to have support for identifiers with spaces added?

Generator Object response

Hi ! I start to try RulerZ with a simple example in a repository :

<?php
// src/AppBundle/Entity/Person/Repository/PersonRepository.php
namespace AppBundle\Entity\Person\Repository;

use Doctrine\ORM\EntityRepository;
use RulerZ\Compiler\FileCompiler;
use RulerZ\Parser\HoaParser;
use RulerZ\Compiler\Target;
use RulerZ\RulerZ;

class PersonRepository extends EntityRepository
{
    public function testRule(){
        // compiler
        $compiler = new FileCompiler(new HoaParser());
        $rulerz = new RulerZ(
            $compiler, [
                new Target\ArrayVisitor([
                    'length' => 'strlen'
                ]),
            ]
        );

        $users = [
            ['pseudo' => 'Joe',   'gender' => 'M', 'points' => 40],
            ['pseudo' => 'Moe',   'gender' => 'M', 'points' => 20],
            ['pseudo' => 'Alice', 'gender' => 'F', 'points' => 60],
        ];

        $rule  = 'gender = :gender';
        $parameters = [
            'gender' => 'F',
        ];

        echo '<pre>';
        var_dump($rulerz->filter($users, $rule, $parameters));
        echo '</pre>';
        exit();
    }
}

?>

I just have "object(Generator)[919]" as answer.
any ideas what I forgot ?
Thanks !

Cheers.
Vincent.

DoctrineQueryBuilderVisitor compile issue

\RulerZ\Compiler\Target\Sql\DoctrineQueryBuilderVisitor::$detectedJoins must be reseted after compilation, otherwise one rule will use $detectedJoins data of previous rule

Cannot use the same specification multiple times?

Hello. I love that someone is finally making a nice specification-pattern library for PHP. However, I have a concern I've ran across.

In the example below, it appears that if I want to filter by more than one group, it will overwrite the parameter with the last group that was specified. This effectively makes the specification as if I had set $spec to new GroupSpec('group 2').

Are there any plans to remedy this within the library itself, or is it something users will need to be aware of when writing custom specifications?

class GroupSpec implements RulerZ\Spec\Specification
{
    private $group;

    public function __construct($group)
    {
        $this->group = $group;
    }

    public function getRule()
    {
        return 'group = :group';
    }

    public function getParameters()
    {
        return [
            'group' => $this->group
        ];
    }
}


$spec = new RulerZ\Spec\OrX([
    new GroupSpec('group 1'),
    new GroupSpec('group 2')
]);

var_dump($spec->getParameters());
/*
 * array(1) {
 *  'group' =>
 *  string(7) "group 2"
 * }
 */

var_dump($spec->getRule());
// string(32) "group = :group OR group = :group"

Fos Elastica

Hi !
Do you ever use Rulerz with FOS Elastica ?
I am blocked here :

$search = new Elastica\Search($client);

I don't know wich services I need to use, if you have any Ideas, I take it :)

Thanks,

Vincent.

Using without DSL

First off, this library is excellent. I would really, really like to use this library in our project but the DSL makes it a nonstarter for our specific use case. Is it possible to use this library without the DSL? Is it possible to split the library into reusable components? From a high level (without looking at the internals), the DSL, the spec pattern, and the storage abstraction layer seem like they might be better off as separate pieces.

Duplicate auto joins for DoctrineORMVisitor

When I'm using a complex composite specification on master branch that uses some autojoin multiple times then for DoctrineORMVisitor detected joins include duplicates. E.g. if my rule contains sections like foo.id = ? AND foo.name = ? it results in two joins to foo table, both having the same alias like _3_foo. This results in Doctrine throwing an exception when query is executed as same alias for join is used multiple times (e.g. there are two identical LEFT JOINs with the same alias).

Am I doing something wrong here? For now I did a simple workaround just to keep me going (you can have a look at it at malef@28f8ac1) but I'm not sure if this is a bug or if I'm misusing this library.

Doctrine nested embeddables do not work

Hi, when having several levels of doctrine embeddables and filtering, an exception "Could find embeddable" is thrown.

This variant works properly when using doctrine QB
$qb ->select('u') ->from(User::class, 'u') ->where('u.organization.organizationId.id = :organization_id') ->setParameter('organization_id', 1)

but doesn't work with rulerz
$qb ->select('s') ->from(SubscriptionProgram::class, 's'); $rulerz->filter($qb, 'organization.organizationId.id = 1');

Thanks.

Doctrine Embeddable Support

Hello,

First off, thank you for this awesome library! I am attempting to utilize RulerZ with Doctrine. I have a basic working implementation thanks to the cookbook. However, I am facing an issue with Doctrine Embeddables.

Here is a snippet of my entity map file:

Website:
    type: entity
    table: websites
    embedded:
        domain_name:
            class: DomainName

Here is a snippet of my embeddable map file:

DomainName:
  type: embeddable
  fields:
    value:
        type: string
        nullable: false
        length: 255
        options:
            fixed: false
        column: url

Here is a snippet of my RulerZ specification file:

public function getRule()
{
    return 'domain_name.value = :domain_name';
}

public function getParameters()
{
    return [
        'domain_name' => $this->domain_name
    ];
}

According to the Doctrine documentation, the DQL should look like this:

SELECT w FROM Website w WHERE w.domain_name.value = :domain_name

However, RulerZ is treating the embeddable as an association. The generated DQL looks like this:

SELECT w FROM Website w INNER JOIN w.domain_name rulerz_domain_name WHERE rulerz_domain_name.value = :domain_name

What am I doing wrong?

Test rules to make sure they are valid

When a UI is responsible for generating some rules, we need to make sure they are valid (that they will execute correctly). Is there a way to "test" that some rules are valid?

What I thought of is trying to compile them with the compiler that Rulerz uses, but:

  • is that enough to make sure they are valid (i.e. no syntax errors)
  • in $compiler->compile($rule, $targetCompiler) can I simply use ArrayVisitor (most basic one) or do I need to set up something more complex?

And finally would it make sense to add that to Rulerz directly?

Contribution guide

Write a contribution guide with details on how to setup a development environment (using docker, Vagrant, ...), how to launch the tests, how to write a patch, …

Joins

Does this only support single tables or can it filter on table joins?

Complex queries

Hi @K-Phoen,

Just wondering if "complex" doctrine queries are possible with the current specification implementation.

Something like this:

SELECT p
FROM Entity:Product p
JOIN p.categories c1
WHERE NOT EXISTS (
    SELECT c2
    FROM Entity:Category c2
    WHERE c2.lft <= c1.lft AND
          c2.rgt >= c1.rgt AND
          c2.status = 'deleted'
)

It basically filters categories that have inactive ancestors.

Also, how to implement something like this with RulerZ:

Spec::lt('startDate', new \DateTime('-4weeks'))

Create Cypher Compiler (for Neo4j)

Hi all,

thank you for this package.

I need to write a Cypher Compiler/Executer but i don't understand how RulerZ works and how to start.

Please can you explain how to do it?

Thank you!

Usage of Specification with aliased query and support Native

Hi,

During reflection with my collaborator, we have hurt a global lib problem (IMO).

If we want create a standardises rule, Usable in QB, Array, Object, Some else..
we need keep a non contextual rule like 'gender = "F"' BUT, if i want use them on Joined Table, Aliased, or subquery, I need add alias of rule Like : 'user.gender = "F"' Or some else...

Actually we solve this issue with that :

<?php

namespace Specification;

use RulerZ\Spec\AbstractSpecification;

class IsValid extends AbstractSpecification
{
    /**  @var string|null */
    private $alias;
    
    public function __construct($alias = null){
        $this->alias = null !== $alias ? $alias.'.' : null;
    }

    public function getRule() {
        return $this->alias.'valide = "1"';
    }
}

We thinks is not a 'good practice', 'easy usable' and this problem is generic of rules.

Better solution to offer ?

Thanks,
Alborq.

Allow to compare Date

I used rulerz 1.9.1 in a project. I updated it to the lateast version (0.20.4) and my specifications classes with date comparison wasn't working anymore.

Example of existing specification :

class LeaderIsRequired extends AbstractSpecification
{
    private $mandatoryDate;

    public function __construct(int $mandatoryDay)
    {
        $this->mandatoryDate = Chronos::now()->addDay($mandatoryDay);
    }

    public function getRule()
    {
        return 'inShopDate <= :mandatory_date';
    }

    public function getParameters()
    {
        return [
            'mandatory_date'    => $this->mandatoryDate
        ];
    }
}

I looked where the problem could come from and I think it was due to this commit because now the ObjectContext wrap the Datetime object.

So I created a fix for this : #88

Be induglent, it's my first real PR and issue on github :).

ObjectContext wraps a null value with an ObjectContext instance

The following code fails:

$objectHasNull = new \stdClass;
$objectHasNull->foo = null;
assert($this->rulerz->satisfies($objectHasNull, 'foo = null'));

with assert(): assert($this->rulerz->satisfies($objectHasNull, 'foo = null')) failed

However, it's expected to pass the assertion.

Get SQL where clause for native query

Hello,

I would like to use RulerZ library in a legacy environment and I need to apply a formatted rule into a pure SQL query.
Can you tell me how can I get a formatted rule like the "execute" method directly without passing it to the "satisfied" nor "filter" methods ?

Thanks for helping.

Cordially,
Anthony Dessalles

Security and SQL injection

Thanks for this awesome library.

I have a question about security: if I use a app that construct a rule on front-end and send it to the back-end to make request on a database. Need I validates values from injections ? The Ruler compiler could only give String, Boolean, Float, Int. The only problem could come from String. But as I use prepare queries and inject values by parameters, I think there could not be a vulnerability.
Am I right ? Do you see other problems ?

Testing for null

Hello.
I want to check that a value is null or is not null.
Now I got error: Expected known function, got 'is'"
How can I do it?

Empty rule

Hi!

What do you think about the support of empty rule?
It should return all the data when we use the filter method and always return true with satisfies.
Currently, if we use a blank rule, it throw a Hoa\Compiler\Exception\UnexpectedToken with this message:

Unexpected token "EOF" (EOF) at line 1 and column 1

Thanks!

UI: get parameters and highlight code

I write a rule manager for the end-users.
How can I extract all parameters from a rule like user.group in :group?
I would like to generate the form which it asks a value for each parameter.
I can do it with a regexp: \b:\w+\b but I'm not sure that it matches exactly like the RuerZ pattern. Moreover this function should be in the same workspace as the other RulerZ tools.

Ahead this form, I display the rule.
But some complex rules are difficult to read. How can I colorize it? (highlighting keywords, parameters, opertator…)

Thanks

hoa/ruler dependencie

while installing via composer:
kphoen/rulerz 0.18.0 requires hoa/ruler ~2.0 -> no matching package found.

Cannot Filter Array of Objects on Array of Objects

Hello,

I am trying to filter an array of objects with the Native target. The objects that I am trying to filter contain an array of objects. When I try to filter the array, I get a property access exception:

Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException(code: 0): Cannot read property "name" from an array. Maybe you intended to write the property path as "[name]" instead.

Here is a simplified snippet of my objects:

class Label
{
	public $name = 'something';
}

class User
{
	public $labels;

	public function __construct()
	{
		$this->labels = [
			new Label(),
			new Label(),
			new Label()
		];
	}
}

Here is a snippet of my RulerZ specification:

class UserLabelsNameSpecification implements Specification
{
	public function getRule()
	{
		return 'labels.name = "something"';
	}
}

Here is the filter:

$users = [
	new User(),
	new User(),
	new User()
];

$filtered_users = $rulerz->filterSpec($users, new UserLabelsNameSpecification());

Is RulerZ supposed to support this? Perhaps I'm doing something wrong? I could not find an example of this situation. My example works fine with the DoctrineOrm target.

GenericVisitor::setInlineOperator with a callable?

Hi,

I use RulerZ in a tracker system of the IoT.
I would alias an operator to an inlineOperator for a clearer view: :point ∈ :shape" instead of include(:point, :shape)

    public function initRulerz()
    {
        $ƒinside = function ($geoLocation, $shapeCode) {
            if (null === $geoLocation) {
                return false;
            }

            return $this->geoFencing
                ->setShapeByCode($shapeCode)
                ->isInside(
                    $geoLocation['latitude'],
                    $geoLocation['longitude']
                );
        };

        $ƒoutside = function ($geoLocation, $shapeCode) use ($ƒinside) {
            return !$ƒinside($geoLocation, $shapeCode);
        };


        $visitor = new ArrayVisitor();

        $visitor->setOperator('inside', $ƒinside);
        $visitor->setOperator('outside', $ƒoutside);

        // <-- insert

        $compiler = new FileCompiler(new HoaParser());
        $this->rulerz = new RulerZ($compiler, [ $visitor ]);

        return $this;
    }

I would like to write:

        $visitor->setInlineOperator('∈', $ƒinside);
        $visitor->setInlineOperator('∉', $ƒoutside);

How is it possible?

Find a text into an array

Hello,
Impossible to find text in the table, but with Elastica is OK.

With Elastica :

       $repository = $this->get('fos_elastica.finder.app.user');
       $boolQuery = new \Elastica\Query\BoolQuery();

       $fieldQuery = new \Elastica\Query\Match();
       $fieldQuery->setFieldQuery('list.text', 'foo');
       $boolQuery->addShould($fieldQuery);

       $data = $repository->find($boolQuery);

With RulerZ :

       $rulerZ = $this->get('rulerz');
        $params = ['text' => 'foo'];
        $data = $rulerZ->filter($repository, 'list.text = :text', $params);

With RulerZ, I got no result.

But, with a number is OK:

        $rulerZ = $this->get('rulerz');
        $params = ['number' => 2];
        $data = $rulerZ->filter($repository, 'list.number > :number', $params)

Did I miss something ?

Where could I find the Expr related file and code?

Hi,

I am new to rulerz, found there is a file named using_spec_builder.php in folder \examples, but when I trying to run it, can not find dependency for below code. How could I resolve the problem? Thanks!

$spec = Expr::andX(
Expr::equals('gender', 'F'),
Expr::moreThan('points', 3000)
);

Parse error when using custom operator

Hey,

I am trying to create a custom operator for range (between), but I always end up with the same error message

"Parse error: syntax error, unexpected '\"', expecting identifier (T_STRING) or variable (T_VARIABLE) or number (T_NUM_STRING)"

which occurs in the compiled file /tmp/rulerz_executor_34868ebc.

Looking at the source, it generates

return "call_user_func($operators["range"], @@_ROOT_ALIAS_@@.createdAt, :from)";

which is not a valid code. I managed to fix this by changing the source to

return call_user_func($operators["range"], "@@_ROOT_ALIAS_@@.createdAt", ":from");

after this change my callback gets called (but wrong data is passed in due to eval issues I guess)

This is my initialization

new RulerZ(
    $compiler,
    [
        new DoctrineQueryBuilderVisitor([
            'range' => function($a, $b) {
                die('foo!');
            }
        ]),
        new ArrayVisitor(),
    ]
);

I am not sure if this is a bug or I am doing something wrong.

Any ideas?

Why ExecutionContext is immutable ?

In my FilterTrait i want to inject metadata in ExecutionContext.
Right now i pushing metadata in parameters : $parameters['ting.metadata'] = $repository->getMetadata();

But i think metadata is more context related than parameters related.
So is there any reason to make ExecutionContext immutable ?

[Support]Use rules with joined entities

Hi,

Looks like the docs are only mentioning the usage of simple entities when defining rules.

But can I use more complex rules with joined entities ?
Should I prepare the query builder with joins ?

Will I be able to use the filter to check entity satisfaction ?

Thanks

Filtering with rules containing parameters failes

Playing with your examples, I get a PHP error when using the filter method of the RulerZ engine with rules containing parameters.

Sample code to reproduce the issue:

<?php
require('../vendor/autoload.php');

use RulerZ\Compiler\FileCompiler;
use RulerZ\Parser\HoaParser;
use RulerZ\Compiler\Target;
use RulerZ\RulerZ;

$playersArr = [
    ['pseudo' => 'Joe',   'gender' => 'M', 'points' => 2500],
    ['pseudo' => 'Moe',   'gender' => 'M', 'points' => 1230],
    ['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001],
    ['pseudo' => 'Nora',  'gender' => 'F', 'points' => 3450],
    ['pseudo' => 'Lisa',  'gender' => 'F', 'points' => 9540],
];

$compiler = new FileCompiler(new HoaParser());
$rulerz = new RulerZ(
    $compiler, [
        new Target\ArrayVisitor(),
    ]
);
$dynamicRule = 'gender = :gender';
$dynamicRuleParameters = [
    'min_points' => 30,
    'gender'     => 'F'
];
$filteredPlayers = $rulerz->filter($playersArr, $dynamicRule, $dynamicRuleParameters);

Executing the code above produces the following PHP error:

[18-Sep-2015 16:43:41 Europe/Berlin] PHP Parse error: syntax error, unexpected '-', expecting '{' in C:\Users\brack\AppData\Local\Temp\rulerz_executor_-184339999 on line 6
[18-Sep-2015 16:43:41 Europe/Berlin] PHP Stack trace:
[18-Sep-2015 16:43:41 Europe/Berlin] PHP 1. {main}() D:\GitHub Projects\DesignPatternsPHP\src\rulez.php:0
[18-Sep-2015 16:43:41 Europe/Berlin] PHP 2. RulerZ\RulerZ->filter() D:\GitHub Projects\DesignPatternsPHP\src\rulez.php:62
[18-Sep-2015 16:43:41 Europe/Berlin] PHP 3. RulerZ\Compiler\FileCompiler->compile() D:\GitHub Projects\DesignPatternsPHP\vendor\kphoen\rulerz\src\RulerZ.php:57

Content of the temp file rulerz_executor_-184339999 from stack trace:

<?php
namespace RulerZ\Compiled\Executor;

use RulerZ\Executor\Executor;

class Executor_-184339999 implements Executor
{
    use \RulerZ\Executor\ArrayTarget\FilterTrait;
    use \RulerZ\Executor\ArrayTarget\SatisfiesTrait;

    protected function execute($target, array $operators, array $parameters)
    {
        return $target["gender"] == $parameters["gender"];
    }
}

Add Solr support

Hello!

Is it possible to add Solr support?
Maybe we should split rulerz into rulerz-core and rulerz-driver-solr, rulerz-driver-elasticsearch, rulerz-driver-doctrine & co.?

Thoughts?

Results handling

Right now, the type of the results returned by $rulerz->filter(…) depends on the target. For instance, using a Doctrine Query Builder will return a Collection of objects hydrated by doctrine but using the Elasticsearch PHP client will return the raw Elasticsearch result as an array.

Having different result types depending on the target defeats one of the purpose of this library: the encapsulation of the storage layer.

By default, the return type for all the targets should be the same (the results with no metadata/noise).

The results could implement an interface like the following:

interface FilterResult
{
    public function getIterator();
}

It would for instance allow the executors to lazily return the results (using yield or any other method). If the number of results can easily be computed, both the FilterResult and the Countable interfaces can be implemented.

There can be cases where the user needs more control on the returned results (pagination for instance). RulerZ should provide a way to update a QueryBuilder (when Doctrine is used) and return it as-is, leaving the user in charge of finishing and executing the request.
This could be done using an option in the execution context: ['result_type' => 'raw_query'], or by exposing a new method in RulerZ.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.