Oihana PHP Arango

fields

Table of Contents

Functions

aqlFieldArray()  : string
Generates an AQL key/value expression for counting elements in an array field.
aqlFieldArrayCount()  : string
Generates an AQL key/value expression for counting elements in an array field.
aqlFieldArrayFirst()  : string
Generates an AQL key/value expression for extracting the first element of an array field.
aqlFieldBool()  : string
Generates an AQL key/value expression that converts a document field to boolean.
aqlFieldConditional()  : string
Generate an AQL key/value expression for a conditional scalar field (`Field::WHEN`).
aqlFieldDateTime()  : string
Generates an AQL key/value expression for a DateTime field.
aqlFieldDefault()  : string
Generates a simple AQL key/value expression referencing a document field.
aqlFieldDocument()  : string
Generates an AQL key/value expression for a DOCUMENT-type field.
aqlFieldMap()  : string
Generates an AQL expression for mapping an array field to structured documents.
aqlFieldNumber()  : string
Generates an AQL key/value expression for a numeric field.
aqlFieldObject()  : string
Generates an AQL key/value expression for extracting an object or the first element of an array field.
aqlFieldTranslate()  : string
Generates an AQL key/value expression for a translated document field.
aqlFieldUrl()  : string
Generates an AQL expression for a document URL field with optional dynamic placeholders.
aqlFieldWrap()  : string
Generates an AQL key/value expression that wraps the **current reference** itself under a named key.
buildWhenCondition()  : string
Compile a `Field::WHEN` descriptor into a boolean AQL expression.
buildWhenLeaf()  : string
Compile a single `Field::WHEN` condition leaf into a boolean AQL expression.
collectWhenAttributes()  : array<int, string>
Collects the document attribute names **read** by a {@see \oihana\arango\enums\Field::WHEN} (or {@see \oihana\arango\enums\Field::WHERE}) condition, mirroring the grammar compiled by {@see buildWhenCondition()} — a truthiness string, an explicit leaf (list `[ attr, op, val ]` or associative `[ FilterParam::KEY => …, … ]`), and the `and` / `or` / `not` / implicit-AND groups.
conditionReadsDeniedField()  : bool
Decides whether a conditional projection would **read** a field the caller may not read — an inference oracle through a condition (T5). The field that *carries* the condition is already gated by {@see \oihana\arango\db\helpers\isAuthorized()}; this closes the complementary hole where the condition *reads* a masked field:
guardProjection()  : string
Wrap an already-built **fabricated** projection behind an optional guard, so the field can yield `null` (or a `Field::ELSE` fallback) instead of a value rebuilt out of nothing.
resolveWhenElse()  : string
Resolve the `Field::ELSE` branch of a conditional field into an AQL expression.

Functions

aqlFieldArray()

Generates an AQL key/value expression for counting elements in an array field.

aqlFieldArray(string $key[, string $docRef = AQL::DOC ][, string|null $default = '[]' ]) : string

This helper constructs an expression suitable for inclusion in a RETURN { ... } block. It safely checks if the field is an array using IS_ARRAY() and returns its length with LENGTH(). If the field is not an array, it defaults to 0.

Example usage:

aqlFieldArrayCount('tags');
// Produces: "tags: IS_ARRAY(doc.tags) ? doc.tags : null"

// Count elements in a custom document variable "u" with property "authors"
aqlFieldArrayCount('authors', 'edge', '[]');
// Produces: "authors: IS_ARRAY(edge.authors) ? LENGTH(edge.authors) : []"
Parameters
$key : string

Logical key to use in the resulting AQL object.

$docRef : string = AQL::DOC

Document variable reference (default: AQL::DOC).

$default : string|null = '[]'

The default value if the doc property is not an array (Default '[]').

Tags
author

Marc Alcaraz

since
1.0.0
Return values
string

AQL key/value snippet returns an array element.

aqlFieldArrayCount()

Generates an AQL key/value expression for counting elements in an array field.

aqlFieldArrayCount(string $key[, string $docRef = AQL::DOC ][, string|null $alias = null ]) : string

This helper constructs an expression suitable for inclusion in a RETURN { ... } block. It safely checks if the field is an array using IS_ARRAY() and returns its length with LENGTH(). If the field is not an array, it defaults to 0.

Example usage:

// Count elements in "tags" array in "doc"
aqlFieldArrayCount('tagCount');
// Produces: "tagCount: IS_ARRAY(doc.tags) ? LENGTH(doc.tags) : 0"

// Count elements in a custom document variable "u" with property "authors"
aqlFieldArrayCount('authorCount', 'u', 'authors');
// Produces: "authorCount: IS_ARRAY(u.authors) ? LENGTH(u.authors) : 0"
Parameters
$key : string

Logical key to use in the resulting AQL object.

$docRef : string = AQL::DOC

Document variable reference (default: AQL::DOC).

$alias : string|null = null

Optional alias or property name in the document if different from $key.

Tags
author

Marc Alcaraz

since
1.0.0
Return values
string

AQL key/value snippet counting array elements.

aqlFieldArrayFirst()

Generates an AQL key/value expression for extracting the first element of an array field.

aqlFieldArrayFirst(string $key, string $value) : string

This helper constructs an expression suitable for inclusion in a RETURN { ... } block. It checks if the field is an array using IS_ARRAY() and returns the first element using FIRST(). If the field is not an array, it returns null.

Example usage:

// Extract the first author from "doc.authors"
aqlFieldArrayFirst('mainAuthor', 'doc.authors');
// Produces: "mainAuthor: IS_ARRAY(doc.authors) ? FIRST(doc.authors) : null"

// Extract the first tag from a custom variable
aqlFieldArrayFirst('firstTag', 'tagsList');
// Produces: "firstTag: IS_ARRAY(tagsList) ? FIRST(tagsList) : null"
Parameters
$key : string

Logical key to use in the resulting AQL object.

$value : string

AQL field reference to evaluate (e.g. 'doc.authors').

Tags
since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value snippet extracting the first array element.

aqlFieldBool()

Generates an AQL key/value expression that converts a document field to boolean.

aqlFieldBool(string $key[, string $doc = AQL::DOC ][, string|null $keyName = null ][, array<string|int, mixed> $options = [] ]) : string

This helper builds a snippet suitable for a RETURN { ... } block in AQL. It references a field in the given document (or alias) and wraps it with the TO_BOOL() function, ensuring the value is interpreted as a boolean in AQL.

If $keyName is not provided, the $key parameter is used as both the resulting key in the object and the field name in the document.

Example usage:

// PHP call
aqlFieldBool('isActive');

// Generates
isActive: TO_BOOL(doc.isActive)

Abstaining (Field::NULLABLE):

TO_BOOL() answers even when nothing was asked: a document that says nothing about the attribute comes back with false, indistinguishable from one that stores false. An opt-in Field::NULLABLE guards the cast behind the presence of the attribute, and Field::ELSE picks what is said instead (default null):

aqlFieldBool( 'active' , 'doc' , null , [ Field::NULLABLE => true ] ) ;
// active:doc.active != null ? TO_BOOL(doc.active) : null

The test is != null, not IS_BOOL(): TO_BOOL() exists precisely to accept what is not a boolean — a document storing 1 or "yes" counts as true today — so a type test would make all of them abstain. The question asked is only « is the attribute there? ». Without the marker the emitted AQL is unchanged, byte for byte.

Parameters
$key : string

The key to use in the resulting AQL object (e.g. "isActive").

$doc : string = AQL::DOC

The document alias or variable name to reference (default: AQL::DOC).

$keyName : string|null = null

Optional field name in the document; if omitted, $key is used.

$options : array<string|int, mixed> = []

The field definition, read for the optional guard (Field::NULLABLE, Field::ELSE).

Tags
throws
UnsupportedOperationException

If the guard descriptor is malformed.

ValidationException

If an else attribute name is unsafe.

since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value expression, e.g. "isActive: TO_BOOL(doc.isActive)".

aqlFieldConditional()

Generate an AQL key/value expression for a conditional scalar field (`Field::WHEN`).

aqlFieldConditional(string $key, string $then, mixed $when[, mixed $else = null ][, string $doc = AQL::DOC ]) : string

Guards the projected value behind a condition — the SQL CASE WHEN … THEN … ELSE … shape, rendered as an AQL ternary:

price: doc.visibility == 'public' ? doc.price : null

The key is always present; only the value switches to the else branch (default null) when the condition is false — it never omits the key (that would require a MERGE, intentionally out of scope). The condition is compiled by buildWhenCondition() (inline, no bind variables) and the else branch by resolveWhenElse().

The $then expression is the already-built scalar projection — the bare field reference (doc.price) or, when the field also declares Field::ALTERS, the transformed value (LOWER(TRIM(doc.price))). The caller (aqlFields()) builds it.

Parameters
$key : string

The output key/label (possibly already double-quoted).

$then : string

The value projected when the condition holds (e.g. doc.price).

$when : mixed

The Field::WHEN condition descriptor.

$else : mixed = null

The Field::ELSE descriptor (literal or [ Field::PROPERTY => '<attr>' ]; default null).

$doc : string = AQL::DOC

The document reference for the condition/else operands (default: AQL::DOC).

Tags
throws
UnsupportedOperationException

If the condition descriptor is malformed.

ValidationException

If a condition or else attribute name is unsafe.

since
1.3.0
author

Marc Alcaraz

Return values
string

The AQL key/value snippet, e.g. price:doc.visibility == 'public' ? doc.price : null.

aqlFieldDateTime()

Generates an AQL key/value expression for a DateTime field.

aqlFieldDateTime(string $key[, string $doc = AQL::DOC ][, string|null $keyName = null ][, string|null $format = null ]) : string

This helper builds a snippet suitable for inclusion in a RETURN { ... } block. It performs the following steps:

  1. Checks whether the specified document field contains a valid date string using IS_DATE_STRING().
  2. If valid, formats the date to the provided ISO 8601 pattern using DATE_FORMAT().
  3. If not valid, returns null.

This ensures that the resulting AQL object always contains a valid ISO-formatted date or null.

Example usage:

// PHP call
aqlFieldDateTime('createdAt');

// Generates
createdAt: IS_DATE_STRING(doc.createdAt) ? DATE_FORMAT(doc.createdAt, "%yyyy-%mm-%ddT%hh:%ii:%ssZ") : null
Parameters
$key : string

The key to use in the resulting AQL object (e.g. "createdAt").

$doc : string = AQL::DOC

The document alias or variable name (default: AQL::DOC).

$keyName : string|null = null

Optional field name in the document; if omitted, $key is used.

$format : string|null = null

Optional AQL date format pattern (default: ISO 8601 style "%yyyy-%mm-%ddT%hh:%ii:%ssZ").

Tags
since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value expression string, e.g.: "createdAt: IS_DATE_STRING(doc.createdAt) ? DATE_FORMAT(doc.createdAt, "...") : null".

aqlFieldDefault()

Generates a simple AQL key/value expression referencing a document field.

aqlFieldDefault(string $key, string $doc[, string|null $keyName = null ]) : string

This helper builds a snippet suitable for inclusion in a RETURN { ... } block. It maps a document property to an object key in the AQL result:

  • $key becomes the key in the resulting AQL object.
  • $doc and optional $keyName define the field to reference.

If $keyName is omitted, the same name as $key is used.

Example usage:

aqlFieldDefault( 'name'   , 'doc' ) ; // "name: doc.name"
aqlFieldDefault( 'userId' , 'doc' , 'id'); // "userId: doc.id"
Parameters
$key : string

The key to use in the resulting AQL object (e.g. "name").

$doc : string

The document alias or variable name (e.g. "doc").

$keyName : string|null = null

Optional property name in the document; if omitted, $key is used.

Tags
since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value expression string, e.g. "name: doc.name".

aqlFieldDocument()

Generates an AQL key/value expression for a DOCUMENT-type field.

aqlFieldDocument(string $key, string $doc, array<string|int, mixed> $options[, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ]) : string

This helper handles nested document fields and can include subfields recursively.

  • If $options[Field::FIELDS] is provided as an array of subfields, it generates a nested { ... } expression using aqlFields().
  • If no subfields are defined, it falls back to a default field expression using aqlFieldDefault().

Example usage:

// Simple document field
aqlFieldDocument('author', 'doc', ['name' => 'author']);
// Produces: "author: doc.author"

// Document field with nested subfields
aqlFieldDocument( 'author', 'doc',
[
    Field::NAME   => 'author',
    Field::FIELDS =>
    [
      'firstName' => Filter::DEFAULT ,
      'lastName'  => Filter::DEFAULT ,
    ]
]);
// Produces: "author: { firstName: doc.author.firstName, lastName: doc.author.lastName }"

Guarded projection (Field::NULLABLE / Field::WHEN):

The rebuilt object is emitted unconditionally by default — when the source attribute is missing, every line of the projection reads an attribute of a nothing, which AQL resolves to null without error, and the key comes back dressed as an object of nulls ({ _key: null, url: 'https://base/things/' }) instead of null. Two opt-in markers guard it:

  • Field::NULLABLE => true — the intent « no source, no object », compiled to an IS_OBJECT() test on the source attribute;
  • Field::WHEN — the general mechanism, the same condition grammar as a scalar conditional projection (aqlFieldConditional()), compiled against the parent reference ($doc, not the sub-document), so the permission gate of conditionReadsDeniedField() applies to it verbatim.

Both compose with &&, and the false branch is Field::ELSE (default null):

aqlFieldDocument( 'thing' , 'doc' ,
[
    Field::NULLABLE => true ,
    Field::FIELDS   => [ 'name' => [] ] ,
]);
// Produces: "thing:IS_OBJECT(doc.thing) ? {name:doc.thing.name} : null"

Without either marker the emitted AQL is unchanged, byte for byte.

Parameters
$key : string

The key of the field in the parent document.

$doc : string

The document variable or reference for the field.

$options : array<string|int, mixed>

Field options, typically including:

  • Field::NAME => actual key name in the document
  • Field::FIELDS => array of nested subfields
  • Field::NULLABLE => bool, guard the rebuilt object behind IS_OBJECT(<source>)
  • Field::WHEN => optional condition guarding the projection (parent-scoped)
  • Field::ELSE => the guarded projection's false branch (default null)
$container : ContainerInterface|null = null

The optional DI Container reference.

$init : array<string|int, mixed> = []

Optional associative array definition.

Tags
throws
ContainerExceptionInterface
NotFoundExceptionInterface
UnsupportedOperationException

If a Field::WHEN descriptor is malformed.

ValidationException

If a condition or else attribute name is unsafe.

since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value expression representing the document field.

aqlFieldMap()

Generates an AQL expression for mapping an array field to structured documents.

aqlFieldMap(string $key, string $doc, array<string|int, mixed> $options[, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ]) : string

This method creates a sub-query that iterates over an array in the document and returns each element with only the specified fields.

Example output:

addresses: ( FOR item IN doc.addresses RETURN { street: item.street, city: item.city } )

With a Field::WHERE condition, a FILTER restricts the projected elements:

addresses: ( FOR item IN doc.addresses
             FILTER item.region IN @allowedRegions
             RETURN { street: item.street, city: item.city } )
Parameters
$key : string

The field key in the parent document (e.g., "addresses").

$doc : string

The document reference for AQL (e.g., "doc").

$options : array<string|int, mixed>

Field options:

  • Field::FIELDS: Array of sub-field definitions to include in each mapped item
  • Field::NAME: Optional source field name (defaults to $key)
  • Field::WHERE: Optional condition (Field::WHEN grammar) restricting the projected elements; its value may be an AqlBindReference (aqlBindRef).
$container : ContainerInterface|null = null

The optional DI Container reference.

$init : array<string|int, mixed> = []

The optional associative array definition.

Tags
throws
ContainerExceptionInterface
NotFoundExceptionInterface
ReflectionException
UnsupportedOperationException
BindException
ConstantException
ValidationException

If a Field::WHERE condition attribute name is unsafe.

since
1.0.0
author

Marc Alcaraz

Return values
string

AQL snippet with the array mapping expression.

aqlFieldNumber()

Generates an AQL key/value expression for a numeric field.

aqlFieldNumber(string $key[, string $doc = AQL::DOC ][, string|null $keyName = null ][, array<string|int, mixed> $options = [] ]) : string

This helper constructs an expression suitable for inclusion in a RETURN { ... } block, converting a document property to a numeric value using TO_NUMBER(). It ensures that non-numeric values are safely handled by AQL.

Behavior:

  • $key becomes the key in the resulting AQL object.
  • $doc is the document alias or variable.
  • $keyName optionally specifies a different property name in the document; defaults to $key.

Example usage:

// PHP call
aqlFieldInt('age');
// Generates: age: TO_NUMBER(doc.age)

aqlFieldInt('id', 'u', 'identifier');
// Generates: id: TO_NUMBER(u.identifier)

Abstaining (Field::NULLABLE):

TO_NUMBER() converts even when there is nothing to convert: a document that says nothing about the attribute comes back with 0, indistinguishable from one that really stores 0 — « it is free » and « we have no price » collapse into one value. An opt-in Field::NULLABLE guards the cast behind the presence of the attribute, and Field::ELSE picks what is said instead (default null):

aqlFieldNumber( 'price' , 'doc' , null , [ Field::NULLABLE => true ] ) ;
// price:doc.price != null ? TO_NUMBER(doc.price) : null

The test is != null, not IS_NUMBER(): TO_NUMBER() exists precisely to accept what is not a number — a document storing "42" counts as 42 today — so a type test would make all of them abstain. The question asked is only « is the attribute there? ». Without the marker the emitted AQL is unchanged, byte for byte.

Parameters
$key : string

The logical key to use in the AQL return object.

$doc : string = AQL::DOC

The document variable or alias (default: doc / AQL::DOC).

$keyName : string|null = null

Optional property name in the document if different from $key.

$options : array<string|int, mixed> = []

The field definition, read for the optional guard (Field::NULLABLE, Field::ELSE).

Tags
throws
UnsupportedOperationException

If the guard descriptor is malformed.

ValidationException

If an else attribute name is unsafe.

since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value snippet for numeric conversion (e.g. "age: TO_NUMBER(doc.age)").

aqlFieldObject()

Generates an AQL key/value expression for extracting an object or the first element of an array field.

aqlFieldObject(string $key, string $value) : string
Parameters
$key : string

Logical key to use in the resulting AQL object.

$value : string

AQL field reference to evaluate (e.g. 'doc.authors').

Tags
since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value snippet extracting the first array element.

aqlFieldTranslate()

Generates an AQL key/value expression for a translated document field.

aqlFieldTranslate(string $key[, string $doc = AQL::DOC ][, string|null $keyName = null ][, string|array<string|int, mixed>|null $lang = null ]) : string

This helper constructs an expression suitable for inclusion in a RETURN { ... } block. It returns the translated value of a field using the TRANSLATE() function if a language code is provided. Otherwise, it falls back to the original field value.

Behavior:

  • $key is used as the key in the resulting AQL object.
  • $doc is the document variable or expression containing the field.
  • $keyName allows specifying a different property name in the document; defaults to $key.
  • $lang is the optional language code for translation; if null, the original value is returned.

Note: the language is the FOURTH argument; the third is $keyName.

Example usage:

// Translate the "title" field to French (lang is the 4th argument)
aqlFieldTranslate('title', 'doc', null, 'fr');
// Produces: title:IS_OBJECT(doc.title) ? TRANSLATE("fr",doc.title,"") : null

// No translation (lang null) → original field
aqlFieldTranslate('description', 'doc');
// Produces: description:doc.description

// Different property name ($keyName) AND a language
aqlFieldTranslate('label', 'doc', 'name', 'en');
// Produces: label:IS_OBJECT(doc.name) ? TRANSLATE("en",doc.name,"") : null

Why the IS_OBJECT() guard:

TRANSLATE() looks the language up in a document, so it needs its second argument to be one. Handed anything else — an absent attribute, or a plain string left over from a pre-i18n record — it returns null and raises an AQL warning (1542, invalid argument type in call to function 'TRANSLATE()'), once per row concerned. That warning is not cosmetic: a query run with the failOnWarning cursor option fails outright (HTTP 400), so a single un-translated document could break a whole listing.

The guard asks the question first and yields null itself, which is exactly what the unguarded form returned — no projected value changes, the warning simply stops being raised. The three outcomes, measured against a real server:

Stored Projected Warning (before)
{ "fr": "Bonjour" } Bonjour
{ "en": "Hi" } ""
absent / not an object null ⚠ raised

⚠ Note the middle row, unchanged by this guard: a document that has the attribute but not the requested language yields the empty string, not null — the "" third argument of TRANSLATE(). Two shapes of « no text », two values; a consumer testing for emptiness must accept both.

Parameters
$key : string

The logical key to use in the AQL return object.

$doc : string = AQL::DOC

The document variable or alias (default: AQL::DOC).

$keyName : string|null = null

Optional property name in the document if different from $key.

$lang : string|array<string|int, mixed>|null = null

Optional language code for translation; if null, returns the original field.

Tags
since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value snippet for the translated field.

aqlFieldUrl()

Generates an AQL expression for a document URL field with optional dynamic placeholders.

aqlFieldUrl(string $key[, string $doc = AQL::DOC ][, array<string|int, mixed> $options = [] ][, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ][, string|null $urlKey = null ]) : string

This function constructs a full URL for a document field by optionally combining:

  1. A base URL retrieved from a DI container (if $container and $urlKey are provided).
  2. A path pattern ($path) that can contain placeholders in the form {param} or {param:regex}.
  3. The document key (defaulting to _key or a custom $keyName).

Placeholders in the path will be replaced by values provided in the $init array under the key Arango::ARGS. If a placeholder has no corresponding value in $init, it remains unchanged in the resulting URL.

Example URL pattern:

'/observation/{observation:[A-Za-z0-9_]+}/workspace/{workspace}/places'

Example usage:

echo aqlFieldUrl(
    key       : 'url',
    options   : [ Field::PATH => '/observation/{observation}/workspace/{workspace}/places' ],
    init      : [ Arango::ARGS => ['observation' => '15454', 'workspace' => '787878'] ],
    container : $container
);
// Returns:
// url:CONCAT('https://base.url/observation/15454/workspace/787878/places','/',doc._key)

Discriminant routing (Field::PATHS):

When Field::PATHS is provided, the path is resolved at query time from a discriminant attribute of the document (default Schema::ADDITIONAL_TYPE, overridable via Field::PROPERTY) using the AQL TRANSLATE() function. Field::PATH is then mandatory and is used as the fallback route for documents whose discriminant value is not present in the map — emitting it as the third TRANSLATE() argument guarantees an unmatched value never leaks the raw discriminant into the URL.

echo aqlFieldUrl(
    key     : 'url',
    options :
    [
        Field::PATH     => '/thing' ,                                  // fallback (mandatory with PATHS)
        Field::PATHS    => [ 'Place' => '/places' , 'Person' => '/people' ] ,
        Field::PROPERTY => Schema::ADDITIONAL_TYPE ,                   // optional discriminant
    ],
    container : $container
);
// Returns:
// url:CONCAT(TRANSLATE(doc.additionalType,{Place:'https://base.url/places',Person:'https://base.url/people'},'https://base.url/thing'),'/',doc._key)

Abstaining (Field::WHEN):

AQL drops null arguments from a CONCAT(), so a document with no key does not yield a null url — it yields a truncated link leading nowhere (https://base/places/). An opt-in Field::WHEN guards the whole expression, so the field can abstain instead:

echo aqlFieldUrl(
    key     : 'url',
    options : [ Field::PATH => '/things' , Field::WHEN => [ '_key' ] ]
);
// Returns:
// url:TO_BOOL(doc._key) ? CONCAT('/things','/',doc._key) : null

The condition uses the buildWhenCondition() grammar and is compiled against $doc — the reference the projection itself reads from, which inside a sub-document projection is the sub-document. A one-element leaf ([ '_key' ]) is a truthiness test, so an absent and an empty key both abstain. Field::ELSE sets the fallback branch (default null). Without the marker the emitted AQL is unchanged, byte for byte.

Field::NULLABLE is not the marker to reach for here: it tests that the source is an object, which a document key never is. aqlFields() rejects it on every filter but Filter::DOCUMENT, and that refusal is the single seat of the rule.

Parameters
$key : string

The field key in the parent document (e.g., 'url').

$doc : string = AQL::DOC

The document reference for AQL (default: AQL::DOC).

$options : array<string|int, mixed> = []

The field definition. Recognised keys: Field::PATH (URL path pattern / fallback route), Field::PATHS (discriminant → route map), Field::PROPERTY (discriminant attribute, default Schema::ADDITIONAL_TYPE), Field::NAME (document key name to append, default _key) and Field::WHEN / Field::ELSE (the conditional guard, see above).

$container : ContainerInterface|null = null

Optional DI container to fetch the base URL.

$init : array<string|int, mixed> = []

Optional initialization array. Use Arango::ARGS to provide an associative array of placeholder values.

$urlKey : string|null = null

Optional container key for the base URL (default: Arango::BASE_URL).

Tags
throws
ValidationException

If the discriminant attribute name is unsafe.

ContainerExceptionInterface

If fetching the base URL from the container fails.

NotFoundExceptionInterface

If the container does not contain the requested base URL.

UnsupportedOperationException

If the base URL retrieved from the container is not a string, or if Field::PATHS is malformed (empty / not an associative map) or used without an explicit Field::PATH fallback.

since
1.0.0
author

Marc Alcaraz

Return values
string

Returns an AQL snippet mapping the field to a full URL expression, e.g.: url:CONCAT('fullUrl','/',doc._key).

aqlFieldWrap()

Generates an AQL key/value expression that wraps the **current reference** itself under a named key.

aqlFieldWrap(string $key, string $ref, array<string|int, mixed> $options[, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ]) : string

Unlike aqlFieldDocument() — which nests a sub-attribute of the reference (ref.key.subfield) — this helper projects the sub-fields directly against $ref (ref.subfield), so the whole reference is embedded inside a new object under $key. It is the symmetric counterpart of Field::SCOPE : inside an edge traversal it lets a definition hoist the traversal vertex (the related entity) under a named key — e.g. subject — alongside the edge metadata, instead of flattening the vertex fields at the root.

  • With Field::FIELDS : projects the listed sub-fields against $ref and wraps them in a { ... } object under $key.
  • Without Field::FIELDS : a field whitelist is required by default. Pass Field::RAW => true to deliberately embed the whole reference as-is (key: ref) — every attribute of the vertex/edge, no projection.

The wrapped reference may also carry its own relations. The sub-fields can include the usual relation markers (Filter::EDGE / Filter::EDGES / Filter::EDGES_COUNT for edges, Filter::JOIN / Filter::JOINS for joins) and the field declares a companion Field::EDGES / Field::JOINS map of sub-relations that start from the wrapped vertex — exactly the same shape as a top-level projection (fields markers beside an edges/joins registry). The backing LET variables are emitted upstream by buildVariables() with $ref as the traversal root, so the related entities nest inside the wrapped object (e.g. subject.worksFor) in a single query. Field::RAW is mutually exclusive with Field::EDGES / Field::JOINS (a verbatim reference has no projected object to nest into).

Example usage:

// Wrap the traversal vertex under "subject", with a projection
aqlFieldWrap( 'subject', 'v',
[
    Field::FIELDS =>
    [
        'id'        => Filter::DEFAULT ,
        'givenName' => Filter::DEFAULT ,
    ]
]);
// Produces: "subject: { id: v.id, givenName: v.givenName }"

// Wrap the vertex AND nest one of its relations under the wrapped key.
// The sub-edge declares the cardinality marker in Field::FIELDS and the
// traversal definition in Field::EDGES (here reached INBOUND).
aqlFieldWrap( 'subject', 'v',
[
    Field::FIELDS =>
    [
        'id'       => Filter::DEFAULT ,
        'name'     => Filter::DEFAULT ,
        'worksFor' => [ Field::FILTER => Filter::EDGE , Field::UNIQUE => 'worksFor_e1' ] ,
    ] ,
    Field::EDGES =>
    [
        'worksFor' => [ AQL::MODEL => OrgHasMember::class , AQL::DIRECTION => Traversal::INBOUND ] ,
    ] ,
]);
// Produces (the worksFor_e1 LET being emitted upstream against `v`):
// "subject: { id: v.id, name: v.name, worksFor: (IS_OBJECT(worksFor_e1) ? … ) }"

// Wrap the whole vertex as-is (opt-in)
aqlFieldWrap( 'subject', 'v', [ Field::RAW => true ] );
// Produces: "subject: v"
Parameters
$key : string

The output key under which the reference is wrapped.

$ref : string

The reference to wrap (the traversal vertex v by default, or the edge e when the field declares Field::SCOPE => Scope::EDGE).

$options : array<string|int, mixed>

Field options, typically including:

  • Field::FIELDS => array of sub-fields projected against $ref (may include relation markers)
  • Field::EDGES => array of sub-traversal definitions starting from $ref, nested under $key
  • Field::JOINS => array of sub-join definitions resolved from $ref, nested under $key
  • Field::RAW => bool, embed the whole reference when no sub-fields are given (excludes Field::EDGES / Field::JOINS)
$container : ContainerInterface|null = null

The optional DI Container reference.

$init : array<string|int, mixed> = []

Optional associative array definition.

Tags
throws
ContainerExceptionInterface
NotFoundExceptionInterface
UnsupportedOperationException

If neither Field::FIELDS nor Field::RAW => true is provided, or if Field::RAW => true is combined with Field::EDGES / Field::JOINS.

ValidationException
since
1.0.0
author

Marc Alcaraz

Return values
string

AQL key/value expression wrapping the reference.

buildWhenCondition()

Compile a `Field::WHEN` descriptor into a boolean AQL expression.

buildWhenCondition(mixed $when[, string $doc = AQL::DOC ]) : string

Mirrors the recursive grammar of the flat ?filter= DSL (HasFilterConditions) — so a condition written for a filter reads the same here — but compiles inline (no bind variables), because the projection layer (aqlFields()) carries none.

Disambiguation:

  • string → a truthiness leaf: 'active'TO_BOOL(doc.active).
  • associative array → an explicit leaf (see buildWhenLeaf()).
  • list whose first element is a logic keyword (and / or / not) → a group over the remaining conditions; not expects exactly one condition.
  • list whose elements are all arrays → an implicit AND group.
  • list of scalars → a single leaf [ '<attr>', '<op>'?, <value> ].

Examples:

buildWhenCondition( 'active' );
// TO_BOOL(doc.active)

buildWhenCondition( [ 'visibility', 'public' ] );
// doc.visibility == 'public'

buildWhenCondition( [ [ 'a', 'x' ], [ 'b', 'gt', 0 ] ] );
// (doc.a == 'x' && doc.b > 0)

buildWhenCondition( [ 'or', [ 'role', 'admin' ], [ 'owner', 'eq', true ] ] );
// (doc.role == 'admin' || doc.owner == true)

buildWhenCondition( [ 'not', [ 'anonymized', true ] ] );
// !(doc.anonymized == true)
Parameters
$when : mixed

The condition descriptor (string, leaf, or group).

$doc : string = AQL::DOC

The document reference (default: AQL::DOC).

Tags
throws
UnsupportedOperationException

If the descriptor is malformed (empty, or a not group with the wrong arity).

ValidationException

If a leaf attribute name is unsafe.

since
1.3.0
author

Marc Alcaraz

Return values
string

The boolean AQL expression.

buildWhenLeaf()

Compile a single `Field::WHEN` condition leaf into a boolean AQL expression.

buildWhenLeaf(array<string|int, mixed> $leaf[, string $doc = AQL::DOC ]) : string

A leaf compares a document attribute against a value (or tests its truthiness). Two declaration forms are accepted:

  • Compact list[ '<attr>' ] (truthy), [ '<attr>', <value> ] (equality), [ '<attr>', '<op>', <value> ] (explicit comparator).
  • Associative[ FilterParam::KEY => '<attr>', FilterParam::OP => '<op>', FilterParam::VAL => <value>, FilterParam::ALT => <chain> ]. Without FilterParam::VAL the leaf is a truthiness test.

The compared attribute (left) and the value (right) may be wrapped by an alt chain — same "lower" / { key, val } / { key, val:true } mirror vocabulary as the flat filters (resolved by resolveAltSides()). A WHEN value is a developer-declared static literal, inlined via aqlValue(), and the attribute is validated by assertAttributeName().

Either side may instead be an AqlBindReference (built with aqlBindRef()): it renders as its @name token — never inlined, never prefixed with the document reference — and its value is supplied at query time through the top-level bind mechanism (AQL::BINDS). This is what lets Field::WHERE compare an array element against a runtime bind (e.g. item.region IN @allowedRegions).

Only the infix comparators (eq, ne, ge, gt, le, lt, in, nin, like, nlike, match, nmatch) are supported; a function-form operator (contains, sw, ew, regex, …) throws — use the flat ?filter= for those.

Parameters
$leaf : array<string|int, mixed>

The condition leaf (compact list or associative form).

$doc : string = AQL::DOC

The document reference (default: AQL::DOC).

Tags
throws
UnsupportedOperationException

If the leaf is empty or uses a non-infix operator.

ValidationException

If the attribute name is unsafe.

since
1.3.0
author

Marc Alcaraz

Return values
string

The boolean AQL expression, e.g. doc.status == 'public' or TO_BOOL(doc.active).

collectWhenAttributes()

Collects the document attribute names **read** by a {@see \oihana\arango\enums\Field::WHEN} (or {@see \oihana\arango\enums\Field::WHERE}) condition, mirroring the grammar compiled by {@see buildWhenCondition()} — a truthiness string, an explicit leaf (list `[ attr, op, val ]` or associative `[ FilterParam::KEY => …, … ]`), and the `and` / `or` / `not` / implicit-AND groups.

collectWhenAttributes(mixed $when) : array<int, string>

Only the left (attribute) side of each leaf is returned — the compared value is a bound literal and reads nothing. A left side that is not a plain attribute name (an AqlBindReference, a runtime bind) is skipped: it references no document field, so there is nothing to gate.

Used by conditionReadsDeniedField() to enforce that a conditional projection cannot read a field the caller may not read (no inference oracle through a condition).

Parameters
$when : mixed

The Field::WHEN / Field::WHERE payload.

Tags
author

Marc Alcaraz (eKameleon)

since
1.6.0
Return values
array<int, string>

The attribute names read by the condition (possibly empty).

conditionReadsDeniedField()

Decides whether a conditional projection would **read** a field the caller may not read — an inference oracle through a condition (T5). The field that *carries* the condition is already gated by {@see \oihana\arango\db\helpers\isAuthorized()}; this closes the complementary hole where the condition *reads* a masked field:

conditionReadsDeniedField(array<string|int, mixed> $options, array<string|int, mixed>|null $fields, array<string|int, mixed> $init) : bool
  • Field::WHEN — the boolean guard (price: doc.secretFlag == true ? … : …): its attributes are read at the current projection level, gated against $fields;
  • Field::ELSE — the fallback branch declaring a Field::PROPERTY (cond ? … : doc.secretAttr): a direct leak, gated against $fields;
  • Field::WHERE — the Filter::MAP element filter (FOR item … FILTER item.region == …): its attributes read the array elements, so they are gated against the map's own sub-fields (Field::FIELDS).

Fail-open, exactly like the projection: an attribute absent from the projection, carrying no Field::REQUIRES, or with no authorizer injected, is allowed — only a field explicitly gated and refused makes this return true. The caller then drops the whole conditional field (fail-closed), never emitting a partial oracle.

Parameters
$options : array<string|int, mixed>

The field definition (reads WHEN / ELSE / WHERE / FIELDS).

$fields : array<string|int, mixed>|null

The current projection map (the WHEN/ELSE context).

$init : array<string|int, mixed>

The request-level init. Reads Arango::AUTHORIZER.

Tags
author

Marc Alcaraz (eKameleon)

since
1.6.0
Return values
bool

true when a read attribute is refused (drop the field), false otherwise.

guardProjection()

Wrap an already-built **fabricated** projection behind an optional guard, so the field can yield `null` (or a `Field::ELSE` fallback) instead of a value rebuilt out of nothing.

guardProjection(string $value, array<string|int, mixed> $options, string $doc, string $source[, string|null $test = null ]) : string

Two projections fabricate rather than read, and neither fails when there is nothing to fabricate from:

  • aqlFieldDocument() reconstructs an object attribute by attribute. When the source attribute is missing, each line reads an attribute of a nothing — which AQL resolves to null without error — and the object is emitted all the same.
  • aqlFieldUrl() rebuilds a link with CONCAT(), which drops its null arguments : a missing key yields a truncated address, not a missing one.

So an empty slot comes back dressed:

{ "_key": null , "name": null , "url": "https://base/things/" }

Two markers, both opt-in, guard it:

  • Field::NULLABLE — the declared intent « nothing to build from, no value », compiled by default to an IS_OBJECT() test on $source. On a rebuilt object that is deliberately a type test and not a != null comparison: an attribute that exists but is not an object (a string, a number) rebuilds the very same object of nulls, and the house style tests the type (aqlFieldArray(), aqlFieldObject()). A caller whose shape calls for another test passes it as $testaqlFieldBool() asks for != null, because IS_BOOL() would make every document storing 1 or "yes" abstain, when TO_BOOL() exists precisely to accept them.
  • Field::WHEN — the general mechanism, sharing the condition grammar of the scalar conditional projection (buildWhenCondition()). It is compiled against $doc, the reference the projection itself reads from — the parent of a rebuilt sub-document, and the sub-document itself for a url projected inside one: that is what keeps the read gate of conditionReadsDeniedField() correct without a line of its own, since it gates a Field::WHEN against the projection of the current level.

Declared together they compose with &&. A single condition is emitted bare, a pair between parentheses — so the guard never depends on the surrounding precedence.

With neither marker the value is returned untouched: the emitted AQL of every existing projection is unchanged, byte for byte.

Parameters
$value : string

The already-built projection value (e.g. {name:doc.thing.name}).

$options : array<string|int, mixed>

The field definition (reads NULLABLE / WHEN / ELSE).

$doc : string

The parent document reference the condition and the else branch read from.

$source : string

The source attribute the projection is rebuilt from — the sub-document for a Filter::DOCUMENT (e.g. doc.thing), the appended key for a Filter::URL (e.g. doc._key). Read by Field::NULLABLE only.

$test : string|null = null

The condition Field::NULLABLE compiles to. Defaults to IS_OBJECT($source), the test a rebuilt object calls for.

Tags
throws
UnsupportedOperationException

If the Field::WHEN descriptor is malformed.

ValidationException

If a condition or else attribute name is unsafe.

example
guardProjection( '{name:doc.thing.name}' , [ Field::NULLABLE => true ] , 'doc' , 'doc.thing' ) ;
// IS_OBJECT(doc.thing) ? {name:doc.thing.name} : null

guardProjection( '{email:doc.contact.email}' , [ Field::WHEN => [ 'visibility' , 'public' ] ] , 'doc' , 'doc.contact' ) ;
// doc.visibility == 'public' ? {email:doc.contact.email} : null

guardProjection( "CONCAT('/things','/',doc._key)" , [ Field::WHEN => [ '_key' ] ] , 'doc' , 'doc._key' ) ;
// TO_BOOL(doc._key) ? CONCAT('/things','/',doc._key) : null

guardProjection( 'TO_BOOL(doc.active)' , [ Field::NULLABLE => true ] , 'doc' , 'doc.active' , 'doc.active != null' ) ;
// doc.active != null ? TO_BOOL(doc.active) : null
author

Marc Alcaraz (eKameleon)

since
1.6.0
Return values
string

The value, guarded when asked for: <cond> ? <value> : <else>.

resolveWhenElse()

Resolve the `Field::ELSE` branch of a conditional field into an AQL expression.

resolveWhenElse([mixed $else = null ][, string $doc = AQL::DOC ]) : string

Two forms:

  • Attribute reference[ Field::PROPERTY => '<attr>' ]doc.<attr> (validated by assertAttributeName()), so the fallback can mirror another document attribute.
  • Literal — anything else (a scalar, an array of scalars, or null — the default) is inlined via aqlValue().

⚠️ A string literal is quoted by aqlValue() unless it already looks like AQL — which is deliberate (it is what lets a condition compare two attributes, [ 'price', 'gt', 'doc.minPrice' ]). A few real-world literals fall into that net: 'N/A' and 'en/US' have the shape of a document handle (collection/key) and are emitted raw, which ArangoDB then reads as code and refuses (error 1203). Declare an ambiguous literal — one containing a / or a . — with betweenQuotes(), the explicit « this really is a string » form, which passes through untouched:

Field::ELSE => betweenQuotes( 'N/A' ) ,   // →  : 'N/A'
Parameters
$else : mixed = null

The Field::ELSE descriptor (default null).

$doc : string = AQL::DOC

The document reference (default: AQL::DOC).

Tags
throws
UnsupportedOperationException
ValidationException

If the referenced attribute name is unsafe.

since
1.3.0
author

Marc Alcaraz

Return values
string

The AQL expression for the else branch (null, 0, 'unknown', doc.basePrice, …).

On this page

Search results