Skip to content

Commit

Permalink
Merge pull request #420 from nextras/condition_parser
Browse files Browse the repository at this point in the history
Condition parser
  • Loading branch information
hrach committed May 24, 2020
2 parents aa9141a + 4acdeb9 commit af9a280
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 62 deletions.
14 changes: 12 additions & 2 deletions src/Collection/Functions/ConjunctionOperatorFunction.php
Expand Up @@ -5,14 +5,24 @@

use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ConditionParserHelper;
use Nextras\Orm\Collection\Helpers\ConditionParser;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Entity\IEntity;


class ConjunctionOperatorFunction implements IArrayFunction, IQueryBuilderFunction
{
/** @var ConditionParser */
protected $conditionParser;


public function __construct(ConditionParser $conditionParserHelper)
{
$this->conditionParser = $conditionParserHelper;
}


public function processArrayExpression(ArrayCollectionHelper $helper, IEntity $entity, array $args)
{
foreach ($this->normalizeFunctions($args) as $arg) {
Expand Down Expand Up @@ -61,7 +71,7 @@ protected function normalizeFunctions(array $args): array
/** @phpstan-var array<string, mixed> $args */
$processedArgs = [];
foreach ($args as $argName => $argValue) {
$functionCall = ConditionParserHelper::parsePropertyOperator($argName);
$functionCall = $this->conditionParser->parsePropertyOperator($argName);
$functionCall[] = $argValue;
$processedArgs[] = $functionCall;
}
Expand Down
14 changes: 12 additions & 2 deletions src/Collection/Functions/DisjunctionOperatorFunction.php
Expand Up @@ -5,14 +5,24 @@

use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ConditionParserHelper;
use Nextras\Orm\Collection\Helpers\ConditionParser;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Entity\IEntity;


class DisjunctionOperatorFunction implements IArrayFunction, IQueryBuilderFunction
{
/** @var ConditionParser */
private $conditionParser;


public function __construct(ConditionParser $conditionParserHelper)
{
$this->conditionParser = $conditionParserHelper;
}


public function processArrayExpression(ArrayCollectionHelper $helper, IEntity $entity, array $args)
{
foreach ($this->normalizeFunctions($args) as $arg) {
Expand Down Expand Up @@ -61,7 +71,7 @@ protected function normalizeFunctions(array $args): array
/** @phpstan-var array<string, mixed> $args */
$processedArgs = [];
foreach ($args as $argName => $argValue) {
$functionCall = ConditionParserHelper::parsePropertyOperator($argName);
$functionCall = $this->conditionParser->parsePropertyOperator($argName);
$functionCall[] = $argValue;
$processedArgs[] = $functionCall;
}
Expand Down
6 changes: 4 additions & 2 deletions src/Collection/Helpers/ArrayCollectionHelper.php
Expand Up @@ -66,6 +66,8 @@ public function createFilter(array $expr): Closure
*/
public function createSorter(array $expressions): Closure
{
$conditionParser = $this->repository->getConditionParser();

$parsedExpressions = [];
foreach ($expressions as $expression) {
if (is_array($expression[0])) {
Expand All @@ -79,7 +81,7 @@ public function createSorter(array $expressions): Closure
}
$parsedExpressions[] = [$collectionFunction, $expression[1], $expression[0]];
} else {
[$column, $sourceEntity] = ConditionParserHelper::parsePropertyExpr($expression[0]);
[$column, $sourceEntity] = $conditionParser->parsePropertyExpr($expression[0]);
$sourceEntityMeta = $this->repository->getEntityMetadata($sourceEntity);
$parsedExpressions[] = [$column, $expression[1], $sourceEntityMeta];
}
Expand Down Expand Up @@ -136,7 +138,7 @@ public function getValue(IEntity $entity, $expr): ArrayPropertyValueReference
return new ArrayPropertyValueReference($value, false, null);
}

[$tokens, $sourceEntityClassName] = ConditionParserHelper::parsePropertyExpr($expr);
[$tokens, $sourceEntityClassName] = $this->repository->getConditionParser()->parsePropertyExpr($expr);
$sourceEntityMeta = $this->repository->getEntityMetadata($sourceEntityClassName);
return $this->getValueByTokens($entity, $tokens, $sourceEntityMeta);
}
Expand Down
Expand Up @@ -21,31 +21,42 @@
use function trigger_error;


class ConditionParserHelper
/**
* @internal
*/
class ConditionParser
{
// language=PhpRegExp
protected const PATH_REGEXP = '(?:([\w\\\]+)::)?([\w\\\]++(?:->\w++)*+)';


/**
* @return array{class-string, string}
*/
public static function parsePropertyOperator(string $condition): array
public function parsePropertyOperator(string $condition): array
{
if (!preg_match('#^(.+?)(!=|<=|>=|=|>|<|~)?$#', $condition, $matches)) {
// language=PhpRegExp
$regexp = '#^(?P<path>' . self::PATH_REGEXP . ')(?P<operator>!=|<=|>=|=|>|<|~)?$#';
if (!preg_match($regexp, $condition, $matches)) {
return [CompareEqualsFunction::class, $condition];
}
$operator = $matches[2] ?? '=';
$operator = $matches['operator'] ?? '=';
$path = $matches['path'];

if ($operator === '=') {
return [CompareEqualsFunction::class, $matches[1]];
return [CompareEqualsFunction::class, $path];
} elseif ($operator === '!=') {
return [CompareNotEqualsFunction::class, $matches[1]];
return [CompareNotEqualsFunction::class, $path];
} elseif ($operator === '>=') {
return [CompareGreaterThanEqualsFunction::class, $matches[1]];
return [CompareGreaterThanEqualsFunction::class, $path];
} elseif ($operator === '>') {
return [CompareGreaterThanFunction::class, $matches[1]];
return [CompareGreaterThanFunction::class, $path];
} elseif ($operator === '<=') {
return [CompareSmallerThanEqualsFunction::class, $matches[1]];
return [CompareSmallerThanEqualsFunction::class, $path];
} elseif ($operator === '<') {
return [CompareSmallerThanFunction::class, $matches[1]];
return [CompareSmallerThanFunction::class, $path];
} elseif ($operator === '~') {
return [CompareLikeFunction::class, $matches[1]];
return [CompareLikeFunction::class, $path];
} else {
throw new InvalidStateException();
}
Expand All @@ -55,15 +66,10 @@ public static function parsePropertyOperator(string $condition): array
/**
* @return array{list<string>, class-string<IEntity>|null}
*/
public static function parsePropertyExpr(string $propertyPath): array
public function parsePropertyExpr(string $propertyPath): array
{
static $regexp = '#
^
(?:([\w\\\]+)::)?
([\w\\\]++(?:->\w++)*+)
$
#x';

// language=PhpRegExp
$regexp = '#^' . self::PATH_REGEXP . '$#';
if (!preg_match($regexp, $propertyPath, $matches)) {
throw new InvalidArgumentException('Unsupported condition format.');
}
Expand Down
2 changes: 1 addition & 1 deletion src/Collection/Helpers/DbalQueryBuilderHelper.php
Expand Up @@ -87,7 +87,7 @@ public function processPropertyExpr(QueryBuilder $builder, $expr): DbalExpressio
return $collectionFunction->processQueryBuilderExpression($this, $builder, $expr);
}

[$tokens, $sourceEntity] = ConditionParserHelper::parsePropertyExpr($expr);
[$tokens, $sourceEntity] = $this->repository->getConditionParser()->parsePropertyExpr($expr);
return $this->processTokens($tokens, $sourceEntity, $builder);
}

Expand Down
19 changes: 13 additions & 6 deletions src/Collection/Helpers/FetchPairsHelper.php
Expand Up @@ -29,23 +29,30 @@ public static function process(Traversable $collection, ?string $key, ?string $v
throw new InvalidArgumentException('FetchPairsHelper requires defined key or value.');
}

$row = $rows[0] ?? null;
if ($row === null) {
return [];
}
assert($row instanceof IEntity);
$conditionParser = $row->getRepository()->getConditionParser();

if ($key === null) {
assert($value !== null);
$valueChain = self::parseExpr($value);
$valueChain = self::parseExpr($conditionParser, $value);
foreach ($rows as $row) {
$return[] = self::getProperty($row, $valueChain);
}

} elseif ($value === null) {
$valueChain = self::parseExpr($key);
$valueChain = self::parseExpr($conditionParser, $key);
foreach ($rows as $row) {
$keyResult = self::getProperty($row, $valueChain);
$return[($keyResult instanceof DateTimeImmutable) ? (string) $keyResult : $keyResult] = $row;
}

} else {
$keyChain = self::parseExpr($key);
$valueChain = self::parseExpr($value);
$keyChain = self::parseExpr($conditionParser, $key);
$valueChain = self::parseExpr($conditionParser, $value);
foreach ($rows as $row) {
$keyResult = self::getProperty($row, $keyChain);
$valueResult = self::getProperty($row, $valueChain);
Expand All @@ -60,9 +67,9 @@ public static function process(Traversable $collection, ?string $key, ?string $v
/**
* @phpstan-return list<string>
*/
private static function parseExpr(string $expr): array
private static function parseExpr(ConditionParser $conditionParser, string $expr): array
{
[$chain] = ConditionParserHelper::parsePropertyExpr($expr);
[$chain] = $conditionParser->parsePropertyExpr($expr);
return $chain;
}

Expand Down
7 changes: 7 additions & 0 deletions src/Repository/IRepository.php
Expand Up @@ -12,6 +12,7 @@

use Nextras\Orm\Collection\Functions\IArrayFunction;
use Nextras\Orm\Collection\Functions\IQueryBuilderFunction;
use Nextras\Orm\Collection\Helpers\ConditionParser;
use Nextras\Orm\Collection\ICollection;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\Reflection\EntityMetadata;
Expand Down Expand Up @@ -165,6 +166,12 @@ public function findById($ids): ICollection;
public function getCollectionFunction(string $name);


/**
* @internal
*/
public function getConditionParser(): ConditionParser;


public function persist(IEntity $entity, bool $withCascade = true): IEntity;


Expand Down
24 changes: 22 additions & 2 deletions src/Repository/Repository.php
Expand Up @@ -26,6 +26,7 @@
use Nextras\Orm\Collection\Functions\MaxAggregateFunction;
use Nextras\Orm\Collection\Functions\MinAggregateFunction;
use Nextras\Orm\Collection\Functions\SumAggregateFunction;
use Nextras\Orm\Collection\Helpers\ConditionParser;
use Nextras\Orm\Collection\ICollection;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\Reflection\EntityMetadata;
Expand Down Expand Up @@ -132,6 +133,11 @@ abstract class Repository implements IRepository
*/
private $collectionFunctions = [];

/**
* @var ConditionParser|null
*/
private $conditionParser;


/**
* @param IMapper $mapper
Expand Down Expand Up @@ -282,15 +288,19 @@ protected function createCollectionFunction(string $name)
CompareSmallerThanEqualsFunction::class => true,
CompareSmallerThanFunction::class => true,
CompareLikeFunction::class => true,
ConjunctionOperatorFunction::class => true,
DisjunctionOperatorFunction::class => true,
AvgAggregateFunction::class => true,
CountAggregateFunction::class => true,
MaxAggregateFunction::class => true,
MinAggregateFunction::class => true,
SumAggregateFunction::class => true,
];

if ($name === ConjunctionOperatorFunction::class) {
return new ConjunctionOperatorFunction($this->getConditionParser());
} elseif ($name === DisjunctionOperatorFunction::class) {
return new DisjunctionOperatorFunction($this->getConditionParser());
}

if (isset($knownFunctions[$name])) {
return new $name();
} else {
Expand Down Expand Up @@ -348,6 +358,16 @@ public function getEntityClassName(array $data): string
}


/** {@inheritdoc} */
public function getConditionParser(): ConditionParser
{
if ($this->conditionParser === null) {
$this->conditionParser = new ConditionParser();
}
return $this->conditionParser;
}


/** {@inheritdoc} */
public function persist(IEntity $entity, bool $withCascade = true): IEntity
{
Expand Down

0 comments on commit af9a280

Please sign in to comment.