ScientaNL/DoctrineJsonFunctions

Doctrine DQL functions for SQL JSON data type

PHP

593

163 commits

updated Sep 17, 2026

See the code

README

Latest Stable Version Total Downloads License Unittest Coverage Integrationtest Coverage

DoctrineJsonFunctions

A set of extensions to Doctrine ORM that add support for JSON functions in DQL (Doctrine Query Language). Supports MySQL, MariaDB, PostgreSQL, SQLite, and SQL Server.

Overview

Doctrine ORM does not natively support database-specific JSON functions in DQL. This library bridges that gap by registering custom DQL function nodes for each supported platform. Each function validates at SQL generation time that the correct database platform is in use, so you get an early error if a function is used against the wrong database.

Supported Platforms and Functions

DatabaseFunctions
MySQL 5.7+ / MariaDBJSON_ARRAY, JSON_ARRAY_APPEND, JSON_ARRAY_INSERT, JSON_ARRAYAGG, JSON_CONTAINS, JSON_CONTAINS_PATH, JSON_DEPTH, JSON_EXTRACT, JSON_INSERT, JSON_KEYS, JSON_LENGTH, JSON_MERGE, JSON_MERGE_PATCH, JSON_MERGE_PRESERVE, JSON_OBJECT, JSON_OBJECTAGG, JSON_OVERLAPS, JSON_PRETTY, JSON_QUOTE, JSON_REMOVE, JSON_REPLACE, JSON_SEARCH, JSON_SET, JSON_TYPE, JSON_UNQUOTE, JSON_VALID
MySQL 8.0.21+ onlyJSON_VALUE
MariaDB onlyJSON_COMPACT, JSON_DETAILED, JSON_EQUALS, JSON_EXISTS, JSON_LOOSE, JSON_NORMALIZE, JSON_QUERY, JSON_VALUE
PostgreSQL 9.3+JSONB_CONTAINS, JSONB_EXISTS, JSONB_EXISTS_ALL, JSONB_EXISTS_ANY, JSONB_INSERT, JSONB_IS_CONTAINED, JSON_EXTRACT_PATH, JSON_GET, JSON_GET_PATH, JSON_GET_PATH_TEXT, JSON_GET_TEXT
SQLite (json1 ext.)JSON, JSON_ARRAY, JSON_ARRAY_LENGTH, JSON_EXTRACT, JSON_GROUP_ARRAY, JSON_GROUP_OBJECT, JSON_INSERT, JSON_OBJECT, JSON_PATCH, JSON_QUOTE, JSON_REMOVE, JSON_REPLACE, JSON_SET, JSON_TYPE, JSON_VALID
SQL Server 2016+JSON_VALUE

Table of Contents

Requirements

  • PHP 8.1+
  • doctrine/orm: ^2.19 or ^3
  • doctrine/dbal: ^3.2 or ^4
  • doctrine/lexer: ^2.0 or ^3.0

Installation

Install via Composer:

composer require scienta/doctrine-json-functions

Registration

All functions must be registered as custom string functions in the Doctrine configuration before they can be used in DQL. Each function class exposes a FUNCTION_NAME constant that matches the DQL keyword you use in queries.

Note on boolean functions: Doctrine DQL does not have a native boolean function type (upstream issue). Register boolean-returning functions (e.g., JSONB_CONTAINS, JSON_CONTAINS) as string_functions and compare them explicitly with = true or = 1 in your DQL to avoid parser errors.

Doctrine ORM

<?php

use Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql as MysqlFunctions;

$config = new \Doctrine\ORM\Configuration();

// Register the functions you need (example: MySQL)
$config->addCustomStringFunction(MysqlFunctions\JsonExtract::FUNCTION_NAME, MysqlFunctions\JsonExtract::class);
$config->addCustomStringFunction(MysqlFunctions\JsonContains::FUNCTION_NAME, MysqlFunctions\JsonContains::class);
$config->addCustomStringFunction(MysqlFunctions\JsonUnquote::FUNCTION_NAME, MysqlFunctions\JsonUnquote::class);

$em = EntityManager::create($dbParams, $config);

Symfony with DoctrineBundle

# config/packages/doctrine.yaml
doctrine:
    orm:
        dql:
            string_functions:
                # MySQL / MariaDB shared
                JSON_EXTRACT:        Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonExtract
                JSON_CONTAINS:       Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonContains
                JSON_UNQUOTE:        Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonUnquote
                JSON_SEARCH:         Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonSearch
                # PostgreSQL
                JSONB_CONTAINS:      Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonbContains
                JSONB_EXISTS:        Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonbExists
                JSON_GET:            Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonGet
                JSON_GET_TEXT:       Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonGetText
                # SQL Server
                JSON_VALUE:          Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mssql\JsonValue

Usage

Use the registered DQL function names directly in your DQL queries or query builders. JSON path expressions must use single-quoted strings in DQL (not double-quoted).

MySQL / MariaDB

// Extract a value from a JSON column
$results = $em->createQuery(
    "SELECT c FROM App\Entity\Customer c
     WHERE JSON_UNQUOTE(JSON_EXTRACT(c.attributes, '$.country')) = :country"
)->setParameter('country', 'NL')->getResult();

// Check if a JSON array contains a value
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_CONTAINS(c.roles, :role) = 1")
    ->setParameter('role', '"admin"')
    ->getQuery()->getResult();

// Use JSON_SEARCH to find a path
$q = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_SEARCH(c.attributes, 'one', :cert, null, '$.certificates') IS NOT NULL")
    ->setParameter('cert', 'BIO');

PostgreSQL

PostgreSQL operators (->, ->>, @>, ?, etc.) are exposed as named DQL functions because Doctrine DQL does not support custom operators.

// Get a JSON object field as text
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_GET_TEXT(c.attributes, 'country') = :country")
    ->setParameter('country', 'NL')
    ->getQuery()->getResult();

// Check JSONB containment (boolean — must compare with = true)
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->andWhere('JSONB_CONTAINS(c.roles, :role) = true')
    ->setParameter('role', '"ROLE_ADMIN"')
    ->getQuery()->getResult();

// Check if a key exists in a JSONB column
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->andWhere('JSONB_EXISTS(c.data, :key) = true')
    ->setParameter('key', 'active')
    ->getQuery()->getResult();

PostgreSQL operator chaining (e.g., col->'a'->'b') is not supported. Use JSON_GET_PATH (works on both json and jsonb) or JSON_EXTRACT_PATH (json columns only) instead.

SQLite

// Extract a field from a JSON column
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_EXTRACT(c.attributes, '$.country') = :country")
    ->setParameter('country', 'NL')
    ->getQuery()->getResult();

SQL Server

// Extract a scalar value from a JSON column
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_VALUE(c.attributes, '$.country') = :country")
    ->setParameter('country', 'NL')
    ->getQuery()->getResult();

To apply type conversion, use CAST outside the function: CAST(JSON_VALUE(c.attributes, '$.score') AS DECIMAL(4,2))

DQL Function Reference

MySQL 5.7+ and MariaDB (shared)

All functions in the Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql namespace unless noted. Functions marked as shared also work on MariaDB (they are registered under the MySQL namespace but validate against AbstractMySQLPlatform / MariaDBPlatform).

DQL FunctionClassSignatureDescription
JSON_ARRAYJsonArrayJSON_ARRAY([val, ...])Creates a JSON array from arguments
JSON_ARRAY_APPENDJsonArrayAppendJSON_ARRAY_APPEND(doc, path, val[, ...])Appends values to JSON arrays
JSON_ARRAY_INSERTJsonArrayInsertJSON_ARRAY_INSERT(doc, path, val[, ...])Inserts into a JSON array
JSON_ARRAYAGGJsonArrayAggJSON_ARRAYAGG(value)Aggregate: builds a JSON array from rows
JSON_CONTAINSJsonContainsJSON_CONTAINS(doc, val[, path])Returns 1 if doc contains val
JSON_CONTAINS_PATHJsonContainsPathJSON_CONTAINS_PATH(doc, one_or_all, path[, ...])Returns 1 if path(s) exist
JSON_DEPTHJsonDepthJSON_DEPTH(doc)Returns maximum depth of document
JSON_EXTRACTJsonExtractJSON_EXTRACT(doc, path[, ...])Extracts data from a JSON document
JSON_INSERTJsonInsertJSON_INSERT(doc, path, val[, ...])Inserts data into a JSON document
JSON_KEYSJsonKeysJSON_KEYS(doc[, path])Returns top-level keys as a JSON array
JSON_LENGTHJsonLengthJSON_LENGTH(doc[, path])Returns the length of a JSON document or value
JSON_MERGEJsonMergeJSON_MERGE(doc, doc[, ...])Merges JSON documents (deprecated alias)
JSON_MERGE_PATCHJsonMergePatchJSON_MERGE_PATCH(doc, doc[, ...])RFC 7396 merge patch
JSON_MERGE_PRESERVEJsonMergePreserveJSON_MERGE_PRESERVE(doc, doc[, ...])Merges preserving duplicate keys
JSON_OBJECTJsonObjectJSON_OBJECT([key, val, ...])Creates a JSON object
JSON_OBJECTAGGJsonObjectAggJSON_OBJECTAGG(key, val)Aggregate: builds a JSON object from rows
JSON_OVERLAPSJsonOverlapsJSON_OVERLAPS(doc1, doc2)Returns 1 if documents share key-value pairs or array elements
JSON_PRETTYJsonPrettyJSON_PRETTY(val)Returns pretty-printed JSON
JSON_QUOTEJsonQuoteJSON_QUOTE(val)Quotes a string as a JSON value
JSON_REMOVEJsonRemoveJSON_REMOVE(doc, path[, ...])Removes data from a JSON document
JSON_REPLACEJsonReplaceJSON_REPLACE(doc, path, val[, ...])Replaces existing values
JSON_SEARCHJsonSearchJSON_SEARCH(doc, one|all, str[, escape[, path...]])Returns path to a string in a document
JSON_SETJsonSetJSON_SET(doc, path, val[, ...])Inserts or updates values
JSON_TYPEJsonTypeJSON_TYPE(val)Returns the JSON type string
JSON_UNQUOTEJsonUnquoteJSON_UNQUOTE(val)Unquotes a JSON value
JSON_VALIDJsonValidJSON_VALID(val)Returns 1 if value is valid JSON

MySQL functions that also apply to MariaDB use MysqlAndMariadbJsonFunctionNode as their base, which validates against AbstractMySQLPlatform (DBAL 3.3+) or MySQLPlatform (older DBAL).

MySQL 8.0.21+ only

DQL FunctionClassSignatureDescription
JSON_VALUEJsonValueJSON_VALUE(doc, path[, RETURNING type])Extracts a scalar value; supports RETURNING DECIMAL(n,m), RETURNING CHAR, etc.

This function uses MysqlJsonFunctionNode and only works on MySQL (not MariaDB).

MariaDB only

Namespace: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mariadb

DQL FunctionClassAvailable SinceDescription
JSON_VALUEJsonValueMariaDB 10.2.3Extracts a scalar value at a path
JSON_EXISTSJsonExistsMariaDB 10.2.3Returns 1 if path exists in document
JSON_QUERYJsonQueryMariaDB 10.2.3Returns an object or array at a path
JSON_COMPACTJsonCompactMariaDB 10.2.4Removes unnecessary whitespace from JSON
JSON_DETAILEDJsonDetailedMariaDB 10.2.4Returns human-readable formatted JSON
JSON_LOOSEJsonLooseMariaDB 10.2.4Adds spaces for readability
JSON_EQUALSJsonEqualsMariaDB 10.7.0Returns 1 if two JSON documents are equal
JSON_NORMALIZEJsonNormalizeMariaDB 10.7.0Sorts keys and removes spaces for comparison

MySQL operators like JSON_EXTRACT are also available on MariaDB — register them from the Mysql namespace.

PostgreSQL 9.3+

Namespace: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql

PostgreSQL JSON operators are wrapped as named functions. SQL output uses the native operators.

DQL FunctionClassSQL EquivalentDescription
JSONB_CONTAINSJsonbContainsjsonb @> jsonbReturns true if left contains right
JSONB_EXISTSJsonbExistsnative jsonb_exists(jsonb, text)Returns true if key exists in JSONB
JSONB_EXISTS_ALLJsonbExistsAllnative jsonb_exists_all(jsonb, text[])Returns true if all keys exist
JSONB_EXISTS_ANYJsonbExistsAnynative jsonb_exists_any(jsonb, text[])Returns true if any key exists
JSONB_INSERTJsonbInsertnative jsonb_insert(...)Inserts a value into a JSONB document
JSONB_IS_CONTAINEDJsonbIsContainedjsonb <@ jsonbReturns true if left is contained by right
JSON_EXTRACT_PATHJsonExtractPathnative json_extract_path(...)Extracts a JSON sub-object at a path (json columns only, not jsonb)
JSON_GETJsonGetjson -> key (numeric: -> int, text: -> 'key')Returns a JSON field/element
JSON_GET_TEXTJsonGetTextjson ->> keyReturns a JSON field/element as text
JSON_GET_PATHJsonGetPathjson #> '{path}'Extracts a sub-object at path array
JSON_GET_PATH_TEXTJsonGetPathTextjson #>> '{path}'Extracts a sub-object at path array as text

Boolean functions (JSONB_CONTAINS, JSONB_EXISTS, JSONB_EXISTS_ALL, JSONB_EXISTS_ANY, JSONB_IS_CONTAINED) must be compared with = true due to the Doctrine DQL boolean function limitation:

->andWhere('JSONB_CONTAINS(e.data, :val) = true')

SQLite json1 extension

Namespace: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Sqlite

SQLite must have the json1 extension enabled (it is compiled in by default since SQLite 3.38.0).

Scalar functions

DQL FunctionClassSignatureDescription
JSONJsonJSON(json)Validates and minifies a JSON string
JSON_ARRAYJsonArrayJSON_ARRAY([val, ...])Creates a JSON array
JSON_ARRAY_LENGTHJsonArrayLengthJSON_ARRAY_LENGTH(json[, path])Returns the number of elements in an array
JSON_EXTRACTJsonExtractJSON_EXTRACT(json, path[, ...])Extracts one or more values
JSON_INSERTJsonInsertJSON_INSERT(json[, path, value, ...])Inserts values without overwriting
JSON_OBJECTJsonObjectJSON_OBJECT(label, value[, ...])Creates a JSON object
JSON_PATCHJsonPatchJSON_PATCH(target, patch)Applies an RFC 7396 merge patch
JSON_QUOTEJsonQuoteJSON_QUOTE(value)Converts a SQL value to its JSON representation
JSON_REMOVEJsonRemoveJSON_REMOVE(json[, path, ...])Removes values at given paths
JSON_REPLACEJsonReplaceJSON_REPLACE(json[, path, value, ...])Overwrites values at given paths
JSON_SETJsonSetJSON_SET(json[, path, value, ...])Inserts or overwrites values
JSON_TYPEJsonTypeJSON_TYPE(json[, path])Returns the type of a JSON value
JSON_VALIDJsonValidJSON_VALID(json)Returns 1 if argument is valid JSON

Aggregate functions

DQL FunctionClassSignatureDescription
JSON_GROUP_ARRAYJsonGroupArrayJSON_GROUP_ARRAY(value)Aggregates all values into a JSON array
JSON_GROUP_OBJECTJsonGroupObjectJSON_GROUP_OBJECT(name, value)Aggregates name/value pairs into a JSON object

SQL Server 2016+

Namespace: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mssql

DQL FunctionClassSignatureDescription
JSON_VALUEJsonValueJSON_VALUE(doc, path)Extracts a scalar value from a JSON string at the given path

SQL Server does not support inline type conversion inside JSON_VALUE. Use CAST(JSON_VALUE(...) AS type) in your DQL for type conversion.

Contributing

See CONTRIBUTING.md for how to run the test suites, how the function nodes are layered, and how to add a function or a platform.

Domain terms are defined in CONTEXT.md.

Changelog

Changes per release are documented in GitHub releases.

See Also

doctrine
doctrine-bundle
dql-functions
json-functions
json-operators
mariadb
mysql
orm
postgresql
sqlite

ScientaNL/DoctrineJsonFunctions

Doctrine DQL functions for SQL JSON data type

PHP

593

163 commits

updated Sep 17, 2026

See the code

README

Latest Stable Version Total Downloads License Unittest Coverage Integrationtest Coverage

DoctrineJsonFunctions

A set of extensions to Doctrine ORM that add support for JSON functions in DQL (Doctrine Query Language). Supports MySQL, MariaDB, PostgreSQL, SQLite, and SQL Server.

Overview

Doctrine ORM does not natively support database-specific JSON functions in DQL. This library bridges that gap by registering custom DQL function nodes for each supported platform. Each function validates at SQL generation time that the correct database platform is in use, so you get an early error if a function is used against the wrong database.

Supported Platforms and Functions

DatabaseFunctions
MySQL 5.7+ / MariaDBJSON_ARRAY, JSON_ARRAY_APPEND, JSON_ARRAY_INSERT, JSON_ARRAYAGG, JSON_CONTAINS, JSON_CONTAINS_PATH, JSON_DEPTH, JSON_EXTRACT, JSON_INSERT, JSON_KEYS, JSON_LENGTH, JSON_MERGE, JSON_MERGE_PATCH, JSON_MERGE_PRESERVE, JSON_OBJECT, JSON_OBJECTAGG, JSON_OVERLAPS, JSON_PRETTY, JSON_QUOTE, JSON_REMOVE, JSON_REPLACE, JSON_SEARCH, JSON_SET, JSON_TYPE, JSON_UNQUOTE, JSON_VALID
MySQL 8.0.21+ onlyJSON_VALUE
MariaDB onlyJSON_COMPACT, JSON_DETAILED, JSON_EQUALS, JSON_EXISTS, JSON_LOOSE, JSON_NORMALIZE, JSON_QUERY, JSON_VALUE
PostgreSQL 9.3+JSONB_CONTAINS, JSONB_EXISTS, JSONB_EXISTS_ALL, JSONB_EXISTS_ANY, JSONB_INSERT, JSONB_IS_CONTAINED, JSON_EXTRACT_PATH, JSON_GET, JSON_GET_PATH, JSON_GET_PATH_TEXT, JSON_GET_TEXT
SQLite (json1 ext.)JSON, JSON_ARRAY, JSON_ARRAY_LENGTH, JSON_EXTRACT, JSON_GROUP_ARRAY, JSON_GROUP_OBJECT, JSON_INSERT, JSON_OBJECT, JSON_PATCH, JSON_QUOTE, JSON_REMOVE, JSON_REPLACE, JSON_SET, JSON_TYPE, JSON_VALID
SQL Server 2016+JSON_VALUE

Table of Contents

Requirements

  • PHP 8.1+
  • doctrine/orm: ^2.19 or ^3
  • doctrine/dbal: ^3.2 or ^4
  • doctrine/lexer: ^2.0 or ^3.0

Installation

Install via Composer:

composer require scienta/doctrine-json-functions

Registration

All functions must be registered as custom string functions in the Doctrine configuration before they can be used in DQL. Each function class exposes a FUNCTION_NAME constant that matches the DQL keyword you use in queries.

Note on boolean functions: Doctrine DQL does not have a native boolean function type (upstream issue). Register boolean-returning functions (e.g., JSONB_CONTAINS, JSON_CONTAINS) as string_functions and compare them explicitly with = true or = 1 in your DQL to avoid parser errors.

Doctrine ORM

<?php

use Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql as MysqlFunctions;

$config = new \Doctrine\ORM\Configuration();

// Register the functions you need (example: MySQL)
$config->addCustomStringFunction(MysqlFunctions\JsonExtract::FUNCTION_NAME, MysqlFunctions\JsonExtract::class);
$config->addCustomStringFunction(MysqlFunctions\JsonContains::FUNCTION_NAME, MysqlFunctions\JsonContains::class);
$config->addCustomStringFunction(MysqlFunctions\JsonUnquote::FUNCTION_NAME, MysqlFunctions\JsonUnquote::class);

$em = EntityManager::create($dbParams, $config);

Symfony with DoctrineBundle

# config/packages/doctrine.yaml
doctrine:
    orm:
        dql:
            string_functions:
                # MySQL / MariaDB shared
                JSON_EXTRACT:        Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonExtract
                JSON_CONTAINS:       Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonContains
                JSON_UNQUOTE:        Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonUnquote
                JSON_SEARCH:         Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonSearch
                # PostgreSQL
                JSONB_CONTAINS:      Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonbContains
                JSONB_EXISTS:        Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonbExists
                JSON_GET:            Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonGet
                JSON_GET_TEXT:       Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonGetText
                # SQL Server
                JSON_VALUE:          Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mssql\JsonValue

Usage

Use the registered DQL function names directly in your DQL queries or query builders. JSON path expressions must use single-quoted strings in DQL (not double-quoted).

MySQL / MariaDB

// Extract a value from a JSON column
$results = $em->createQuery(
    "SELECT c FROM App\Entity\Customer c
     WHERE JSON_UNQUOTE(JSON_EXTRACT(c.attributes, '$.country')) = :country"
)->setParameter('country', 'NL')->getResult();

// Check if a JSON array contains a value
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_CONTAINS(c.roles, :role) = 1")
    ->setParameter('role', '"admin"')
    ->getQuery()->getResult();

// Use JSON_SEARCH to find a path
$q = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_SEARCH(c.attributes, 'one', :cert, null, '$.certificates') IS NOT NULL")
    ->setParameter('cert', 'BIO');

PostgreSQL

PostgreSQL operators (->, ->>, @>, ?, etc.) are exposed as named DQL functions because Doctrine DQL does not support custom operators.

// Get a JSON object field as text
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_GET_TEXT(c.attributes, 'country') = :country")
    ->setParameter('country', 'NL')
    ->getQuery()->getResult();

// Check JSONB containment (boolean — must compare with = true)
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->andWhere('JSONB_CONTAINS(c.roles, :role) = true')
    ->setParameter('role', '"ROLE_ADMIN"')
    ->getQuery()->getResult();

// Check if a key exists in a JSONB column
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->andWhere('JSONB_EXISTS(c.data, :key) = true')
    ->setParameter('key', 'active')
    ->getQuery()->getResult();

PostgreSQL operator chaining (e.g., col->'a'->'b') is not supported. Use JSON_GET_PATH (works on both json and jsonb) or JSON_EXTRACT_PATH (json columns only) instead.

SQLite

// Extract a field from a JSON column
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_EXTRACT(c.attributes, '$.country') = :country")
    ->setParameter('country', 'NL')
    ->getQuery()->getResult();

SQL Server

// Extract a scalar value from a JSON column
$results = $queryBuilder
    ->select('c')
    ->from('App\Entity\Customer', 'c')
    ->where("JSON_VALUE(c.attributes, '$.country') = :country")
    ->setParameter('country', 'NL')
    ->getQuery()->getResult();

To apply type conversion, use CAST outside the function: CAST(JSON_VALUE(c.attributes, '$.score') AS DECIMAL(4,2))

DQL Function Reference

MySQL 5.7+ and MariaDB (shared)

All functions in the Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql namespace unless noted. Functions marked as shared also work on MariaDB (they are registered under the MySQL namespace but validate against AbstractMySQLPlatform / MariaDBPlatform).

DQL FunctionClassSignatureDescription
JSON_ARRAYJsonArrayJSON_ARRAY([val, ...])Creates a JSON array from arguments
JSON_ARRAY_APPENDJsonArrayAppendJSON_ARRAY_APPEND(doc, path, val[, ...])Appends values to JSON arrays
JSON_ARRAY_INSERTJsonArrayInsertJSON_ARRAY_INSERT(doc, path, val[, ...])Inserts into a JSON array
JSON_ARRAYAGGJsonArrayAggJSON_ARRAYAGG(value)Aggregate: builds a JSON array from rows
JSON_CONTAINSJsonContainsJSON_CONTAINS(doc, val[, path])Returns 1 if doc contains val
JSON_CONTAINS_PATHJsonContainsPathJSON_CONTAINS_PATH(doc, one_or_all, path[, ...])Returns 1 if path(s) exist
JSON_DEPTHJsonDepthJSON_DEPTH(doc)Returns maximum depth of document
JSON_EXTRACTJsonExtractJSON_EXTRACT(doc, path[, ...])Extracts data from a JSON document
JSON_INSERTJsonInsertJSON_INSERT(doc, path, val[, ...])Inserts data into a JSON document
JSON_KEYSJsonKeysJSON_KEYS(doc[, path])Returns top-level keys as a JSON array
JSON_LENGTHJsonLengthJSON_LENGTH(doc[, path])Returns the length of a JSON document or value
JSON_MERGEJsonMergeJSON_MERGE(doc, doc[, ...])Merges JSON documents (deprecated alias)
JSON_MERGE_PATCHJsonMergePatchJSON_MERGE_PATCH(doc, doc[, ...])RFC 7396 merge patch
JSON_MERGE_PRESERVEJsonMergePreserveJSON_MERGE_PRESERVE(doc, doc[, ...])Merges preserving duplicate keys
JSON_OBJECTJsonObjectJSON_OBJECT([key, val, ...])Creates a JSON object
JSON_OBJECTAGGJsonObjectAggJSON_OBJECTAGG(key, val)Aggregate: builds a JSON object from rows
JSON_OVERLAPSJsonOverlapsJSON_OVERLAPS(doc1, doc2)Returns 1 if documents share key-value pairs or array elements
JSON_PRETTYJsonPrettyJSON_PRETTY(val)Returns pretty-printed JSON
JSON_QUOTEJsonQuoteJSON_QUOTE(val)Quotes a string as a JSON value
JSON_REMOVEJsonRemoveJSON_REMOVE(doc, path[, ...])Removes data from a JSON document
JSON_REPLACEJsonReplaceJSON_REPLACE(doc, path, val[, ...])Replaces existing values
JSON_SEARCHJsonSearchJSON_SEARCH(doc, one|all, str[, escape[, path...]])Returns path to a string in a document
JSON_SETJsonSetJSON_SET(doc, path, val[, ...])Inserts or updates values
JSON_TYPEJsonTypeJSON_TYPE(val)Returns the JSON type string
JSON_UNQUOTEJsonUnquoteJSON_UNQUOTE(val)Unquotes a JSON value
JSON_VALIDJsonValidJSON_VALID(val)Returns 1 if value is valid JSON

MySQL functions that also apply to MariaDB use MysqlAndMariadbJsonFunctionNode as their base, which validates against AbstractMySQLPlatform (DBAL 3.3+) or MySQLPlatform (older DBAL).

MySQL 8.0.21+ only

DQL FunctionClassSignatureDescription
JSON_VALUEJsonValueJSON_VALUE(doc, path[, RETURNING type])Extracts a scalar value; supports RETURNING DECIMAL(n,m), RETURNING CHAR, etc.

This function uses MysqlJsonFunctionNode and only works on MySQL (not MariaDB).

MariaDB only

Namespace: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mariadb

DQL FunctionClassAvailable SinceDescription
JSON_VALUEJsonValueMariaDB 10.2.3Extracts a scalar value at a path
JSON_EXISTSJsonExistsMariaDB 10.2.3Returns 1 if path exists in document
JSON_QUERYJsonQueryMariaDB 10.2.3Returns an object or array at a path
JSON_COMPACTJsonCompactMariaDB 10.2.4Removes unnecessary whitespace from JSON
JSON_DETAILEDJsonDetailedMariaDB 10.2.4Returns human-readable formatted JSON
JSON_LOOSEJsonLooseMariaDB 10.2.4Adds spaces for readability
JSON_EQUALSJsonEqualsMariaDB 10.7.0Returns 1 if two JSON documents are equal
JSON_NORMALIZEJsonNormalizeMariaDB 10.7.0Sorts keys and removes spaces for comparison

MySQL operators like JSON_EXTRACT are also available on MariaDB — register them from the Mysql namespace.

PostgreSQL 9.3+

Namespace: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Postgresql

PostgreSQL JSON operators are wrapped as named functions. SQL output uses the native operators.

DQL FunctionClassSQL EquivalentDescription
JSONB_CONTAINSJsonbContainsjsonb @> jsonbReturns true if left contains right
JSONB_EXISTSJsonbExistsnative jsonb_exists(jsonb, text)Returns true if key exists in JSONB
JSONB_EXISTS_ALLJsonbExistsAllnative jsonb_exists_all(jsonb, text[])Returns true if all keys exist
JSONB_EXISTS_ANYJsonbExistsAnynative jsonb_exists_any(jsonb, text[])Returns true if any key exists
JSONB_INSERTJsonbInsertnative jsonb_insert(...)Inserts a value into a JSONB document
JSONB_IS_CONTAINEDJsonbIsContainedjsonb <@ jsonbReturns true if left is contained by right
JSON_EXTRACT_PATHJsonExtractPathnative json_extract_path(...)Extracts a JSON sub-object at a path (json columns only, not jsonb)
JSON_GETJsonGetjson -> key (numeric: -> int, text: -> 'key')Returns a JSON field/element
JSON_GET_TEXTJsonGetTextjson ->> keyReturns a JSON field/element as text
JSON_GET_PATHJsonGetPathjson #> '{path}'Extracts a sub-object at path array
JSON_GET_PATH_TEXTJsonGetPathTextjson #>> '{path}'Extracts a sub-object at path array as text

Boolean functions (JSONB_CONTAINS, JSONB_EXISTS, JSONB_EXISTS_ALL, JSONB_EXISTS_ANY, JSONB_IS_CONTAINED) must be compared with = true due to the Doctrine DQL boolean function limitation:

->andWhere('JSONB_CONTAINS(e.data, :val) = true')

SQLite json1 extension

Namespace: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Sqlite

SQLite must have the json1 extension enabled (it is compiled in by default since SQLite 3.38.0).

Scalar functions

DQL FunctionClassSignatureDescription
JSONJsonJSON(json)Validates and minifies a JSON string
JSON_ARRAYJsonArrayJSON_ARRAY([val, ...])Creates a JSON array
JSON_ARRAY_LENGTHJsonArrayLengthJSON_ARRAY_LENGTH(json[, path])Returns the number of elements in an array
JSON_EXTRACTJsonExtractJSON_EXTRACT(json, path[, ...])Extracts one or more values
JSON_INSERTJsonInsertJSON_INSERT(json[, path, value, ...])Inserts values without overwriting
JSON_OBJECTJsonObjectJSON_OBJECT(label, value[, ...])Creates a JSON object
JSON_PATCHJsonPatchJSON_PATCH(target, patch)Applies an RFC 7396 merge patch
JSON_QUOTEJsonQuoteJSON_QUOTE(value)Converts a SQL value to its JSON representation
JSON_REMOVEJsonRemoveJSON_REMOVE(json[, path, ...])Removes values at given paths
JSON_REPLACEJsonReplaceJSON_REPLACE(json[, path, value, ...])Overwrites values at given paths
JSON_SETJsonSetJSON_SET(json[, path, value, ...])Inserts or overwrites values
JSON_TYPEJsonTypeJSON_TYPE(json[, path])Returns the type of a JSON value
JSON_VALIDJsonValidJSON_VALID(json)Returns 1 if argument is valid JSON

Aggregate functions

DQL FunctionClassSignatureDescription
JSON_GROUP_ARRAYJsonGroupArrayJSON_GROUP_ARRAY(value)Aggregates all values into a JSON array
JSON_GROUP_OBJECTJsonGroupObjectJSON_GROUP_OBJECT(name, value)Aggregates name/value pairs into a JSON object

SQL Server 2016+

Namespace: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mssql

DQL FunctionClassSignatureDescription
JSON_VALUEJsonValueJSON_VALUE(doc, path)Extracts a scalar value from a JSON string at the given path

SQL Server does not support inline type conversion inside JSON_VALUE. Use CAST(JSON_VALUE(...) AS type) in your DQL for type conversion.

Contributing

See CONTRIBUTING.md for how to run the test suites, how the function nodes are layered, and how to add a function or a platform.

Domain terms are defined in CONTEXT.md.

Changelog

Changes per release are documented in GitHub releases.

See Also

doctrine
doctrine-bundle
dql-functions
json-functions
json-operators
mariadb
mysql
orm
postgresql
sqlite

Project health

Overall

4.5/10

Maintained

10/10

Code review

7/10
OpenSSF Scorecard

Significant stargazers

Kévin Dunglas

4,633 followers · starred May 2017

Jérôme Tamarelle

277 followers · starred Nov 2024

Louis-Arnaud

29 followers · starred Mar 2024

Michael Moravec

195 followers · starred Jul 2017