From b0f4a5d576b54fb75a120123f1a52749832ec5ef Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 14 Apr 2025 01:40:16 +0100 Subject: [PATCH 1/2] refactor: allow node mapping in variadic functions to have different patterns, thus opening the path to a combination of node types (compared to the previous single type support) --- .../Doctrine/ORM/Query/AST/Functions/Arr.php | 5 ++- .../ORM/Query/AST/Functions/ArrayToJson.php | 5 +++ .../AST/Functions/BaseComparisonFunction.php | 5 ++- .../AST/Functions/BaseVariadicFunction.php | 31 +++++++++++++++---- .../ORM/Query/AST/Functions/DateAdd.php | 5 +++ .../ORM/Query/AST/Functions/DateSubtract.php | 5 +++ .../Query/AST/Functions/JsonBuildObject.php | 5 +++ .../Query/AST/Functions/JsonbBuildObject.php | 5 +++ .../ORM/Query/AST/Functions/JsonbInsert.php | 5 +++ .../Query/AST/Functions/JsonbPathMatch.php | 5 +++ .../Query/AST/Functions/JsonbPathQuery.php | 5 +++ .../AST/Functions/JsonbPathQueryArray.php | 5 +++ .../AST/Functions/JsonbPathQueryFirst.php | 5 +++ .../ORM/Query/AST/Functions/JsonbSet.php | 5 +++ .../Doctrine/ORM/Query/AST/Functions/Row.php | 5 ++- .../ORM/Query/AST/Functions/RowToJson.php | 5 +++ .../ORM/Query/AST/Functions/ToTsquery.php | 5 +++ .../ORM/Query/AST/Functions/ToTsvector.php | 5 ++- .../ORM/Query/AST/Functions/Unaccent.php | 5 ++- .../BaseComparisonFunctionTestCase.php | 4 +-- 20 files changed, 112 insertions(+), 13 deletions(-) diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php index f8c84885..533cd141 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Arr.php @@ -17,7 +17,10 @@ */ class Arr extends BaseVariadicFunction { - protected string $commonNodeMapping = 'StringPrimary'; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } protected function customizeFunction(): void { diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php index 4f20134e..05c16deb 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayToJson.php @@ -26,6 +26,11 @@ class ArrayToJson extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('array_to_json(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php index e745948b..225ae271 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunction.php @@ -11,5 +11,8 @@ */ abstract class BaseComparisonFunction extends BaseVariadicFunction { - protected string $commonNodeMapping = 'SimpleArithmeticExpression'; + protected function getNodeMappingPattern(): array + { + return ['SimpleArithmeticExpression']; + } } diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php index 679c413a..6df0345c 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php @@ -19,18 +19,37 @@ */ abstract class BaseVariadicFunction extends BaseFunction { - protected string $commonNodeMapping = 'StringPrimary'; + /** + * @return array + */ + abstract protected function getNodeMappingPattern(): array; /** * @throws ParserException */ protected function feedParserWithNodes(Parser $parser): void { + foreach ($this->getNodeMappingPattern() as $nodeMappingPattern) { + try { + $this->feedParserWithNodesForNodeMappingPattern($parser, $nodeMappingPattern); + + break; + } catch (ParserException) { + // swallow and continue with next pattern + } + } + + $this->validateArguments(...$this->nodes); // @phpstan-ignore-line + } + + private function feedParserWithNodesForNodeMappingPattern(Parser $parser, string $nodeMappingPattern): void + { + $nodeMapping = \explode(',', $nodeMappingPattern); $lexer = $parser->getLexer(); try { // @phpstan-ignore-next-line - $this->nodes[] = $parser->{$this->commonNodeMapping}(); + $this->nodes[] = $parser->{$nodeMapping[0]}(); $lookaheadType = DoctrineLexer::getLookaheadType($lexer); if ($lookaheadType === null) { throw ParserException::missingLookaheadType(); @@ -40,18 +59,18 @@ protected function feedParserWithNodes(Parser $parser): void } $shouldUseLexer = DoctrineOrm::isPre219(); - + $isNodeMappingASimplePattern = \count($nodeMapping) === 1; + $nodeIndex = 1; while (($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS) !== $lookaheadType) { if (($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA) === $lookaheadType) { $parser->match($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA); // @phpstan-ignore-next-line - $this->nodes[] = $parser->{$this->commonNodeMapping}(); + $this->nodes[] = $parser->{$nodeMapping[$isNodeMappingASimplePattern ? 0 : $nodeIndex]}(); + $nodeIndex++; } $lookaheadType = DoctrineLexer::getLookaheadType($lexer); } - - $this->validateArguments(...$this->nodes); // @phpstan-ignore-line } public function getSql(SqlWalker $sqlWalker): string diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAdd.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAdd.php index 55d8b111..72904239 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAdd.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateAdd.php @@ -25,6 +25,11 @@ class DateAdd extends BaseVariadicFunction { use TimezoneValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('date_add(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtract.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtract.php index 31d0f0e3..8a5c917d 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtract.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DateSubtract.php @@ -25,6 +25,11 @@ class DateSubtract extends BaseVariadicFunction { use TimezoneValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('date_subtract(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObject.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObject.php index 8daa091b..b89e9a60 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObject.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonBuildObject.php @@ -17,6 +17,11 @@ */ class JsonBuildObject extends BaseVariadicFunction { + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('json_build_object(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObject.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObject.php index bebba4d8..8ca5192c 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObject.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbBuildObject.php @@ -17,6 +17,11 @@ */ class JsonbBuildObject extends BaseVariadicFunction { + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('jsonb_build_object(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsert.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsert.php index 855ebb09..44e70e11 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsert.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbInsert.php @@ -26,6 +26,11 @@ class JsonbInsert extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('jsonb_insert(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php index b5e052c7..dec46af7 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php @@ -26,6 +26,11 @@ class JsonbPathMatch extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('jsonb_path_match(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php index a88008c9..c3535842 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php @@ -25,6 +25,11 @@ class JsonbPathQuery extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('jsonb_path_query(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php index cb863046..9bd2ad64 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php @@ -24,6 +24,11 @@ class JsonbPathQueryArray extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('jsonb_path_query_array(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php index 8c0d3bc2..ad3af1d2 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php @@ -25,6 +25,11 @@ class JsonbPathQueryFirst extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('jsonb_path_query_first(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php index 2626ccec..bb499506 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbSet.php @@ -30,6 +30,11 @@ class JsonbSet extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('jsonb_set(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php index 2c959617..e04dcdd2 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Row.php @@ -14,7 +14,10 @@ */ class Row extends BaseVariadicFunction { - protected string $commonNodeMapping = 'InParameter'; + protected function getNodeMappingPattern(): array + { + return ['InParameter']; + } protected function customizeFunction(): void { diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php index a6488259..83213fb7 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/RowToJson.php @@ -25,6 +25,11 @@ class RowToJson extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('row_to_json(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php index 653f1b54..265b844d 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsquery.php @@ -17,6 +17,11 @@ */ class ToTsquery extends BaseVariadicFunction { + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('to_tsquery(%s)'); diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php index e5b1ef71..444436a0 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ToTsvector.php @@ -17,7 +17,10 @@ */ class ToTsvector extends BaseVariadicFunction { - protected string $commonNodeMapping = 'StringExpression'; + protected function getNodeMappingPattern(): array + { + return ['StringExpression']; + } protected function customizeFunction(): void { diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php index 8b4aea19..3d92433f 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Unaccent.php @@ -16,7 +16,10 @@ */ class Unaccent extends BaseVariadicFunction { - protected string $commonNodeMapping = 'StringPrimary'; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } protected function customizeFunction(): void { diff --git a/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunctionTestCase.php b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunctionTestCase.php index 64fe3e74..ad1974d7 100644 --- a/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunctionTestCase.php +++ b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseComparisonFunctionTestCase.php @@ -9,7 +9,7 @@ use Doctrine\ORM\Query; use Doctrine\ORM\Query\Parser; use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\BaseComparisonFunction; -use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Exception\ParserException; +use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Exception\InvalidArgumentForVariadicFunctionException; abstract class BaseComparisonFunctionTestCase extends TestCase { @@ -20,7 +20,7 @@ abstract protected function createFixture(): BaseComparisonFunction; */ public function throws_an_exception_when_lexer_is_not_populated_with_a_lookahead_type(): void { - $this->expectException(ParserException::class); + $this->expectException(InvalidArgumentForVariadicFunctionException::class); $em = $this->createMock(EntityManager::class); $em->expects($this->any()) From 70525771d0afcea8f34d17e4aebe1488706da1ca Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Mon, 14 Apr 2025 01:40:37 +0100 Subject: [PATCH 2/2] no message --- .../Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php index 5d6a199d..cd2a5230 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php @@ -24,6 +24,11 @@ class JsonbPathExists extends BaseVariadicFunction { use BooleanValidationTrait; + protected function getNodeMappingPattern(): array + { + return ['StringPrimary']; + } + protected function customizeFunction(): void { $this->setFunctionPrototype('jsonb_path_exists(%s)');