From f0f3c9fc18c0ac4975c2f75ab60a2c24f00d7975 Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Thu, 13 Mar 2025 12:38:51 +0000 Subject: [PATCH 1/3] feat: add support for `json_exists`, `json_query`, `json_scalar`, `json_serialize` and `json_value` --- .../ORM/Query/AST/Functions/JsonExists.php | 23 ++++++++++ .../ORM/Query/AST/Functions/JsonQuery.php | 23 ++++++++++ .../ORM/Query/AST/Functions/JsonScalar.php | 22 ++++++++++ .../ORM/Query/AST/Functions/JsonSerialize.php | 33 +++++++++++++++ .../ORM/Query/AST/Functions/JsonValue.php | 23 ++++++++++ .../Query/AST/Functions/JsonExistsTest.php | 39 +++++++++++++++++ .../ORM/Query/AST/Functions/JsonQueryTest.php | 42 +++++++++++++++++++ .../Query/AST/Functions/JsonScalarTest.php | 32 ++++++++++++++ .../Query/AST/Functions/JsonSerializeTest.php | 39 +++++++++++++++++ .../ORM/Query/AST/Functions/JsonValueTest.php | 36 ++++++++++++++++ 10 files changed, 312 insertions(+) create mode 100644 src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php create mode 100644 src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php create mode 100644 src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php create mode 100644 src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php create mode 100644 src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php create mode 100644 tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExistsTest.php create mode 100644 tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQueryTest.php create mode 100644 tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalarTest.php create mode 100644 tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerializeTest.php create mode 100644 tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValueTest.php diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php new file mode 100644 index 00000000..e9331483 --- /dev/null +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php @@ -0,0 +1,23 @@ + + */ +class JsonExists extends BaseFunction +{ + protected function customizeFunction(): void + { + $this->setFunctionPrototype('json_exists(%s, %s)'); + $this->addNodeMapping('StringPrimary'); + $this->addNodeMapping('StringPrimary'); + } +} diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php new file mode 100644 index 00000000..f4fdc4ac --- /dev/null +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php @@ -0,0 +1,23 @@ + + */ +class JsonQuery extends BaseFunction +{ + protected function customizeFunction(): void + { + $this->setFunctionPrototype('json_query(%s, %s)'); + $this->addNodeMapping('StringPrimary'); + $this->addNodeMapping('StringPrimary'); + } +} diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php new file mode 100644 index 00000000..efe8c30e --- /dev/null +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php @@ -0,0 +1,22 @@ + + */ +class JsonScalar extends BaseFunction +{ + protected function customizeFunction(): void + { + $this->setFunctionPrototype('json_scalar(%s)'); + $this->addNodeMapping('StringPrimary'); + } +} diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php new file mode 100644 index 00000000..a1e5f415 --- /dev/null +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php @@ -0,0 +1,33 @@ + + */ +class JsonSerialize extends BaseVariadicFunction +{ + protected function customizeFunction(): void + { + $this->setFunctionPrototype('json_serialize(%s)'); + } + + protected function validateArguments(array $arguments): void + { + if (\count($arguments) !== 1) { + throw InvalidArgumentForVariadicFunctionException::exact('json_serialize', 1); + } + } +} diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php new file mode 100644 index 00000000..270995cd --- /dev/null +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php @@ -0,0 +1,23 @@ + + */ +class JsonValue extends BaseFunction +{ + protected function customizeFunction(): void + { + $this->setFunctionPrototype('json_value(%s, %s)'); + $this->addNodeMapping('StringPrimary'); + $this->addNodeMapping('StringPrimary'); + } +} diff --git a/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExistsTest.php b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExistsTest.php new file mode 100644 index 00000000..2467d59e --- /dev/null +++ b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExistsTest.php @@ -0,0 +1,39 @@ + JsonExists::class, + ]; + } + + protected function getExpectedSqlStatements(): array + { + return [ + // Basic usage + "SELECT json_exists(c0_.object1, '$.name') AS sclr_0 FROM ContainsJsons c0_", + // Nested path + "SELECT json_exists(c0_.object1, '$.address.city') AS sclr_0 FROM ContainsJsons c0_", + // Array element + "SELECT json_exists(c0_.object1, '$.items[0]') AS sclr_0 FROM ContainsJsons c0_", + ]; + } + + protected function getDqlStatements(): array + { + return [ + \sprintf("SELECT JSON_EXISTS(e.object1, '$.name') FROM %s e", ContainsJsons::class), + \sprintf("SELECT JSON_EXISTS(e.object1, '$.address.city') FROM %s e", ContainsJsons::class), + \sprintf("SELECT JSON_EXISTS(e.object1, '$.items[0]') FROM %s e", ContainsJsons::class), + ]; + } +} diff --git a/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQueryTest.php b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQueryTest.php new file mode 100644 index 00000000..5dc81ee4 --- /dev/null +++ b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQueryTest.php @@ -0,0 +1,42 @@ + JsonQuery::class, + ]; + } + + protected function getExpectedSqlStatements(): array + { + return [ + "SELECT json_query(c0_.object1, '$.items[*]') AS sclr_0 FROM ContainsJsons c0_", + "SELECT json_query(c0_.object1, '$.address') AS sclr_0 FROM ContainsJsons c0_", + // Additional test cases for important scenarios + "SELECT json_query(c0_.object1, '$.store.book[*].author') AS sclr_0 FROM ContainsJsons c0_", + "SELECT json_query(c0_.object1, '$.store.book[0 to 2]') AS sclr_0 FROM ContainsJsons c0_", + "SELECT json_query(c0_.object1, '$.store.book[*]?(@.price > 10)') AS sclr_0 FROM ContainsJsons c0_", + ]; + } + + protected function getDqlStatements(): array + { + return [ + \sprintf("SELECT JSON_QUERY(e.object1, '$.items[*]') FROM %s e", ContainsJsons::class), + \sprintf("SELECT JSON_QUERY(e.object1, '$.address') FROM %s e", ContainsJsons::class), + // Additional test cases matching the SQL statements above + \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*].author') FROM %s e", ContainsJsons::class), + \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[0 to 2]') FROM %s e", ContainsJsons::class), + \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*]?(@.price > 10)') FROM %s e", ContainsJsons::class), + ]; + } +} diff --git a/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalarTest.php b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalarTest.php new file mode 100644 index 00000000..b58f8792 --- /dev/null +++ b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalarTest.php @@ -0,0 +1,32 @@ + JsonScalar::class, + ]; + } + + protected function getExpectedSqlStatements(): array + { + return [ + 'SELECT json_scalar(c0_.object1) AS sclr_0 FROM ContainsJsons c0_', + ]; + } + + protected function getDqlStatements(): array + { + return [ + \sprintf('SELECT JSON_SCALAR(e.object1) FROM %s e', ContainsJsons::class), + ]; + } +} diff --git a/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerializeTest.php b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerializeTest.php new file mode 100644 index 00000000..44c99748 --- /dev/null +++ b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerializeTest.php @@ -0,0 +1,39 @@ + JsonSerialize::class, + ]; + } + + protected function getExpectedSqlStatements(): array + { + return [ + // Basic serialization + 'SELECT json_serialize(c0_.object1) AS sclr_0 FROM ContainsJsons c0_', + // With expression + 'SELECT json_serialize(UPPER(c0_.object1)) AS sclr_0 FROM ContainsJsons c0_', + // With literal + "SELECT json_serialize('{\"key\": \"value\"}') AS sclr_0 FROM ContainsJsons c0_", + ]; + } + + protected function getDqlStatements(): array + { + return [ + \sprintf('SELECT JSON_SERIALIZE(e.object1) FROM %s e', ContainsJsons::class), + \sprintf('SELECT JSON_SERIALIZE(UPPER(e.object1)) FROM %s e', ContainsJsons::class), + \sprintf("SELECT JSON_SERIALIZE('{\"key\": \"value\"}') FROM %s e", ContainsJsons::class), + ]; + } +} diff --git a/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValueTest.php b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValueTest.php new file mode 100644 index 00000000..1a809a89 --- /dev/null +++ b/tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValueTest.php @@ -0,0 +1,36 @@ + JsonValue::class, + ]; + } + + protected function getExpectedSqlStatements(): array + { + return [ + "SELECT json_value(c0_.object1, '$.name') AS sclr_0 FROM ContainsJsons c0_", + "SELECT json_value(c0_.object1, '$.address.city') AS sclr_0 FROM ContainsJsons c0_", + "SELECT json_value(c0_.object1, '$.items[0]') AS sclr_0 FROM ContainsJsons c0_", + ]; + } + + protected function getDqlStatements(): array + { + return [ + \sprintf("SELECT JSON_VALUE(e.object1, '$.name') FROM %s e", ContainsJsons::class), + \sprintf("SELECT JSON_VALUE(e.object1, '$.address.city') FROM %s e", ContainsJsons::class), + \sprintf("SELECT JSON_VALUE(e.object1, '$.items[0]') FROM %s e", ContainsJsons::class), + ]; + } +} From 831e7d6c12c59ded93da659fbba8f50f60ce83f5 Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Thu, 13 Mar 2025 14:41:12 +0000 Subject: [PATCH 2/3] feat: add support for `json_exists`, `json_query`, `json_scalar`, `json_serialize` and `json_value` --- docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md | 5 +++++ docs/INTEGRATING-WITH-DOCTRINE.md | 21 +++++++++++++++++-- docs/INTEGRATING-WITH-LARAVEL.md | 5 +++++ docs/INTEGRATING-WITH-SYMFONY.md | 7 ++++++- .../ORM/Query/AST/Functions/JsonSerialize.php | 12 ++--------- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md b/docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md index 2d638bc3..946d25ac 100644 --- a/docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md +++ b/docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md @@ -53,10 +53,15 @@ | json_build_object | JSON_BUILD_OBJECT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonBuildObject` | | json_each | JSON_EACH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEach` | | json_each_text | JSON_EACH_TEXT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEachText` | +| json_exists | JSON_EXISTS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonExists` | | json_object_agg | JSON_OBJECT_AGG | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectAgg` | | json_object_keys | JSON_OBJECT_KEYS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectKeys` | +| json_query | JSON_QUERY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonQuery` | +| json_scalar | JSON_SCALAR | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonScalar` | +| json_serialize | JSON_SERIALIZE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonSerialize` | | json_strip_nulls | JSON_STRIP_NULLS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonStripNulls` | | json_typeof | JSON_TYPEOF | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonTypeof` | +| json_value | JSON_VALUE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonValue` | | jsonb_array_elements | JSONB_ARRAY_ELEMENTS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbArrayElements` | | jsonb_agg | JSONB_AGG | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbAgg` | | jsonb_array_elements_text | JSONB_ARRAY_ELEMENTS_TEXT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbArrayElementsText` | diff --git a/docs/INTEGRATING-WITH-DOCTRINE.md b/docs/INTEGRATING-WITH-DOCTRINE.md index f27ee4da..d9a6611a 100644 --- a/docs/INTEGRATING-WITH-DOCTRINE.md +++ b/docs/INTEGRATING-WITH-DOCTRINE.md @@ -33,9 +33,26 @@ use Doctrine\ORM\Configuration; $configuration = new Configuration(); # Register json functions +$configuration->addCustomStringFunction('JSON_AGG', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonAgg::class); +$configuration->addCustomStringFunction('JSON_ARRAY_LENGTH', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonArrayLength::class); $configuration->addCustomStringFunction('JSON_BUILD_OBJECT', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonBuildObject::class); -$configuration->addCustomStringFunction('JSONB_BUILD_OBJECT', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbBuildObject::class); - +$configuration->addCustomStringFunction('JSON_EACH', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEach::class); +$configuration->addCustomStringFunction('JSON_EACH_TEXT', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEachText::class); +$configuration->addCustomStringFunction('JSON_EXISTS', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonExists::class); +$configuration->addCustomStringFunction('JSON_GET_FIELD', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetField::class); +$configuration->addCustomStringFunction('JSON_GET_FIELD_AS_TEXT', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetFieldAsText::class); +$configuration->addCustomStringFunction('JSON_GET_FIELD_AS_INTEGER', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetFieldAsInteger::class); +$configuration->addCustomStringFunction('JSON_GET_OBJECT', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetObject::class); +$configuration->addCustomStringFunction('JSON_GET_OBJECT_AS_TEXT', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetObjectAsText::class); +$configuration->addCustomStringFunction('JSON_OBJECT_AGG', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectAgg::class); +$configuration->addCustomStringFunction('JSON_OBJECT_KEYS', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectKeys::class); +$configuration->addCustomStringFunction('JSON_QUERY', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonQuery::class); +$configuration->addCustomStringFunction('JSON_SCALAR', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonScalar::class); +$configuration->addCustomStringFunction('JSON_SERIALIZE', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonSerialize::class); +$configuration->addCustomStringFunction('JSON_STRIP_NULLS', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonStripNulls::class); +$configuration->addCustomStringFunction('JSON_VALUE', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonValue::class); +$configuration->addCustomStringFunction('TO_JSON', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ToJson::class); +$configuration->addCustomStringFunction('ROW_TO_JSON', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\RowToJson::class); # Register text search functions $configuration->addCustomStringFunction('TO_TSQUERY', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ToTsquery::class); $configuration->addCustomStringFunction('TO_TSVECTOR', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ToTsvector::class); diff --git a/docs/INTEGRATING-WITH-LARAVEL.md b/docs/INTEGRATING-WITH-LARAVEL.md index 7664fdb5..e8192c78 100644 --- a/docs/INTEGRATING-WITH-LARAVEL.md +++ b/docs/INTEGRATING-WITH-LARAVEL.md @@ -111,6 +111,7 @@ return [ 'JSON_BUILD_OBJECT' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonBuildObject::class, 'JSON_EACH' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEach::class, 'JSON_EACH_TEXT' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEachText::class, + 'JSON_EXISTS' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonExists::class, 'JSON_GET_FIELD' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetField::class, 'JSON_GET_FIELD_AS_TEXT' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetFieldAsText::class, 'JSON_GET_FIELD_AS_INTEGER' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetFieldAsInteger::class, @@ -118,7 +119,11 @@ return [ 'JSON_GET_OBJECT_AS_TEXT' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetObjectAsText::class, 'JSON_OBJECT_AGG' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectAgg::class, 'JSON_OBJECT_KEYS' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectKeys::class, + 'JSON_QUERY' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonQuery::class, + 'JSON_SCALAR' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonScalar::class, + 'JSON_SERIALIZE' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonSerialize::class, 'JSON_STRIP_NULLS' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonStripNulls::class, + 'JSON_VALUE' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonValue::class, 'TO_JSON' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ToJson::class, 'ROW_TO_JSON' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\RowToJson::class, diff --git a/docs/INTEGRATING-WITH-SYMFONY.md b/docs/INTEGRATING-WITH-SYMFONY.md index 26c528c4..20016ce3 100644 --- a/docs/INTEGRATING-WITH-SYMFONY.md +++ b/docs/INTEGRATING-WITH-SYMFONY.md @@ -104,6 +104,7 @@ doctrine: JSON_BUILD_OBJECT: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonBuildObject JSON_EACH: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEach JSON_EACH_TEXT: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEachText + JSON_EXISTS: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonExists JSON_GET_FIELD: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetField JSON_GET_FIELD_AS_INTEGER: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetFieldAsInteger JSON_GET_FIELD_AS_TEXT: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetFieldAsText @@ -111,10 +112,14 @@ doctrine: JSON_GET_OBJECT_AS_TEXT: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetObjectAsText JSON_OBJECT_AGG: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectAgg JSON_OBJECT_KEYS: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectKeys + JSON_QUERY: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonQuery + JSON_SCALAR: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonScalar + JSON_SERIALIZE: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonSerialize JSON_STRIP_NULLS: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonStripNulls + JSON_TYPEOF: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonTypeof + JSON_VALUE: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonValue TO_JSON: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ToJson ROW_TO_JSON: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\RowToJson - JSON_TYPEOF: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonTypeof # jsonb specific functions JSONB_AGG: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbAgg diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php index a1e5f415..a447e3f7 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php @@ -4,8 +4,6 @@ namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions; -use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Exception\InvalidArgumentForVariadicFunctionException; - /** * Implementation of PostgreSQL JSON_SERIALIZE(). * @@ -17,17 +15,11 @@ * * @author Martin Georgiev */ -class JsonSerialize extends BaseVariadicFunction +class JsonSerialize extends BaseFunction { protected function customizeFunction(): void { $this->setFunctionPrototype('json_serialize(%s)'); - } - - protected function validateArguments(array $arguments): void - { - if (\count($arguments) !== 1) { - throw InvalidArgumentForVariadicFunctionException::exact('json_serialize', 1); - } + $this->addNodeMapping('StringPrimary'); } } From cd6cc4fb6b3651b3e7ebed6868c215da25d13464 Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Thu, 13 Mar 2025 14:47:52 +0000 Subject: [PATCH 3/3] improve documentation for the implemented support and the limitations imposed --- .../ORM/Query/AST/Functions/JsonExists.php | 9 +++++++++ .../Doctrine/ORM/Query/AST/Functions/JsonQuery.php | 14 ++++++++++++++ .../ORM/Query/AST/Functions/JsonScalar.php | 8 ++++++++ .../ORM/Query/AST/Functions/JsonSerialize.php | 5 +++++ .../Doctrine/ORM/Query/AST/Functions/JsonValue.php | 12 ++++++++++++ 5 files changed, 48 insertions(+) diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php index e9331483..d07991b5 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php @@ -7,6 +7,15 @@ /** * Implementation of PostgreSQL JSON_EXISTS(). * + * Supports basic form: + * - json_exists(target_json, path_text) + * + * Note: The following variations are NOT supported due to DQL limitations: + * - PASSING variables + * - ON ERROR clause + * - ON EMPTY clause + * - WITH UNIQUE KEYS clause + * * @see https://www.postgresql.org/docs/17/functions-json.html * @since 2.10 * diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php index f4fdc4ac..552de27a 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php @@ -7,6 +7,20 @@ /** * Implementation of PostgreSQL JSON_QUERY(). * + * Supports basic form: + * - json_query(target_json, path_text) + * + * Note: The following variations are NOT supported due to DQL limitations: + * - PASSING variables + * - RETURNING type + * - WRAPPER clause + * - ON ERROR clause + * - ON EMPTY clause + * - WITH WRAPPER clause + * - WITH CONDITIONAL WRAPPER clause + * - EMPTY/NULL ON EMPTY + * - ERROR/NULL ON ERROR + * * @see https://www.postgresql.org/docs/17/functions-json.html * @since 2.10 * diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php index efe8c30e..4b4189d2 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php @@ -7,6 +7,14 @@ /** * Implementation of PostgreSQL JSON_SCALAR(). * + * Supports basic form: + * - json_scalar(target_json) + * + * Note: The following variations are NOT supported due to DQL limitations: + * - RETURNING type + * - ON ERROR clause + * - ERROR/NULL ON ERROR + * * @see https://www.postgresql.org/docs/17/functions-json.html * @since 2.10 * diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php index a447e3f7..94951da2 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php @@ -10,6 +10,11 @@ * Supports basic form: * - json_serialize(expression) * + * Note: The following variations are NOT supported due to DQL limitations: + * - RETURNING type + * - FORMAT JSON clause + * - ENCODING clause + * * @see https://www.postgresql.org/docs/17/functions-json.html * @since 2.10 * diff --git a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php index 270995cd..f977212c 100644 --- a/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php +++ b/src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php @@ -7,6 +7,18 @@ /** * Implementation of PostgreSQL JSON_VALUE(). * + * Supports basic form: + * - json_value(target_json, path_text) + * + * Note: The following variations are NOT supported due to DQL limitations: + * - PASSING variables + * - RETURNING type + * - DEFAULT clause + * - ON ERROR clause + * - ON EMPTY clause + * - ERROR/NULL/DEFAULT ON ERROR + * - ERROR/NULL/DEFAULT ON EMPTY + * * @see https://www.postgresql.org/docs/17/functions-json.html * @since 2.10 *