Oihana PHP Arango

joins

Table of Contents

Functions

buildJoinSubquery()  : string
Builds the inner AQL join sub-query body — everything a join `LET` wraps, WITHOUT the enclosing `LET name = ( … )`.
buildJoinVariable()  : string
Builds a single AQL 'LET' subquery string for a specific join relation.
buildJoinVariables()  : string
Generates a string of multiple AQL 'LET' statements for all defined joins variables.
buildPolymorphicJoinVariable()  : string
Builds a single AQL 'LET' subquery for a *polymorphic* join — a join whose target collection is chosen at query time from a discriminator field of the parent document.
sortJoinVariable()  : string
Generates the internal AQL 'SORT' clause for a join variable subquery.

Functions

buildJoinSubquery()

Builds the inner AQL join sub-query body — everything a join `LET` wraps, WITHOUT the enclosing `LET name = ( … )`.

buildJoinSubquery(string|null $name[, array<string|int, mixed> $definition = [] ][, string $docRef = AQL::DOC ][, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ][, bool $isArray = false ][, array<string|int, mixed> $extraConditions = [] ][, string|null $keyPath = null ]) : string

The returned string is the compiled body:

FOR doc_join IN collection [<nested LETs>] FILTER … [SORT …] RETURN …

buildJoinVariable() wraps it into LET name = ( … ) for a regular join, while buildPolymorphicJoinVariable() wraps several such bodies into a single APPEND( ( … ) , ( … ) ) array so the collection can vary with a discriminator field.

Extracting this body from buildJoinVariable() lets a polymorphic join reuse the whole join machinery (filtering, sorting, skinning, nested edges / joins, definition-level gating) per branch. The only addition over the historical logic is $extraConditions, a list of ready-made AQL predicates (typically the discriminator guard) prepended to the branch filter.

Arango::CONDITIONS — restricting which joined documents are kept

The definition may carry extra predicates, appended to the key match. Two shapes: a plain array of AQL predicate strings, or — the useful one — a callable returning such an array. The callable receives three arguments, always:

Arango::CONDITIONS => fn( string $join , string $parent , array $init ) :array =>
    [ $join . '.active == true' ] ,
  1. $join — the generated loop variable (a randomKey, so it cannot be hardcoded);
  2. $parent — the enclosing document reference, to compare against the parent;
  3. $init — the request-level init. Contractual keys only: Arango::AUTHORIZER, AQL::SKIN and Arango::BINDS; the rest is internal.

Declare only the parameters you need. PHP discards the surplus handed to a userland callable, so a one- or two-parameter closure keeps working untouched. An object method or an invokable is accepted too.

Returning [] emits no predicate, which is how a scope stays inert outside an HTTP request (a CLI run has no authorizer): the query is then byte-for-byte the unrestricted one, and no bind is required. A non-array return raises.

⚠️ A bind referenced as text inside a predicate (… NOT IN @hidden) cannot be discovered by the optional-bind pruning of prepareAndExecute(), which looks for aqlBindRef() objects. If a skin can drop this join, name that bind explicitly in the 4th argument of prepareAndExecute(), or ArangoDB rejects the whole query.

Parameters
$name : string|null

The logical name of the join relation — used to skin the projection and to prefix the generated variable names of nested relations. Also the default parent key path when $keyPath is null.

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

Configuration array for the join — same keys as buildJoinVariable() (AQL::MODEL, AQL::FIELDS, Arango::KEY, Arango::PROPERTY, Arango::CONDITIONS, …).

$docRef : string = AQL::DOC

The AQL variable name of the main document reference.

$container : ContainerInterface|null = null

Optional DI container used to resolve models.

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

Optional associative array used for variable initialization.

$isArray : bool = false

If true, the join key is treated as an array of keys (IN).

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

Ready-made AQL predicate strings prepended to the branch filter, right after the key match (e.g. the discriminator guard of a polymorphic join).

$keyPath : string|null = null

The parent key path used to match the join, absolute from $docRef (e.g. selector.providerIddoc.selector.providerId). Null falls back on $name, keeping the historical "output name = key path" behaviour. Decoupling it from $name lets Arango::SOURCE anchor the key elsewhere and keeps nested-variable prefixes free of the (possibly dotted) key path. A polymorphic branch passes the shared key path here.

Tags
throws
Exception

If a traversal or join cannot be built properly.

ContainerExceptionInterface

If the Documents model cannot be resolved from the container.

NotFoundExceptionInterface

If the Documents model cannot be found in the container.

ReflectionException

If a nested projection or relation fails reflection.

UnexpectedValueException

If $name is empty, the model is invalid, collection not set, or CONDITIONS does not return an array.

Return values
string

The compiled join sub-query body (no LET, no enclosing parentheses).

buildJoinVariable()

Builds a single AQL 'LET' subquery string for a specific join relation.

buildJoinVariable(string|null $name[, array<string|int, mixed> $definition = [] ][, string $docRef = AQL::DOC ][, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ][, bool $isArray = false ]) : string

This method generates a complete subquery, enclosed in parentheses, which is assigned to a 'LET' variable. It handles:

  • Filtering based on the document keys or custom conditions
  • Sorting (if $isArray is true)
  • Nested edges and joins
  • Field selection and skinning

The subquery body itself is produced by buildJoinSubquery(); this wrapper only resolves the LET variable name and parenthesizes the body.

Example output:

LET myJoinVar = (
    FOR doc_join IN @@collection
        FILTER doc_join._key == doc.relatedKey
        RETURN { _key: doc_join._key, name: doc_join.name }
)
Parameters
$name : string|null

The logical name for this variable (e.g., 'friends', 'subsidiaries'). Used as the AQL 'LET' variable name.

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

Configuration array for the join. Possible keys:

  • AQL::MODEL (string) The Documents model class to query.
  • AQL::UNIQUE (string|null) Optional AQL variable name, overrides $name.
  • AQL::FIELDS (array|null) Array of fields to include in the result.
  • AQL::EDGES (array) Array of nested edge definitions.
  • AQL::JOINS (array) Array of nested join definitions.
  • AQL::SKIN (string|null) Optional 'skin' name for field selection.
  • Arango::KEY (string) The key property of the document to match (default Schema::_KEY).
  • Arango::SOURCE (string|null) Optional absolute key path, read from the main document, that anchors the join match (e.g. selector.providerIddoc.selector.providerId). Decoupled from the output field name; defaults to $name.
  • Arango::PROPERTY (string|array|null) Optional property appended, relative to the key path, to the join key.
  • Arango::SORT (string|array|null) Optional sort definition when $isArray is true.
  • Arango::CONDITIONS (callable|array|null) Optional filter conditions: - If array, it must be a list of AQL filter expressions. - If callable, it receives one or two arguments: 1. $docJoin (string) – the join document variable name 2. $docRef (string, optional) – the main document variable name - Must return an array of AQL filter expressions.
$docRef : string = AQL::DOC

The AQL variable name of the main document reference (default 'doc').

$container : ContainerInterface|null = null

Optional DI container instance used to resolve models.

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

Optional associative array used for variable initialization in nested joins.

$isArray : bool = false

If true, the join key is treated as an array of keys, generating an IN filter.

Tags
throws
Exception

If a traversal or join cannot be built properly.

ContainerExceptionInterface

If the Documents model cannot be resolved from the container.

NotFoundExceptionInterface

If the Documents model cannot be found in the container.

ReflectionException

If a callable conditions closure fails reflection.

UnexpectedValueException

If $name is empty, the model is invalid, collection not set, or CONDITIONS does not return an array.

Return values
string

The complete AQL 'LET' statement.

buildJoinVariables()

Generates a string of multiple AQL 'LET' statements for all defined joins variables.

buildJoinVariables([array<string|int, mixed> &$variables = [] ][, array<string|int, mixed> $definitions = [] ][, string $docRef = AQL::DOC ][, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ]) : string

Definition-level gating: a definition declaring AQL::REQUIRES that the request-scoped authorizer denies is skipped — its LET is not emitted.

Parameters
$variables : array<string|int, mixed> = []
$definitions : array<string|int, mixed> = []
$docRef : string = AQL::DOC
$container : ContainerInterface|null = null
$init : array<string|int, mixed> = []
Tags
throws
ContainerExceptionInterface
NotFoundExceptionInterface
ReflectionException
Return values
string

buildPolymorphicJoinVariable()

Builds a single AQL 'LET' subquery for a *polymorphic* join — a join whose target collection is chosen at query time from a discriminator field of the parent document.

buildPolymorphicJoinVariable(string|null $name[, array<string|int, mixed> $definition = [] ][, string $docRef = AQL::DOC ][, ContainerInterface|null $container = null ][, array<string|int, mixed> $init = [] ][, bool $isArray = false ]) : string

The whole branch machinery (guarding, per-branch permission gating, the FALLBACK branch, the APPEND combine) lives in the shared buildPolymorphicRelationVariable(); this function only resolves the join-specific shared defaults — the parent key path (Arango::PROPERTY, defaulting to $name) and the foreign key attribute (Arango::KEY) — and supplies the per-branch builder that wraps buildJoinSubquery().

The resulting LET always holds an array — exactly like a regular join — so the projection layer (aqlFieldObject()) unwraps it with FIRST() for a Filter::JOIN or keeps the whole array for a Filter::JOINS.

Example output (single Filter::JOIN, two branches):

LET area = APPEND(
  ( FOR doc_join IN warehouses
      FILTER doc_join._key == doc.selector.areaServed
         && doc.selector.areaScope == "…#Warehouse"
      RETURN { _key: doc_join._key, name: doc_join.name } ) ,
  ( FOR doc_join IN subsidiaries
      FILTER doc_join._key == doc.selector.areaServed
         && doc.selector.areaScope == "…#Company"
      RETURN { _key: doc_join._key, name: doc_join.name } )
)
Parameters
$name : string|null

The join field name — also the default LET variable name and the default parent key path.

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

The polymorphic join definition. Keys:

  • Arango::DISCRIMINATOR (string) Parent field path deciding the branch (required).
  • Arango::MAP (array) Non-empty type => join-definition table (required).
  • Arango::PROPERTY (string|null) Shared parent key path (default: $name).
  • Arango::KEY (string|null) Shared foreign key attribute (default per branch: _key).
  • Arango::UNIQUE (string|null) Optional LET variable name, overrides $name.
  • Arango::FALLBACK (array|null) Join definition for unmatched discriminator values (null = none).
$docRef : string = AQL::DOC

The AQL variable name of the main document reference.

$container : ContainerInterface|null = null

Optional DI container used to resolve branch models.

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

Optional associative array used for variable initialization.

$isArray : bool = false

If true, each branch matches an array of keys (IN).

Tags
throws
Exception

If a branch sub-query cannot be built.

ContainerExceptionInterface

If a branch model cannot be resolved from the container.

NotFoundExceptionInterface

If a branch model cannot be found in the container.

ReflectionException

If a callable conditions closure fails reflection.

UnexpectedValueException

If $name is empty, or the definition lacks a non-empty Arango::MAP / Arango::DISCRIMINATOR.

since
1.0.0
author

Marc Alcaraz

Return values
string

The complete AQL 'LET' statement.

sortJoinVariable()

Generates the internal AQL 'SORT' clause for a join variable subquery.

sortJoinVariable(array<string|int, mixed>|string|null $definition[, string $docRef = AQL::DOC_JOIN ][, string $defaultProperty = Schema::_KEY ]) : string

This helper interprets a flexible sort definition and constructs the appropriate AQL 'SORT' expression. Unlike an edge relation, a join has a single document variable ($docRef), so both the explicit sort property and the default fallback target that same reference.

  • If $definition is an array: looks for AQL::SORT (property) and AQL::ORDER (ASC/DESC) and sorts by that property on the join document.
  • If $definition is a string (legacy): sorts by that string as the property on the join document in ASC order.
  • If $definition is null (or AQL::SORT is not set in the array): sorts by the $defaultProperty (e.g. '_key') on the join document in DESC order.
Parameters
$definition : array<string|int, mixed>|string|null

The sort configuration. Typically the $definition array from buildJoinVariable.

$docRef : string = AQL::DOC_JOIN

The internal AQL join-document variable reference (default: 'doc_join').

$defaultProperty : string = Schema::_KEY

The fallback property to sort by (default: '_key').

Tags
example
// Case 1: default sort (null definition) — '_key' DESC on the join document.
echo sortJoinVariable( null , 'doc_join' );
// Output: "SORT doc_join._key DESC"

// Case 2: legacy string sort — 'name' ASC on the join document.
echo sortJoinVariable( 'name' , 'doc_join' );
// Output: "SORT doc_join.name ASC"

// Case 3: array definition (DESC).
echo sortJoinVariable( [ AQL::SORT => 'age' , AQL::ORDER => Order::DESC ] , 'doc_join' );
// Output: "SORT doc_join.age DESC"

// Case 4: array definition without 'sort' key — falls back to default (Case 1).
echo sortJoinVariable( [ AQL::ORDER => Order::DESC ] , 'doc_join' );
// Output: "SORT doc_join._key DESC"
Return values
string

The generated AQL 'SORT' clause.

On this page

Search results