PropertyController extends Controller uses AuthorizationContextTrait, CapabilityContextTrait, ModelCallTrait, PayloadsTrait, PermissionAuthorizerTrait, PropertyControllerGetTrait, PropertyControllerPatchTrait
The Property Controller based on the Arango DB engine.
Use a Documents model to read (get) or update (patch) a property in a Document.
Like DocumentsController, it carries an authorization seat: the
capability enforcer and the permission-subject resolver are resolved from the
container at construction time, and every model call is wrapped by the
ModelCallTrait hooks. The class itself only poses the request-scoped
authorizer — a subclass overriding beforeModelCall() is what turns the
seat into an actual scope (extra Arango::CONDITIONS, extra Arango::BINDS),
both of which the model already honours.
Every query carries the scope. The hook runs around each read, around the
existence probe that precedes each write, and around the write itself — whose
FILTER now reads the same Arango::CONDITIONS as the reads. A document
outside the scope is therefore reported missing by the probe, and would match
nothing in the UPDATE even if it slipped past it: the two no longer have to
agree across a window in which the document may have moved.
The six array operations of ArrayPropertyController are the exception:
they build their own FILTER and do not read Arango::CONDITIONS, so the
scoped existence probe remains their only gate.
Table of Contents
Properties
- $payload : string|array<string|int, mixed>|null
- The initial payload definition to prepare a new document to insert in a collection with the POST/PATCH/PUT methods.
Methods
- __construct() : mixed
- Creates a new DocumentsController instance.
- enforceI18nShape() : ResponseInterface|null
- Pre-validate the i18n-typed fields and short-circuit with a 422 if any field has an invalid shape.
- generatePayload() : array<string|int, mixed>
- Prepares a key-value payload object based on the provided request and definitions.
- get() : mixed
- Returns a specific document with a specific identifier.
- initializePayload() : static
- Initialize the 'payload' definition used to prepare a document for insertion or replace/update.
- patch() : mixed
- Update a part of a document in a collection with a specific identifier (by default use the _key attribute).
- preparePayload() : array<string|int, mixed>
- Prepare the 'payload' to insert or modify in the POST, PATCH or PUT methods.
- prepareWritePayload() : bool
- Runs the full payload preparation of a write handler: the i18n shape guard, the payload extraction, the rule validation, and the relation stripping.
- propertyPayload() : mixed
- Returns an associative array with a key/value definition based on the property name and the payload request object.
- stripRelationKeys() : mixed
- Removes from the payload the attributes registered as **relations**.
- validateI18nShape() : array<string, string>
- Pre-validate the shape of i18n-typed fields in the request body.
- beforeModelCall() : void
- Injects the request-scoped permission authorizer into the model `$init` payload before every model call.
- initializeAuthorizationContext() : static
- Resolves the capability enforcer and the permission-subject resolver from the container (each guarded by an `instanceof`, null when absent) and wires them through `initializeCapabilities()` and `initializePermissionSubjectResolver()`.
- alterPayload() : mixed
- Apply an alteration function to a payload value.
- extractCustomPayloadValue() : mixed
- Extract a custom type value (method-based or fallback).
- extractEdgePayloadValue() : string|null
- Extract a payload 'EDGE' type value and register it in relations.
- extractPayloadValue() : mixed
- Extract a single payload value based on its type definition.
- extractSubPayloadValue() : array<string|int, mixed>|null
- Extract a payload 'PAYLOAD' type value (recursive payload generation).
- isSimplePayload() : bool
- Determine if the payload definition is a simple value (not a complex document structure).
- prefixPayloadDirectChildren() : array<string|int, mixed>
- Prefix only the direct children field names with parent key.
- reloadProperty() : mixed
- Re-reads the updated property so the response carries the stored value rather than the submitted one (`Arango::RAW` skips this round-trip).
Properties
$payload
The initial payload definition to prepare a new document to insert in a collection with the POST/PATCH/PUT methods.
public
string|array<string|int, mixed>|null
$payload
= []
Tags
Methods
__construct()
Creates a new DocumentsController instance.
public
__construct(Container $container[, array<string|int, mixed> $init = [] ]) : mixed
Parameters
- $container : Container
-
The DI Container reference.
- $init : array<string|int, mixed> = []
-
The optional properties to passed-in to initialize the object.
Tags
enforceI18nShape()
Pre-validate the i18n-typed fields and short-circuit with a 422 if any field has an invalid shape.
public
enforceI18nShape(ServerRequestInterface|null $request, ResponseInterface|null $response[, string|null $method = null ][, array<string|int, mixed> $init = [] ]) : ResponseInterface|null
Convenience wrapper around validateI18nShape() that builds the
canonical "Unprocessable Entity" response when validation fails.
Callers should return the response directly when this method returns
a non-null value.
Parameters
- $request : ServerRequestInterface|null
-
The current HTTP request.
- $response : ResponseInterface|null
-
The current HTTP response.
- $method : string|null = null
-
The HTTP method (POST, PATCH, PUT).
- $init : array<string|int, mixed> = []
-
Optional override of the payload definitions.
Tags
Return values
ResponseInterface|null —Null when the body is well-formed, otherwise the 422 response to return.
generatePayload()
Prepares a key-value payload object based on the provided request and definitions.
public
generatePayload(ServerRequestInterface $request[, array<string|int, mixed>|null $definitions = null ][, array<string|int, mixed> $args = [] ][, array<string|int, mixed> &$relations = [] ][, bool $throwable = false ]) : array<string|int, mixed>
This method processes the given definitions and extracts values from the request based on the type specified in the definitions.
If a type is not specified but a value is provided in the definitions, that value is directly assigned to the document.
Parameters
- $request : ServerRequestInterface
-
The request object that contains the input data.
- $definitions : array<string|int, mixed>|null = null
-
An array of definitions that specify the types and names of expected parameters or their predefined values. Each definition may include a type (e.g., BOOL, FLOAT, I18N, INT, etc.), a name, or a predefined value.
- $args : array<string|int, mixed> = []
-
The optional arguments to initialize the document key/value.
- $relations : array<string|int, mixed> = []
-
The array reference to register all payload attributes with a relation behavior (edges).
- $throwable : bool = false
-
Indicates if the method throws errors.
Tags
Return values
array<string|int, mixed> —An associative array containing the processed key-value pairs extracted or derived from the request and definitions.
get()
Returns a specific document with a specific identifier.
public
get([ServerRequestInterface|null $request = null ][, ResponseInterface|null $response = null ][, array<string|int, mixed> $args = [] ][, array<string|int, mixed> $init = [] ]) : mixed
Ex: ../element?search=film Ex: ../element?facets={"location":12} Ex: ../element?facets={"type":"-event,visual/exhibition","eventStatus":"-scheduled"}
Parameters
- $request : ServerRequestInterface|null = null
- $response : ResponseInterface|null = null
- $args : array<string|int, mixed> = []
-
An associative array that contains values for the current route’s named placeholders.
- $init : array<string|int, mixed> = []
-
An optional associative array to initialize the method.
Tags
initializePayload()
Initialize the 'payload' definition used to prepare a document for insertion or replace/update.
public
initializePayload([array<string|int, mixed> $init = [] ]) : static
This method sets the $payload property based on the provided associative array.
If the array contains the key Arango::PAYLOADS, its value will replace the current payload.
Otherwise, the existing payload is kept.
Example:
$controller->initializePayloads
([
Arango::PAYLOADS =>
[
HttpMethod::ALL =>
[
Prop::NAME => [ Arango::TYPE => AQLType::STRING ],
Prop::ADDRESS =>
[
Arango::TYPE => AQLType::OBJECT ,
Arango::COMPRESS => true ,
Arango::PAYLOAD =>
[
Prop::STREET_ADDRESS => [ Arango::TYPE => AQLType::STRING ] ,
Prop::EXTENDED_ADDRESS => [ Arango::TYPE => AQLType::STRING ] ,
Prop::ADDRESS_LOCALITY => [ Arango::TYPE => AQLType::STRING ] ,
Prop::ADDRESS_COUNTRY => [ Arango::TYPE => AQLType::STRING ] ,
Prop::ADDRESS_DEPARTMENT => [ Arango::TYPE => AQLType::STRING ] ,
Prop::ADDRESS_REGION => [ Arango::TYPE => AQLType::STRING ] ,
Prop::POST_OFFICE_BOX_NUMBER => [ Arango::TYPE => AQLType::STRING ] ,
]
]
// ... other field definitions
],
HttpMethod::POST =>
[
Prop::ACTIVE => [ Arango::VALUE => 1 ],
// ... other field definitions
],
],
]);
Parameters
- $init : array<string|int, mixed> = []
-
Associative array containing the schema definition.
Return values
static —Returns the current instance for method chaining.
patch()
Update a part of a document in a collection with a specific identifier (by default use the _key attribute).
public
patch([ServerRequestInterface|null $request = null ][, ResponseInterface|null $response = null ][, array<string|int, mixed> $args = [] ][, array<string|int, mixed> $init = [] ]) : mixed
Example: PATCH ../collection/{id}
Parameters
- $request : ServerRequestInterface|null = null
- $response : ResponseInterface|null = null
- $args : array<string|int, mixed> = []
-
An associative array that contains values for the current route’s named placeholders.
- $init : array<string|int, mixed> = []
-
An optional associative array to initialize the method.
Tags
preparePayload()
Prepare the 'payload' to insert or modify in the POST, PATCH or PUT methods.
public
preparePayload(ServerRequestInterface|null $request[, string|null $method = null ][, array<string|int, mixed> $init = [] ][, array<string|int, mixed> &$relations = [] ]) : array<string|int, mixed>
This method builds a document array based on the request body and the payload definitions corresponding to the current HTTP method.
It can optionally "compress" the document structure depending on the compress configuration.
Parameters
- $request : ServerRequestInterface|null
-
The current HTTP request instance (may be null).
- $method : string|null = null
-
The current HTTP method (e.g. HttpMethod::POST, HttpMethod::PATCH, HttpMethod::PUT).
- $init : array<string|int, mixed> = []
-
Initialization options to customize behavior:
- compress (array|bool) Compress behavior definition (default:
false).- If
true, the document is always compressed. - If an array, only compress when the current method is included (e.g.
[HttpMethod::POST, HttpMethod::PATCH]).
- If
- payload (array) Definition to override the default payload settings.
- compress (array|bool) Compress behavior definition (default:
- $relations : array<string|int, mixed> = []
-
The array reference to register all payload attributes with a relation behavior (edges).
Tags
Return values
array<string|int, mixed> —The prepared document ready for insertion or modification.
prepareWritePayload()
Runs the full payload preparation of a write handler: the i18n shape guard, the payload extraction, the rule validation, and the relation stripping.
public
prepareWritePayload(ServerRequestInterface|null $request, ResponseInterface|null $response, string|null $method, array<string|int, mixed> $init, array<string|int, mixed> &$relations, mixed &$payload[, mixed &$failure = null ]) : bool
post() and update() performed these four steps identically, in the same
order, with the same early returns. They are stated here once — the sequence
matters (the shape guard must run before extraction, the stripping after
validation, so the rules still see the relation attributes the caller sent).
It answers whether the write may proceed, and hands the response to return
through $failure when it may not:
$relations = [] ;
$payload = null ;
$failure = null ;
$method = $request?->getMethod() ;
if ( !$this->prepareWritePayload( $request , $response , $method , $init , $relations , $payload , $failure ) )
{
return $failure ;
}
The verdict is a boolean, never the response object, and that is the point.
fail() returns null when $response is null — the convention the controller
tests rely on — so a caller branching on the truthiness of an error response
cannot tell "it failed" from "it went fine": in that mode it would carry on and
write a payload the rules had just refused. Production always supplies a
response and never saw it; the test suite did, silently. Each guard below is
therefore decided on its cause — a non-empty error list, fails() — and the
response is only ever built afterwards, to be carried back.
Parameters
- $request : ServerRequestInterface|null
-
The current HTTP request.
- $response : ResponseInterface|null
-
The current HTTP response.
- $method : string|null
-
The HTTP method (POST, PATCH, PUT).
- $init : array<string|int, mixed>
-
Optional override of the payload definitions.
- $relations : array<string|int, mixed>
-
Reference filled with the attributes registered as relations (edges).
- $payload : mixed
-
Reference filled with the payload to write, relation keys already stripped.
- $failure : mixed = null
-
Reference filled with the response to return when the verdict is false.
Tags
Return values
bool —True when the payload is ready to write, false when the caller must return $failure.
propertyPayload()
Returns an associative array with a key/value definition based on the property name and the payload request object.
public
propertyPayload(ServerRequestInterface $request, string|null $property[, array<string|int, mixed> &$relations = [] ]) : mixed
Parameters
- $request : ServerRequestInterface
- $property : string|null
- $relations : array<string|int, mixed> = []
stripRelationKeys()
Removes from the payload the attributes registered as **relations**.
public
stripRelationKeys(mixed $payload, array<string|int, mixed> $relations) : mixed
An attribute declared with the EDGE payload type is not a field of the
document: it names an edge to create, and preparePayload() /
propertyPayload() register it in $relations rather than in the
document. Writing it as a plain attribute would store the target reference
twice — once in the edge, once inside the document — so the write handlers
strip those keys before handing the payload to the model.
A no-op when nothing was registered, which is the common case.
Parameters
- $payload : mixed
-
The payload about to be written.
- $relations : array<string|int, mixed>
-
The relations registered during the payload extraction.
Return values
mixed —The payload without its relation keys.
validateI18nShape()
Pre-validate the shape of i18n-typed fields in the request body.
public
validateI18nShape(ServerRequestInterface|null $request[, string|null $method = null ][, array<string|int, mixed> $init = [] ]) : array<string, string>
Inspects the payload definitions for fields typed as AQLType::I18N and checks the raw request body. If any such field is present with a non-array/object/null value (e.g. a flat string), an entry is returned for it. Callers should respond with a 422 when the result is non-empty, before invoking preparePayload() (which would otherwise drop the invalid value silently via filterLanguages()).
Parameters
- $request : ServerRequestInterface|null
-
The current HTTP request.
- $method : string|null = null
-
The HTTP method (POST, PATCH, PUT).
- $init : array<string|int, mixed> = []
-
Optional override of the payload definitions (same shape as preparePayload's
$init).
Tags
Return values
array<string, string> —Map of field name → error message. Empty when the body is well-formed.
beforeModelCall()
Injects the request-scoped permission authorizer into the model `$init` payload before every model call.
protected
beforeModelCall(ServerRequestInterface|null $request, array<string, mixed> &$init) : void
Overrides the no-op ModelCallTrait::beforeModelCall(), invoked
around each model call of this controller — get(), the post-write reload,
the update() of patch(), and the existence probe that gates patch()
and the six array operations of ArrayPropertyController. It builds a
request-scoped Closure(string $subject): bool through
PermissionAuthorizerTrait::buildPermissionAuthorizer() and stores it
under Arango::AUTHORIZER, where the projection layer
(isAuthorized()) consults it to enforce
the field-level Field::REQUIRES and definition-level AQL::REQUIRES gates.
Strictly the behaviour of DocumentsController::beforeModelCall(), with the same two guards:
- an authorizer already present in
$initis left untouched (a caller, a unit test, or a subclass that set one earlier wins) ; buildPermissionAuthorizer()returnsnullwhen there is no request, no enforcer, no resolver, or no authenticated user — nothing is then posed and the projection layer falls open, so a controller that never carries the authorization stack (CLI, tests) keeps its previous behaviour.
Parameters
- $request : ServerRequestInterface|null
-
The current PSR-7 request (null in CLI / test contexts).
- $init : array<string, mixed>
-
The init array forwarded to the model (by reference).
Tags
initializeAuthorizationContext()
Resolves the capability enforcer and the permission-subject resolver from the container (each guarded by an `instanceof`, null when absent) and wires them through `initializeCapabilities()` and `initializePermissionSubjectResolver()`.
protected
initializeAuthorizationContext([array<string, mixed> $init = [] ]) : static
Parameters
- $init : array<string, mixed> = []
-
Same array passed to the controller constructor.
Tags
Return values
staticalterPayload()
Apply an alteration function to a payload value.
private
alterPayload(mixed $value, mixed $alter) : mixed
Parameters
- $value : mixed
- $alter : mixed
extractCustomPayloadValue()
Extract a custom type value (method-based or fallback).
private
extractCustomPayloadValue(ServerRequestInterface $request, string|null $type, string $name, array<string|int, mixed> $args, array<string|int, mixed> $options, mixed $default) : mixed
Parameters
- $request : ServerRequestInterface
- $type : string|null
- $name : string
- $args : array<string|int, mixed>
- $options : array<string|int, mixed>
- $default : mixed
extractEdgePayloadValue()
Extract a payload 'EDGE' type value and register it in relations.
private
extractEdgePayloadValue(ServerRequestInterface $request, string $name, string $key, array<string|int, mixed> $options, array<string|int, mixed> $args, array<string|int, mixed> &$relations, mixed $default, bool $throwable) : string|null
Parameters
- $request : ServerRequestInterface
- $name : string
- $key : string
- $options : array<string|int, mixed>
- $args : array<string|int, mixed>
- $relations : array<string|int, mixed>
- $default : mixed
- $throwable : bool
Tags
Return values
string|nullextractPayloadValue()
Extract a single payload value based on its type definition.
private
extractPayloadValue(ServerRequestInterface $request, string $key, array<string|int, mixed> $options, array<string|int, mixed> $args, array<string|int, mixed> &$relations, bool $throwable) : mixed
Parameters
- $request : ServerRequestInterface
- $key : string
- $options : array<string|int, mixed>
- $args : array<string|int, mixed>
- $relations : array<string|int, mixed>
- $throwable : bool
Tags
extractSubPayloadValue()
Extract a payload 'PAYLOAD' type value (recursive payload generation).
private
extractSubPayloadValue(ServerRequestInterface $request, string $parentKey, array<string|int, mixed> $options, array<string|int, mixed> $args, array<string|int, mixed> &$relations, bool $throwable) : array<string|int, mixed>|null
This method automatically prefixes nested field names with their parent key if no explicit Arango::NAME is provided.
The prefixing is done "just-in-time" only for direct children, not recursively. Nested PAYLOAD types will handle their own prefixing when they are processed.
Example:
- Parent key: 'address'
- Child key: 'postalCode'
- Generated name: 'address.postalCode'
Parameters
- $request : ServerRequestInterface
- $parentKey : string
-
The parent key to use as prefix for nested fields
- $options : array<string|int, mixed>
- $args : array<string|int, mixed>
- $relations : array<string|int, mixed>
- $throwable : bool
Tags
Return values
array<string|int, mixed>|nullisSimplePayload()
Determine if the payload definition is a simple value (not a complex document structure).
private
isSimplePayload(mixed $definition) : bool
Parameters
- $definition : mixed
Return values
boolprefixPayloadDirectChildren()
Prefix only the direct children field names with parent key.
private
prefixPayloadDirectChildren(array<string|int, mixed> $definitions, string $prefix[, string $separator = '.' ]) : array<string|int, mixed>
Does NOT recursively process nested PAYLOAD types - they will be handled by their own extractSubPayloadValue call during generatePayload execution.
Automatically generates hierarchical names like 'address.postalCode' for fields that don't already have an explicit Arango::NAME.
Parameters
- $definitions : array<string|int, mixed>
-
The nested payload definitions
- $prefix : string
-
The parent key to use as prefix
- $separator : string = '.'
-
The separator between parent and child keys (default: '.')
Return values
array<string|int, mixed> —The definitions with auto-generated names for direct children only
reloadProperty()
Re-reads the updated property so the response carries the stored value rather than the submitted one (`Arango::RAW` skips this round-trip).
private
reloadProperty(ServerRequestInterface|null $request, array<string|int, mixed> $args, array<string|int, mixed> $init, object|null $document) : mixed
It is a read, so it goes through the same hooks and carries the same
Arango::CONDITIONS as PropertyControllerGetTrait::get() : a write
whose response bypassed the scope would hand back exactly what the scope
is meant to withhold.
Parameters
- $request : ServerRequestInterface|null
-
The current PSR-7 request.
- $args : array<string|int, mixed>
-
The route placeholders.
- $init : array<string|int, mixed>
-
The method init array (source of the declared conditions).
- $document : object|null
-
The document returned by the write.
Tags
Return values
mixed —The stored property value, or null when the re-read returns nothing.