Oihana PHP Arango

AQLQueryTrait uses trait:short, trait:short, trait:short, trait:short, trait:short, trait:short, trait:short, trait:short, trait:short

This trait defines all facet helpers in the Model class.

Table of Contents

Constants

BOUNDS  : string = 'bounds'
The 'bounds' parameter constant.
FACETS  : string = 'facets'
The 'facets' parameter constant.
FILTERS  : string = 'filters'
The 'filters' parameter constant.
SEARCHABLE  : string = 'searchable'
The 'searchable' parameter key.

Properties

$activable  : bool
$bounds  : array<string|int, mixed>|null
The bounds whitelist (`field => true | definition`), or `null` when nothing is boundable.
$facets  : array<string|int, mixed>|null
The facet settings.
$filters  : array<string|int, mixed>|null
Defines all valid filtering conditions for queries used in the list() and count() methods.
$groupable  : array<string, string>|null
Optional whitelist/mapping of groupable dimensions: `urlKey => fieldPath`.
$searchable  : array<string|int, mixed>|null
The searchable fields swept by the classic `?search=` `LIKE` (see {@see prepareSearch()}). A list of field names; an entry may instead be an array carrying its name under {@see Search::KEY} plus options such as {@see Search::REQUIRES} to gate the field by permission:
$searchOperator  : string
The default operator combining the words of a `?search=` term **within a field** during the classic `LIKE` sweep ({@see prepareSearch()}): {@see \oihana\arango\db\enums\Logic::AND} (every word must be found in the field, order-independent) or {@see \oihana\arango\db\enums\Logic::OR} (the whole term matched as one substring — the default, backward-compatible).
$searchSeparators  : string
The extra characters (beyond whitespace) that split a `?search=` term into words for the `AND` operator of the classic `LIKE` sweep ({@see prepareSearch()}).
$sortable  : array<string|int, mixed>|null
The collection (map) of all the sortable fields.
$view  : array<string|int, mixed>|null
The model-level ArangoSearch declaration (`AQL::VIEW` block, see {@see Search}).

Methods

bind()  : string
Bind a value to an AQL query variable.
bindCollection()  : string
Bind a collection name to an AQL query variable.
bindView()  : string
Bind the model's declared View name (`AQL::VIEW` block, {@see Search::NAME}) to an AQL query variable — collection bind parameters (`@@view`) are valid for View names as well.
getViewLinks()  : array<string, ArangoSearchLink>
Returns the desired per-collection link map of the declared View — the model's collection linked with {@see buildViewLink()} — or an empty map when the model has no collection.
getViewName()  : string|null
Returns the name of the declared View ({@see Search::NAME} of the `AQL::VIEW` block), or `null` when the model declares none.
hasViewSearch()  : bool
Indicates whether the View search is active: the model declares a named View (`AQL::VIEW` block) with at least one searched field, **and** the request carries a non-empty search term.
initializeActivable()  : static
Initialize the activable flag to check if the documents are 'active' or not.
initializeBounds()  : static
Initialize the 'bounds' property.
initializeFacets()  : static
Initialize the 'facets' property.
initializeFilters()  : static
Initialize the 'filters' property.
initializeGroupable()  : static
Initializes the {@see GroupTrait::$groupable} whitelist from the model options.
initializeSearchable()  : static
Initialize the 'searchable' property.
initializeSearchOperator()  : static
Initialize the `LIKE` sweep operator ({@see $searchOperator}) from the `Search::OPERATOR` init key. An explicit value is normalized to {@see Logic::AND} / {@see Logic::OR}; an absent key keeps the default ({@see Logic::OR}, backward-compatible).
initializeSearchSeparators()  : static
Initialize the `LIKE` sweep word separators ({@see $searchSeparators}) from the `Search::SEPARATORS` init key. Accepts a string of characters or a list of characters (joined to a string); an absent key keeps the default (the hyphen). Only meaningful with the `AND` operator. See {@see Search::SEPARATORS}.
initializeSortable()  : $this
Initialize the sortable array definition.
initializeView()  : static
Initialize the model-level ArangoSearch declaration (`AQL::VIEW` block) and lazily provision the View, mirroring the collection provisioning of `initializeCollection()`: when the model is lazy and the declared View does not exist, it is created from the declaration — the searched fields (dotted paths supported) are linked on the model's collection with the declared {@see Search::ANALYZER}. An existing View is never altered — inspect and resynchronize explicitly with {@see viewDiff()} / {@see viewSync()} (or the `views` action of the `arangodb` command).
prepareActive()  : string|null
Prepare the 'active' variable.
prepareCollect()  : array<string|int, mixed>
Resolves the `COLLECT` spec for a list query.
prepareFilter()  : string|null
Prepare the AQL query filtering with specific definitions.
prepareGroupSort()  : string|null
Builds the `SORT` clause applied to a grouped result, from {@see Group::SORT}.
prepareLimit()  : string|null
prepareSearch()  : string|null
Prepare the searchable AQL conditions — the classic `?search=` `LIKE` sweep, a parenthesized `OR` of case-insensitive `LIKE()` predicates over every (permitted) searchable field, used when no View search is active.
prepareSort()  : string|null
Prepare the AQL `SORT` expression from the `?sort=` grammar and, optionally, the `?near=` anchor.
prepareViewSearch()  : string|null
Prepare the relevance-ranked `SEARCH` expression of an active View search, or `null` when the View search is inactive (no `AQL::VIEW` declaration, no searched fields, or no search term) — the caller then falls back to the classic `LIKE` sweep of {@see prepareSearch()}.
viewDiff()  : DiffReport
Compares the model's View declaration with the server state and reports the differences, without touching anything.
viewSync()  : DiffReport
Reconciles the model's View with its declaration: creates it when missing, repairs a drift with `updateProperties()` (the View stays available while the inverted index rebuilds in the background), and leaves {@see DiffStatus::IN_SYNC}, {@see DiffStatus::INVALID} or {@see DiffStatus::UNREACHABLE} reports untouched.
alterFilterKey()  : string
Apply the key-side (left) `alt` transformation to a key expression.
buildViewLink()  : ArangoSearchLink
Builds the View link of the model's collection from the `AQL::VIEW` declaration: every searched field (dotted paths become nested fields) is indexed with its resolved Analyzer — the per-field {@see Search::ANALYZER} when declared, otherwise the View-level one.
buildViewSearchGroups()  : array<string, array<string|int, string>>
Builds the per-Analyzer expression groups of the View search under the historical `OR` grammar : each comma-separated term is bound whole and matched in one shot per field (`doc.<field> IN TOKENS(@search_N, …)`, whose `IN` semantics already `OR` the term's tokens), plus the optional phrase and fuzzy branches. This is the exact grammar emitted when no field opts into {@see Search::OPERATOR} `AND` — see {@see prepareViewSearch()}.
buildViewSearchGroupsWithOperator()  : array<string, array<string|int, string>>
Builds the per-Analyzer expression groups when at least one field opts into {@see Search::OPERATOR} `AND`. Each comma-separated term is split into words (whitespace) ; a field then combines those words with its resolved operator :
getSearchableSpecs()  : array<string, array<string, mixed>>
Normalizes the model's `AQL::SEARCHABLE` list into a per-field specification map `field => [ (Search::REQUIRES => …)? ]`, the single source consumed by {@see prepareSearch()} (the `LIKE` sweep) and by the `searchable` fallback of {@see getViewFieldSpecs()}.
getViewFieldSpecs()  : array<string, array<string, mixed>>
Normalizes the searched fields of the `AQL::VIEW` declaration into a per-field specification map `field => [ Search::BOOST => float, … ]` — the single source of truth from which {@see getViewSearchFields()} derives the boost map and {@see prepareViewSearch()} resolves the per-field options.
getViewSearchFields()  : array<string, float>
Normalizes the searched fields into a `field => boost` map — a boost-only façade over {@see getViewFieldSpecs()}, used by {@see buildViewLink()} and {@see viewDiff()} which only care about the field paths and their weights.
ngramMatch()  : string
Builds the `NGRAM_MATCH(doc.<field>, @term [, threshold], "<analyzer>")` core of a {@see Search::NGRAM} branch (without the surrounding boost). The threshold is inlined only when the field declares one, else the server default applies. Shared by the whole-term and the per-word emissions.
prepareFacets()  : string|null
Prepare the query with AQL facets definitions.
prepareFilterBetween()  : string
Prepares an inclusive `between` (range) clause: `key >= @min && key <= @max`.
prepareFilterComparator()  : string
Prepares the filter clause with a specific operator.
prepareFilterKey()  : string
Prepares the filter clause with a specific key and document, with optional function transformations via 'alt' parameter.
prepareFilterValue()  : string
Prepare the filter clause with a specific value to evaluates.
prepareNear()  : string|null
Build the `DISTANCE(...)` expression for a `?near=` anchor and bind its coordinates.
pushWholeTermField()  : void
Pushes the whole-term match of one field onto the Analyzer groups — the base `doc.<field> IN TOKENS(@term, …)` (boosted when the field weight is not `1`), the optional exact-phrase bonus and the optional fuzzy branch, plus the {@see Search::NGRAM} branch when declared. This is the `OR`-grammar emission, shared by {@see buildViewSearchGroups()} (every field) and by the `OR` fields of {@see buildViewSearchGroupsWithOperator()}.
resolveFieldSearchSpec()  : array{0: string[], 1: float, 2: int, 3: bool, 4: string}
Resolves the per-field search facets shared by both group builders : the Analyzers indexing the field (a per-field {@see Search::ANALYZER} overrides the View-level one, a single Analyzer normalized to a one-element list), the boost, the fuzzy tolerance and phrase bonus (a per-field value overrides the View-level default), and the `doc.<field>` accessor (with the `[*]` array marker stripped — the flat path already matches any element of the indexed array, and the `SEARCH` grammar rejects the expansion form).
splitSearchTermWords()  : array<string|int, string>
Splits an `AND`-operator search term into its words, on whitespace plus the given extra separator characters ({@see Search::SEPARATORS}). Whitespace always splits; `$separators` adds literal characters (a string, or a list of characters joined to one), `null` falls back to the hyphen default, and an empty value splits on whitespace only. The extra characters are regex-escaped, so any punctuation is safe. Empty words are dropped.
authorizeSortKey()  : string|null
Resolve a whitelisted sort entry to its AQL field expression, gated by permission.
collectAggregate()  : array<string|int, mixed>
Builds the `AQL::AGGREGATE` map from {@see Group::AGG}.
collectAssign()  : array<string|int, mixed>
Builds the `AQL::ASSIGN` map from {@see Group::BY} and {@see Group::ALT}.
collectMatchFields()  : array<int, string>
Collects the field names referenced by a `match` payload, for the permission gate. Mirrors the shape read by {@see \oihana\arango\db\helpers\buildCombinedInlineFilter()}: the explicit `all` / `any` / `none` lists expose their field under `key`, while the simple object form (`{ propertyID: …, value: … }`) uses its keys directly.
isSortAuthorized()  : bool
Decide whether a sort/near field is granted for the request.
normalizeAggregate()  : array{0: ?string, 1: ?string}
Normalizes an aggregate definition into a `[ code, field ]` pair.
normalizeGroupFields()  : array<string, string>
Normalizes {@see Group::BY} into a `[ varName => field ]` map.
resolveSortEntry()  : array{0: mixed, 1: mixed}
Resolve a whitelisted sort entry to its `[ fieldPath, requires ]` pair.

Constants

BOUNDS

The 'bounds' parameter constant.

public string BOUNDS = 'bounds'

FACETS

The 'facets' parameter constant.

public string FACETS = 'facets'

FILTERS

The 'filters' parameter constant.

public string FILTERS = 'filters'

SEARCHABLE

The 'searchable' parameter key.

public string SEARCHABLE = 'searchable'

Properties

$bounds

The bounds whitelist (`field => true | definition`), or `null` when nothing is boundable.

public array<string|int, mixed>|null $bounds = []

$facets

The facet settings.

public array<string|int, mixed>|null $facets = []

$filters

Defines all valid filtering conditions for queries used in the list() and count() methods.

public array<string|int, mixed>|null $filters = []

$groupable

Optional whitelist/mapping of groupable dimensions: `urlKey => fieldPath`.

public array<string, string>|null $groupable = null

When set, only whitelisted Group::BY keys are allowed and each resolves to its real field path (decoupling the public group key from the internal attribute, like SortTrait::$sortable). The gate is fail-closed: null (no whitelist) means nothing is groupable — a client key never reaches doc.<key>. A whitelisted dimension is further permission-gated (Field::REQUIRES inherited from the projection), so a field hidden from reading cannot be grouped on (no group-by oracle).

$searchable

The searchable fields swept by the classic `?search=` `LIKE` (see {@see prepareSearch()}). A list of field names; an entry may instead be an array carrying its name under {@see Search::KEY} plus options such as {@see Search::REQUIRES} to gate the field by permission:

public array<string|int, mixed>|null $searchable = []
AQL::SEARCHABLE =>
[
    'name' ,                                                       // public
    [ Search::KEY => 'salary' , Search::REQUIRES => 'hr:salary' ], // gated
]

$searchOperator

The default operator combining the words of a `?search=` term **within a field** during the classic `LIKE` sweep ({@see prepareSearch()}): {@see \oihana\arango\db\enums\Logic::AND} (every word must be found in the field, order-independent) or {@see \oihana\arango\db\enums\Logic::OR} (the whole term matched as one substring — the default, backward-compatible).

public string $searchOperator = \oihana\arango\db\enums\Logic::OR

Set once per model through the Search::OPERATOR init key. The View search carries its own operator inside the AQL::VIEW block (per field), see Search::OPERATOR.

$searchSeparators

The extra characters (beyond whitespace) that split a `?search=` term into words for the `AND` operator of the classic `LIKE` sweep ({@see prepareSearch()}).

public string $searchSeparators = \oihana\enums\Char::HYPHEN

A string of characters; set once per model through the Search::SEPARATORS init key (a list of characters is normalized to a string at init). The default is the hyphen (-) so « Jean-Marc » splits like « Jean Marc »; an empty string keeps hyphenated codes whole. Only used in AND mode. See Search::SEPARATORS.

$sortable

The collection (map) of all the sortable fields.

public array<string|int, mixed>|null $sortable = null

$view

The model-level ArangoSearch declaration (`AQL::VIEW` block, see {@see Search}).

public array<string|int, mixed>|null $view = null

When present (with a Search::NAME), the ?search= parameter switches from the simple LIKE sweep to an index-accelerated, relevance-ranked SEARCH against the declared View.

Methods

bind()

Bind a value to an AQL query variable.

public bind(mixed $value[, array<string|int, mixed> &$binds = [] ][, string|null $to = null ]) : string
Parameters
$value : mixed

The value to bind to the query.

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

Reference to the array of existing bind variables.

$to : string|null = null

Optional name of the bind variable. If null, a unique name is generated.

Tags
throws
BindException

If the provided bind variable name is invalid.

Return values
string

The formatted bind variable (including the "@" prefix as needed) for use in the query.

bindCollection()

Bind a collection name to an AQL query variable.

public bindCollection([array<string|int, mixed> &$binds = [] ][, array<string|int, mixed> $init = [] ]) : string

Prepares a bind variable for a collection name. Uses the collection defined in $init or falls back to $this->collection if none is provided.

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

Reference to the array of existing bind variables. If null, a new array is used.

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

Optional initialization array with keys:

  • Arango::COLLECTION => the collection name to bind
  • Arango::NAME => optional bind variable name
Tags
throws
BindException

If the bind variable name is invalid.

Return values
string

The formatted bind variable representing the collection.

bindView()

Bind the model's declared View name (`AQL::VIEW` block, {@see Search::NAME}) to an AQL query variable — collection bind parameters (`@@view`) are valid for View names as well.

public bindView([array<string|int, mixed> &$binds = [] ]) : string
Parameters
$binds : array<string|int, mixed> = []

Reference to the array of existing bind variables.

Tags
throws
BindException

If the bind variable name is invalid.

Return values
string

The formatted bind variable representing the View.

Returns the desired per-collection link map of the declared View — the model's collection linked with {@see buildViewLink()} — or an empty map when the model has no collection.

public getViewLinks() : array<string, ArangoSearchLink>
throws
ValidationException
Return values
array<string, ArangoSearchLink>

getViewName()

Returns the name of the declared View ({@see Search::NAME} of the `AQL::VIEW` block), or `null` when the model declares none.

public getViewName() : string|null
Return values
string|null

hasViewSearch()

Indicates whether the View search is active: the model declares a named View (`AQL::VIEW` block) with at least one searched field, **and** the request carries a non-empty search term.

public hasViewSearch([array<string|int, mixed>|string|null $search = [] ]) : bool
Parameters
$search : array<string|int, mixed>|string|null = []

The $init array (reads Arango::SEARCH) or the search term itself.

Tags
throws
ValidationException
Return values
bool

initializeActivable()

Initialize the activable flag to check if the documents are 'active' or not.

public initializeActivable([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeBounds()

Initialize the 'bounds' property.

public initializeBounds([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeFacets()

Initialize the 'facets' property.

public initializeFacets([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeFilters()

Initialize the 'filters' property.

public initializeFilters([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeGroupable()

Initializes the {@see GroupTrait::$groupable} whitelist from the model options.

public initializeGroupable([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []

The model options (Arango::GROUPABLE).

Return values
static

initializeSearchable()

Initialize the 'searchable' property.

public initializeSearchable([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeSearchOperator()

Initialize the `LIKE` sweep operator ({@see $searchOperator}) from the `Search::OPERATOR` init key. An explicit value is normalized to {@see Logic::AND} / {@see Logic::OR}; an absent key keeps the default ({@see Logic::OR}, backward-compatible).

public initializeSearchOperator([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeSearchSeparators()

Initialize the `LIKE` sweep word separators ({@see $searchSeparators}) from the `Search::SEPARATORS` init key. Accepts a string of characters or a list of characters (joined to a string); an absent key keeps the default (the hyphen). Only meaningful with the `AND` operator. See {@see Search::SEPARATORS}.

public initializeSearchSeparators([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeSortable()

Initialize the sortable array definition.

public initializeSortable([array<string|int, mixed> $init = [] ]) : $this

The raw definition (from the AQL::SORTABLE init key, or the property default) is normalised through normalizeSortable() into the canonical urlKey => fieldPath map. Three interchangeable notations are accepted and may be mixed: the legacy associative urlKey => fieldPath, the indexed shorthand fieldName (token equals field), and the indexed alias [ urlKey => fieldPath ]. null is preserved and means fail-closed (no whitelist → nothing client sorts). The normalisation is idempotent.

Parameters
$init : array<string|int, mixed> = []
Return values
$this

initializeView()

Initialize the model-level ArangoSearch declaration (`AQL::VIEW` block) and lazily provision the View, mirroring the collection provisioning of `initializeCollection()`: when the model is lazy and the declared View does not exist, it is created from the declaration — the searched fields (dotted paths supported) are linked on the model's collection with the declared {@see Search::ANALYZER}. An existing View is never altered — inspect and resynchronize explicitly with {@see viewDiff()} / {@see viewSync()} (or the `views` action of the `arangodb` command).

public initializeView([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Tags
throws
ContainerExceptionInterface
NotFoundExceptionInterface
ValidationException
Return values
static

prepareActive()

Prepare the 'active' variable.

public prepareActive([array<string|int, mixed> $init = [] ][, array<string|int, mixed>|null &$binds = null ][, string $docRef = AQL::DOC ]) : string|null
Parameters
$init : array<string|int, mixed> = []
$binds : array<string|int, mixed>|null = null
$docRef : string = AQL::DOC
Tags
throws
BindException
Return values
string|null

prepareCollect()

Resolves the `COLLECT` spec for a list query.

public prepareCollect([array<string|int, mixed> $init = [] ][, string $docRef = AQL::DOC ]) : array<string|int, mixed>

Translates a friendly Arango::GROUP spec (Group::BY, Group::AGG, Group::COUNT, Group::ALT) into the raw aqlCollect() keys. Falls back to the raw Arango::COLLECT spec (or an empty array) when no group is requested.

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

The list query options.

$docRef : string = AQL::DOC

The document reference grouping fields are read from.

Tags
throws
UnsupportedOperationException
ValidationException
Return values
array<string|int, mixed>

The raw COLLECT spec (AQL::ASSIGN, AQL::AGGREGATE, AQL::WITH_COUNT).

prepareFilter()

Prepare the AQL query filtering with specific definitions.

public prepareFilter([array<string|int, mixed>|null $init = [] ][, array<string|int, mixed>|null &$binds = null ][, string $docRef = AQL::DOC ][, array<string|int, mixed> $auth = [] ]) : string|null
Parameters
$init : array<string|int, mixed>|null = []
$binds : array<string|int, mixed>|null = null
$docRef : string = AQL::DOC
$auth : array<string|int, mixed> = []
Tags
throws
BindException
ConstantException
ContainerExceptionInterface
DependencyException
NotFoundException
NotFoundExceptionInterface
ReflectionException
UnsupportedOperationException
ValidationException
Return values
string|null

prepareGroupSort()

Builds the `SORT` clause applied to a grouped result, from {@see Group::SORT}.

public prepareGroupSort([array<string|int, mixed> $init = [] ][, array<string|int, mixed>|null $availableVars = null ]) : string|null

The sort operates on group/aggregate variable names (never on doc, which is out of scope after COLLECT): a CSV with a leading - for descending, e.g. '-count'count DESC, 'category,-total'category ASC, total DESC.

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

The list query options.

$availableVars : array<string|int, mixed>|null = null
Return values
string|null

The inner sort expression, or null when none.

prepareLimit()

public prepareLimit([array<string|int, mixed> $init = [] ][, array<string|int, mixed>|null &$binds = null ]) : string|null
Parameters
$init : array<string|int, mixed> = []
$binds : array<string|int, mixed>|null = null
Tags
throws
BindException
Return values
string|null

prepareSearch()

Prepare the searchable AQL conditions — the classic `?search=` `LIKE` sweep, a parenthesized `OR` of case-insensitive `LIKE()` predicates over every (permitted) searchable field, used when no View search is active.

public prepareSearch([array<string|int, mixed>|string|null $search = [] ][, array<string|int, mixed>|null &$binds = null ][, array<string|int, mixed>|null $searchable = null ][, string $docRef = AQL::DOC ]) : string|null

The comma-separated terms and the fields are always OR-ed. How the words of a single term combine within a field follows the model's $searchOperator (Search::OPERATOR init key):

  • Logic::OR (default) matches the whole term as one substring (LIKE(doc.name, "%marc fourcade%")), so it needs the words adjacent and in order — the historical, byte-for-byte grammar;
  • Logic::AND splits the term on whitespace and requires every word in the same field (( LIKE(doc.name, "%marc%") && LIKE(doc.name, "%fourcade%") )), order-independent.
Parameters
$search : array<string|int, mixed>|string|null = []
$binds : array<string|int, mixed>|null = null
$searchable : array<string|int, mixed>|null = null
$docRef : string = AQL::DOC
Tags
throws
BindException
example
?search=Marc,Marco
Return values
string|null

prepareSort()

Prepare the AQL `SORT` expression from the `?sort=` grammar and, optionally, the `?near=` anchor.

public prepareSort([array<string|int, mixed> $init = [] ][, array<string|int, mixed>|null $sortable = null ][, string $docRef = AQL::DOC ][, array<string|int, mixed>|null &$binds = null ]) : string|null

Each comma-separated criterion in Arango::SORT is resolved against $sortable (URL key → AQL field path); a leading - makes it descending. The synthetic distance key (Schema::DISTANCE) is resolved from Arango::NEAR and only honored when $binds is provided (so the reference point can be bound).

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

Per-call parameters. Reads Arango::SORT (grammar) and Arango::NEAR (geo anchor).

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

URL-key → field-path whitelist. Defaults to $this->sortable.

$docRef : string = AQL::DOC

The document variable the fields hang off (default doc).

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

Bind variables, populated by reference. Required to enable distance/?near= sorting.

Tags
throws
BindException

When a bound coordinate cannot be registered.

example

Plain field sort

$model->prepareSort( [ Arango::SORT => 'name,-created' ] ) ;
// "doc.name ASC, doc.created DESC"

Distance sort (nearest first) via ?near=

$binds = [] ;
$model->prepareSort
(
    [ Arango::NEAR => [ FilterParam::KEY => 'geo' , 'latitude' => 48.85 , 'longitude' => 2.35 ] ] ,
    binds : $binds
) ;
// "DISTANCE(doc.geo.latitude, doc.geo.longitude, @lat, @lng) ASC"

Distance then name

$model->prepareSort
(
    [ Arango::SORT => 'distance,name' , Arango::NEAR => [ ... ] ] ,
    binds : $binds
) ;
// "DISTANCE(...) ASC, doc.name ASC"
Return values
string|null

The SORT body (without the SORT keyword), or an empty string when nothing sorts.

prepareViewSearch()

Prepare the relevance-ranked `SEARCH` expression of an active View search, or `null` when the View search is inactive (no `AQL::VIEW` declaration, no searched fields, or no search term) — the caller then falls back to the classic `LIKE` sweep of {@see prepareSearch()}.

public prepareViewSearch([array<string|int, mixed>|string|null $search = [] ][, array<string|int, mixed>|null &$binds = null ][, string $docRef = AQL::DOC ]) : string|null

The grammar keeps the ?search= contract (comma-separated terms, OR everywhere, values bound — never inlined). Per term and per field:

  • the base match doc.<field> IN TOKENS(@search_N, "<analyzer>") (both sides analyzed), weighted by BOOST(…, <boost>) when the field boost differs from 1; a field reaching into an array of objects has its [*] expansion marker stripped here (doc.contactPoints.email IN …, not doc.contactPoints[*].email): the SEARCH grammar rejects array expansion, and the flat path already matches any element of the indexed array — see buildViewLink();
  • with a per-field or View-level Search::PHRASE, an exact-phrase bonus BOOST(PHRASE(doc.<field>, @search_N), <boost × 2>); a field may override the View-level flag (an explicit false opts that field out);
  • with a per-field or View-level Search::FUZZY > 0, a typo-tolerant LEVENSHTEIN_MATCH(doc.<field>, @search_N, <fuzzy>); a field may override the View-level tolerance — an explicit 0 opts that field out while the rest stays fuzzy.

Field expressions are grouped by their resolved Analyzer (a field may override the View-level Search::ANALYZER) and each group is wrapped in its own ANALYZER(…, "<analyzer>"), the groups being OR-ed together. With a single Analyzer the output is a single ANALYZER(…) wrap, byte for byte the classic form.

Search::REQUIRES gates the search by the request authorizer (Arango::AUTHORIZER, see isAuthorized()) at two levels: on the AQL::VIEW block it gates the whole search, inside a field entry it gates that field; the two combine with AND (fail-open without an authorizer). If the View-level gate is denied, or permissions remove every field, the expression is false — the search matches nothing and never falls back to searching everything.

When the request carries an active language (Arango::LANG, the ?lang= parameter), localized fields (those declaring Search::LANG) join the SEARCH only when their locale matches; locale-agnostic fields always do. An active language matching no field is ignored — the SEARCH is never emptied (within the permitted set).

Parameters
$search : array<string|int, mixed>|string|null = []

The $init array (reads Arango::SEARCH) or the search term itself.

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

Bind variables, populated by reference.

$docRef : string = AQL::DOC

The document variable the fields hang off.

Tags
throws
BindException
ValidationException
Return values
string|null

The SEARCH expression, or null when the View search is inactive.

viewDiff()

Compares the model's View declaration with the server state and reports the differences, without touching anything.

public viewDiff() : DiffReport

On top of the field/analyzer drift detected by ViewManagementTrait::viewDiff(), the model-level report validates the coherence of the declaration itself: a missing Search::NAME, no searched field, no collection, an analyzer or a collection unknown to the server all resolve to DiffStatus::INVALID — such a View is never created nor synchronized automatically.

Tags
throws
ValidationException
Return values
DiffReport

viewSync()

Reconciles the model's View with its declaration: creates it when missing, repairs a drift with `updateProperties()` (the View stays available while the inverted index rebuilds in the background), and leaves {@see DiffStatus::IN_SYNC}, {@see DiffStatus::INVALID} or {@see DiffStatus::UNREACHABLE} reports untouched.

public viewSync() : DiffReport
Tags
throws
ValidationException
Return values
DiffReport

The viewDiff() report, with $applied set when the View has been created or updated.

alterFilterKey()

Apply the key-side (left) `alt` transformation to a key expression.

protected alterFilterKey(string $key[, array<string|int, mixed> $init = [] ]) : string

Thin wrapper over static::alterExpression(): it resolves the alt parameter into its key/value sides via static::resolveAltSides() and applies the key-side chain. The three legacy alt forms (string, list of functions, function-with-params) keep transforming the key only, unchanged.

Parameters
$key : string

The key expression to transform.

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

Filter initialization array containing the 'alt' parameter.

Tags
throws
UnsupportedOperationException
ValidationException
Return values
string

The transformed key expression.

Builds the View link of the model's collection from the `AQL::VIEW` declaration: every searched field (dotted paths become nested fields) is indexed with its resolved Analyzer — the per-field {@see Search::ANALYZER} when declared, otherwise the View-level one.

protected buildViewLink() : ArangoSearchLink

A field may reach a sub-field of an array of objects with the [*] expansion marker (e.g. contactPoints[*].email). The marker is stripped here (stripArrayExpansion()) so the link declares the flat nested path (contactPointsemail): ArangoSearch (Community) descends into the array on its own — no Enterprise nested flag is needed for this (non-correlated) search. The matching query strips the marker too — the SEARCH grammar rejects array expansion, see prepareViewSearch(). The stripped path is validated (assertAttributeName()) to reject a malformed declaration early.

A field whose resolved Analyzer equals the link-level default is emitted as an empty node (no analyzers key) rather than spelling the default out: the server normalizes a field whose analyzers equal the link default to } (the redundant mention is dropped), so spelling it out would make the declared form differ forever from the stored one and viewDiff() would report a permanent false drift. The link carries no link-level analyzers, so its default is the server default (identity) — computed here rather than hard-coded so the elimination stays correct if a link-level analyzer is introduced later.

throws
ValidationException
Return values
ArangoSearchLink

buildViewSearchGroups()

Builds the per-Analyzer expression groups of the View search under the historical `OR` grammar : each comma-separated term is bound whole and matched in one shot per field (`doc.<field> IN TOKENS(@search_N, …)`, whose `IN` semantics already `OR` the term's tokens), plus the optional phrase and fuzzy branches. This is the exact grammar emitted when no field opts into {@see Search::OPERATOR} `AND` — see {@see prepareViewSearch()}.

protected buildViewSearchGroups(string $search, array<string|int, mixed>|null &$binds, string $docRef, array<string|int, mixed> $fields, string $modelAnalyzer, bool $globalPhrase, int $globalFuzzy) : array<string, array<string|int, string>>
Parameters
$search : string

The raw ?search= term string (comma-separated).

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

Bind variables, populated by reference.

$docRef : string

The document variable the fields hang off.

$fields : array<string|int, mixed>

The resolved, gated per-field specs.

$modelAnalyzer : string

The View-level Analyzer (a per-field override is respected).

$globalPhrase : bool

The View-level phrase-bonus default.

$globalFuzzy : int

The View-level Levenshtein tolerance default.

Tags
throws
BindException
Return values
array<string, array<string|int, string>>

Analyzer name => list of expressions, in first-seen order.

buildViewSearchGroupsWithOperator()

Builds the per-Analyzer expression groups when at least one field opts into {@see Search::OPERATOR} `AND`. Each comma-separated term is split into words (whitespace) ; a field then combines those words with its resolved operator :

protected buildViewSearchGroupsWithOperator(string $search, array<string|int, mixed>|null &$binds, string $docRef, array<string|int, mixed> $fields, string $modelAnalyzer, bool $globalPhrase, int $globalFuzzy, string $globalOperator[, string|array<string|int, mixed>|null $separators = null ]) : array<string, array<string|int, string>>
  • OR (default) reproduces the whole-term match (pushWholeTermField()), so a mixed View keeps its loose fields loose ;
  • AND requires every word in the same field (( doc.<field> IN TOKENS(@w0, …) && doc.<field> IN TOKENS(@w1, …) )), the fuzzy branch (when enabled) widening each word (( … IN TOKENS(@w, …) || LEVENSHTEIN_MATCH(…, @w, …) )), and the phrase bonus (when enabled) kept on the whole term as a ranking boost OR-ed on top — an adjacency match implies the words, so the matched set is unchanged.

The whole-term bind is materialized only when a field needs it (an OR field, or an AND field carrying the phrase bonus), never left dangling. Groups are OR-ed between fields and between terms by prepareViewSearch().

Parameters
$search : string

The raw ?search= term string (comma-separated).

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

Bind variables, populated by reference.

$docRef : string

The document variable the fields hang off.

$fields : array<string|int, mixed>

The resolved, gated per-field specs.

$modelAnalyzer : string

The View-level Analyzer (a per-field override is respected).

$globalPhrase : bool

The View-level phrase-bonus default.

$globalFuzzy : int

The View-level Levenshtein tolerance default.

$globalOperator : string

The View-level operator default (Logic::AND / Logic::OR).

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

The extra AND word separators (Search::SEPARATORS); null = the hyphen default.

Tags
throws
BindException
Return values
array<string, array<string|int, string>>

Analyzer name => list of expressions, in first-seen order.

getSearchableSpecs()

Normalizes the model's `AQL::SEARCHABLE` list into a per-field specification map `field => [ (Search::REQUIRES => …)? ]`, the single source consumed by {@see prepareSearch()} (the `LIKE` sweep) and by the `searchable` fallback of {@see getViewFieldSpecs()}.

protected getSearchableSpecs([array<string|int, mixed>|null $searchable = null ]) : array<string, array<string, mixed>>

Each entry of the list is either:

  • a plain field name (string) → a public field, no options;
  • an array carrying the field name under Search::KEY plus its options (e.g. Search::REQUIRES) → keeps the list homogeneous (no mixed numeric/string keys). The map form field => [ … ] is also tolerated (the field falls back to the entry key).
Parameters
$searchable : array<string|int, mixed>|null = null

An explicit list overriding the model's searchable property.

Return values
array<string, array<string, mixed>>

getViewFieldSpecs()

Normalizes the searched fields of the `AQL::VIEW` declaration into a per-field specification map `field => [ Search::BOOST => float, … ]` — the single source of truth from which {@see getViewSearchFields()} derives the boost map and {@see prepareViewSearch()} resolves the per-field options.

protected getViewFieldSpecs() : array<string, array<string, mixed>>

Search::FIELDS entries accept a numeric boost shorthand or an array carrying Search::BOOST, Search::FUZZY, Search::ANALYZER, Search::LANG, Search::PHRASE and Search::REQUIRES; when the declaration has no fields, the model's searchable list is used with a neutral boost. A per-field option is kept in the spec only when it is explicitly declared — an absent key means "inherit the View-level default", which prepareViewSearch() resolves so that an explicit value (0 included) overrides the global tolerance.

Tags
throws
ValidationException
Return values
array<string, array<string, mixed>>

getViewSearchFields()

Normalizes the searched fields into a `field => boost` map — a boost-only façade over {@see getViewFieldSpecs()}, used by {@see buildViewLink()} and {@see viewDiff()} which only care about the field paths and their weights.

protected getViewSearchFields() : array<string, float>
Tags
throws
ValidationException
Return values
array<string, float>

ngramMatch()

Builds the `NGRAM_MATCH(doc.<field>, @term [, threshold], "<analyzer>")` core of a {@see Search::NGRAM} branch (without the surrounding boost). The threshold is inlined only when the field declares one, else the server default applies. Shared by the whole-term and the per-word emissions.

protected ngramMatch(array<string|int, mixed> $spec, string $path, string $term) : string
Parameters
$spec : array<string|int, mixed>

The field spec (must carry a Search::NGRAM entry).

$path : string

The doc.<field> accessor.

$term : string

The bound term (@search_N or @search_N_M).

Return values
string

The NGRAM_MATCH(…) expression.

prepareFacets()

Prepare the query with AQL facets definitions.

protected prepareFacets(array<string|int, mixed>|null $init[, array<string|int, mixed>|null &$binds = null ][, string $docRef = AQL::DOC ][, string $logicalOperator = Logic::AND ]) : string|null
Parameters
$init : array<string|int, mixed>|null
$binds : array<string|int, mixed>|null = null
$docRef : string = AQL::DOC
$logicalOperator : string = Logic::AND
Return values
string|null

prepareFilterBetween()

Prepares an inclusive `between` (range) clause: `key >= @min && key <= @max`.

protected prepareFilterBetween(array<string|int, mixed> $init, array<string|int, mixed>|null &$binds, string $docRef, callable $resolve, bool $defaultBounds) : string

The compared key is alt-aware (it flows through static::alterFilterKey()). Each bound is resolved by $resolve, which differs per filter type — a raw bind for numbers/strings, the date machinery (now / timezone) for dates.

Bound omission is type-driven:

  • $defaultBounds = false (number/string): an omitted bound drops its side, yielding a one-sided range (key >= @min or key <= @max).
  • $defaultBounds = true (date): an omitted bound still emits a clause; the resolver maps the null value to "now", so the range is always two-sided.
Parameters
$init : array<string|int, mixed>

The filter init (reads min / max).

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

The bind variables, populated by reference.

$docRef : string

The document reference.

$resolve : callable

fn(mixed $value, ?array &$binds): string — resolves a bound to AQL.

$defaultBounds : bool

Whether an omitted bound still emits a clause (dates) or is dropped.

Tags
throws
UnsupportedOperationException
ValidationException
Return values
string

prepareFilterComparator()

Prepares the filter clause with a specific operator.

protected prepareFilterComparator([array<string|int, mixed> $init = [] ]) : string
Parameters
$init : array<string|int, mixed> = []
Return values
string

prepareFilterKey()

Prepares the filter clause with a specific key and document, with optional function transformations via 'alt' parameter.

protected prepareFilterKey([string|array<string|int, mixed>|null $init = [] ][, string $docRef = AQL::DOC ]) : string

Supports function chaining:

  • Single function: "alt":"lower"
  • Multiple functions: "alt":["trim","lower"]
  • Functions with params: "alt":[["trim",1],"lower"]
Parameters
$init : string|array<string|int, mixed>|null = []

Filter initialization array

$docRef : string = AQL::DOC

Document reference (default: AQL::DOC)

Tags
throws
UnsupportedOperationException
ValidationException
example
// Simple key
prepareFilterKey(['key' => 'name'], 'doc')
// Returns: "doc.name"

// With single function
prepareFilterKey(['key' => 'name', 'alt' => 'lower'], 'doc')
// Returns: "LOWER(doc.name)"

// With function chain
prepareFilterKey(['key' => 'name', 'alt' => ['trim', 'lower']], 'doc')
// Returns: "LOWER(TRIM(doc.name))"

// With parameters
prepareFilterKey(['key' => 'code', 'alt' => [['substring', 0, 3]]], 'doc')
// Returns: "SUBSTRING(doc.code, 0, 3)"
Return values
string

The transformed key expression

prepareFilterValue()

Prepare the filter clause with a specific value to evaluates.

protected prepareFilterValue([array<string|int, mixed>|null $init = [] ][, array<string|int, mixed>|null &$binds = null ]) : string

Binds the raw value, then applies the value-side (right) alt chain when one is set (object form alt:{ key:.. , val:.. } or val:true mirror):

  • scalar value → the chain wraps the bind placeholder, e.g. LOWER(@value).
  • array value (e.g. op:in) → the chain is mapped over each element via an inline projection, e.g. @value[* RETURN LOWER(CURRENT)]. The single bind still holds the whole array, so existing binding behavior is preserved.
Parameters
$init : array<string|int, mixed>|null = []
$binds : array<string|int, mixed>|null = null
Tags
throws
BindException
UnsupportedOperationException
ValidationException
Return values
string

prepareNear()

Build the `DISTANCE(...)` expression for a `?near=` anchor and bind its coordinates.

protected prepareNear(array<string|int, mixed> $near, array<string|int, mixed>|null &$binds[, string $docRef = AQL::DOC ][, array<string|int, mixed> $init = [] ]) : string|null

The key of the payload names the geo field to order by distance from, so it is a sort dimension and travels through the same fail-closed gate as any sort key: it must be declared in $this->sortable (URL key → geo field path) and it inherits (or declares) a Field::REQUIRES permission — a geo field hidden from the projection stays untriable (no distance oracle). Returns null when the key is missing, is not whitelisted, is refused by permission, or the coordinates are incomplete.

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

The ?near= payload ({ key, latitude, longitude }), already array-checked by the caller.

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

Bind variables, populated by reference.

$docRef : string = AQL::DOC

The document variable the fields hang off.

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

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

Tags
throws
BindException

When a bound coordinate cannot be registered.

Return values
string|null

DISTANCE(doc.<field>.latitude, doc.<field>.longitude, @lat, @lng) or null.

pushWholeTermField()

Pushes the whole-term match of one field onto the Analyzer groups — the base `doc.<field> IN TOKENS(@term, …)` (boosted when the field weight is not `1`), the optional exact-phrase bonus and the optional fuzzy branch, plus the {@see Search::NGRAM} branch when declared. This is the `OR`-grammar emission, shared by {@see buildViewSearchGroups()} (every field) and by the `OR` fields of {@see buildViewSearchGroupsWithOperator()}.

protected pushWholeTermField(array<string|int, mixed> &$groups, array<string|int, mixed> $spec, string $path, string $term, array<string|int, mixed> $analyzers, float $weight, bool $phrase, int $fuzzy) : void
Parameters
$groups : array<string|int, mixed>

Analyzer name => list of expressions, mutated by reference.

$spec : array<string|int, mixed>

The resolved field spec.

$path : string

The doc.<field> accessor.

$term : string

The bound whole term (@search_N).

$analyzers : array<string|int, mixed>

The Analyzers indexing the field (IN TOKENS side).

$weight : float

The field boost.

$phrase : bool

Whether to add the exact-phrase bonus.

$fuzzy : int

The Levenshtein tolerance (0 disables the branch).

resolveFieldSearchSpec()

Resolves the per-field search facets shared by both group builders : the Analyzers indexing the field (a per-field {@see Search::ANALYZER} overrides the View-level one, a single Analyzer normalized to a one-element list), the boost, the fuzzy tolerance and phrase bonus (a per-field value overrides the View-level default), and the `doc.<field>` accessor (with the `[*]` array marker stripped — the flat path already matches any element of the indexed array, and the `SEARCH` grammar rejects the expansion form).

protected resolveFieldSearchSpec(array<string|int, mixed> $spec, int|string $field, string $modelAnalyzer, bool $globalPhrase, int $globalFuzzy, string $docRef) : array{0: string[], 1: float, 2: int, 3: bool, 4: string}
Parameters
$spec : array<string|int, mixed>

The resolved field spec.

$field : int|string

The field key (the dotted path, [*] allowed).

$modelAnalyzer : string

The View-level Analyzer.

$globalPhrase : bool

The View-level phrase-bonus default.

$globalFuzzy : int

The View-level Levenshtein tolerance default.

$docRef : string

The document variable the field hangs off.

Return values
array{0: string[], 1: float, 2: int, 3: bool, 4: string}

[ analyzers, weight, fuzzy, phrase, path ].

splitSearchTermWords()

Splits an `AND`-operator search term into its words, on whitespace plus the given extra separator characters ({@see Search::SEPARATORS}). Whitespace always splits; `$separators` adds literal characters (a string, or a list of characters joined to one), `null` falls back to the hyphen default, and an empty value splits on whitespace only. The extra characters are regex-escaped, so any punctuation is safe. Empty words are dropped.

protected splitSearchTermWords(string $term[, string|array<string|int, mixed>|null $separators = null ]) : array<string|int, string>
Parameters
$term : string

The raw comma-term (may hold several words).

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

The extra separator characters (string / list / null=default hyphen).

Return values
array<string|int, string>

The non-empty words, in order.

authorizeSortKey()

Resolve a whitelisted sort entry to its AQL field expression, gated by permission.

private authorizeSortKey(string $key, mixed $entry, array<string|int, mixed> $init, string $docRef) : string|null

The entry (the $sortable[$key] value) is either a plain field path — a string or an array path ([ 'address', 'city' ]) — or an explicit definition (an associative array carrying Field::PATH and/or Field::REQUIRES). The permission subject is resolved in two steps, aligned on the projection's Field::REQUIRES:

  • explicit — a Field::REQUIRES declared on the entry itself takes priority;
  • inherited — otherwise the subject of the homonymous field declared in $this->fields is reused, so « what you cannot read, you cannot sort on ».

When a subject is resolved and isAuthorized() denies it, the key is refused (null) and the caller drops the criterion — a field hidden from the projection stays untriable (no sort oracle). No subject, or no authorizer injected, sorts freely (fail-open — exactly the field-level semantics).

Parameters
$key : string

The public URL key (already resolved against the whitelist).

$entry : mixed

The $sortable[$key] value (path or explicit definition).

$init : array<string|int, mixed>

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

$docRef : string

The document variable the field hangs off.

Return values
string|null

The doc.<field> expression, or null when the sort is refused.

collectAggregate()

Builds the `AQL::AGGREGATE` map from {@see Group::AGG}.

private collectAggregate(array<string|int, mixed> $group, string $docRef[, array<string|int, mixed> $init = [] ]) : array<string|int, mixed>
Parameters
$group : array<string|int, mixed>

The group spec.

$docRef : string

The document reference.

$init : array<string|int, mixed> = []
Tags
throws
ValidationException
Return values
array<string|int, mixed>

[ outName => 'FN(doc.field)' ].

collectAssign()

Builds the `AQL::ASSIGN` map from {@see Group::BY} and {@see Group::ALT}.

private collectAssign(array<string|int, mixed> $group, string $docRef[, array<string|int, mixed> $init = [] ]) : array<string|int, mixed>
Parameters
$group : array<string|int, mixed>

The group spec.

$docRef : string

The document reference.

$init : array<string|int, mixed> = []
Tags
throws
UnsupportedOperationException
ValidationException
Return values
array<string|int, mixed>

[ varName => 'doc.field' | 'FN(doc.field)' ].

collectMatchFields()

Collects the field names referenced by a `match` payload, for the permission gate. Mirrors the shape read by {@see \oihana\arango\db\helpers\buildCombinedInlineFilter()}: the explicit `all` / `any` / `none` lists expose their field under `key`, while the simple object form (`{ propertyID: …, value: … }`) uses its keys directly.

private collectMatchFields(array<string|int, mixed> $match) : array<int, string>
Parameters
$match : array<string|int, mixed>

The match payload (already validated as an array by the caller).

Return values
array<int, string>

The referenced sub-field names (possibly empty).

isSortAuthorized()

Decide whether a sort/near field is granted for the request.

private isSortAuthorized(string $path, mixed $requires, array<string|int, mixed> $init) : bool

Two paths, mirroring the projection's own gating:

  • explicit (Façon A) — an entry that declared its own Field::REQUIRES is run through isAuthorized();
  • inherited (Façon B) — otherwise the Field::REQUIRES is inherited from the projection at the resolved $path via isPathAuthorized(), which descends Field::FIELDS / AQL::SKIN_FIELDS and strips [*], so a dotted/aliased path (address.salary) is gated at its exact sub-field — never at the homonym of the URL key.

Both fail open: no explicit subject with a projection that carries no Field::REQUIRES on the path (or no authorizer injected) sorts freely — exactly the field-level semantics, symmetric with ?filter=.

Parameters
$path : string

The resolved field path (address.salary, location.point, …).

$requires : mixed

The explicit Field::REQUIRES subject(s) declared on the entry, or null.

$init : array<string|int, mixed>

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

Return values
bool

true when the field may be sorted on, false when refused.

normalizeAggregate()

Normalizes an aggregate definition into a `[ code, field ]` pair.

private normalizeAggregate(mixed $definition) : array{0: ?string, 1: ?string}

Accepts 'sum:amount' (string) or ['sum','amount'] (list).

Parameters
$definition : mixed
Return values
array{0: ?string, 1: ?string}

normalizeGroupFields()

Normalizes {@see Group::BY} into a `[ varName => field ]` map.

private normalizeGroupFields(mixed $by) : array<string, string>
  • CSV string 'category,status'[ 'category' => 'category', 'status' => 'status' ].
  • list ['category','status'] → same.
  • assoc ['year' => 'created'] → kept as-is.

Dotted fields yield underscore variable names (address.cityaddress_city).

Parameters
$by : mixed
Return values
array<string, string>

resolveSortEntry()

Resolve a whitelisted sort entry to its `[ fieldPath, requires ]` pair.

private resolveSortEntry(string $key, mixed $entry) : array{0: mixed, 1: mixed}

The entry (the $sortable[$key] value) is either a plain field path — a string or an array path ([ 'address', 'city' ]) — or an explicit definition (an associative array carrying Field::PATH and/or Field::REQUIRES). Only the explicit Field::REQUIRES (Façon A) is returned here; the inherited permission (Façon B) is decided by isSortAuthorized() against the resolved $path — never against the URL key — so a dotted/aliased path (salaryaddress.salary) is gated at its exact (sub-)field, symmetric with groupBy/bounds and free of the "wrong homonym" pitfall.

Shared by the textual ?sort= grammar and the ?near= distance anchor, so both resolve a geo/scalar field the same way.

Parameters
$key : string

The public URL key (already resolved against the whitelist).

$entry : mixed

The $sortable[$key] value (path or explicit definition).

Return values
array{0: mixed, 1: mixed}

The [ fieldPath, requires ] pair (requires is the explicit subject, or null when the entry declares none).

On this page

Search results