EdgesController extends Controller uses AuthorizationContextTrait, CapabilityContextTrait, ModelCallTrait, PermissionAuthorizerTrait
Generic controller for managing edge relationships between two vertex collections.
Provides post() and delete() methods to create and remove edges
between a source vertex (from) and a target vertex (to).
Both source and target vertex IDs are read from the URL route placeholders:
{id}for the source vertex (Schema::ID){targetId}for the target vertex
POST also accepts an optional body for edge properties.
Like the other controllers, it carries an authorization seat: the model
calls are wrapped by the ModelCallTrait hooks, so a consumer can refuse
to link — or to unlink — documents its scope hides. Without that seat this was
both a write surface reaching outside any scope, and an existence oracle: the
three refusals (404 source, 404 target, 409 edge exists) told a caller
what a scoped GET withholds.
One hook, three collections. Unlike the other controllers this one talks to three models — the source vertices, the target vertices, and the edges — and a predicate written for one is meaningless on the others. Each hook call therefore carries self::CALL, whose value is self::FROM, self::TO or self::EDGES, so an override knows which collection it is scoping:
protected function beforeModelCall( ?Request $request , array &$init ) : void
{
if ( ( $init[ self::CALL ] ?? null ) === self::FROM )
{
$init[ Arango::CONDITIONS ] = [ ...( $init[ Arango::CONDITIONS ] ?? [] ) , 'doc.status == @scope' ] ;
$init[ Arango::BINDS ] = [ ...( $init[ Arango::BINDS ] ?? [] ) , 'scope' => 'published' ] ;
}
parent::beforeModelCall( $request , $init ) ;
}
The creation itself is deliberately left unhooked. An INSERT has no
FOR and no FILTER, so there is nothing to narrow — and worse,
Edges::insertEdge() forwards its $init to the existEdge() uniqueness
check: a scope posed there would blind the 409 and let a duplicate through.
A creation is refused upstream, by the two vertex probes, which are hooked.
The request-scoped authorizer is still posed on it, so the returned edge is
projected under the same Field::REQUIRES gates as a read.
Tags
Table of Contents
Constants
- CALL : string = 'call'
- The init key naming the collection a hook call is about — its value is one of {@see self::FROM}, {@see self::TO} or {@see self::EDGES}.
- EDGES : string = 'edges'
- Initialization key for the Edges model dependency.
- FROM : string = 'from'
- Initialization key for the source vertex Documents model.
- TARGET_ID : string = 'targetId'
- URL placeholder name for the target vertex ID.
- TO : string = 'to'
- Initialization key for the target vertex Documents model.
Properties
- $edges : Edges|null
- The Edges model for the edge collection.
- $from : Documents|null
- The Documents model for the source vertex collection.
- $to : Documents|null
- The Documents model for the target vertex collection.
Methods
- __construct() : mixed
- Creates a new EdgesController instance.
- delete() : mixed
- Removes an edge between two vertices.
- post() : mixed
- Creates a new edge between two vertices.
- beforeModelCall() : void
- Injects the request-scoped permission authorizer into the model `$init` before every hooked 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()`.
- authorized() : array<string, mixed>
- The caller init carrying the request-scoped authorizer — the projection gate, posed on the calls the consumer hook deliberately does not reach.
- vertexInit() : array<string, mixed>
- Builds the init of a vertex existence probe and runs the hook on it.
Constants
CALL
The init key naming the collection a hook call is about — its value is one of {@see self::FROM}, {@see self::TO} or {@see self::EDGES}.
public
string
CALL
= 'call'
EDGES
Initialization key for the Edges model dependency.
public
string
EDGES
= 'edges'
FROM
Initialization key for the source vertex Documents model.
public
string
FROM
= 'from'
TARGET_ID
URL placeholder name for the target vertex ID.
public
string
TARGET_ID
= 'targetId'
TO
Initialization key for the target vertex Documents model.
public
string
TO
= 'to'
Properties
$edges
The Edges model for the edge collection.
protected
Edges|null
$edges
= null
$from
The Documents model for the source vertex collection.
protected
Documents|null
$from
= null
$to
The Documents model for the target vertex collection.
protected
Documents|null
$to
= null
Methods
__construct()
Creates a new EdgesController instance.
public
__construct(Container $container[, array<string|int, mixed> $init = [] ]) : mixed
Parameters
- $container : Container
-
The DI container reference.
- $init : array<string|int, mixed> = []
-
Supports:
- self::EDGES: Edges model service ID or instance
- self::FROM: Documents model for the source vertex
- self::TO: Documents model for the target vertex
Tags
delete()
Removes an edge between two vertices.
public
delete([ServerRequestInterface|null $request = null ][, ResponseInterface|null $response = null ][, array<string|int, mixed> $args = [] ][, array<string|int, mixed> $init = [] ]) : mixed
Reads both vertex IDs from the route placeholders:
{id}for the source vertex{targetId}for the target vertex
Parameters
- $request : ServerRequestInterface|null = null
-
The PSR-7 request object.
- $response : ResponseInterface|null = null
-
The PSR-7 response object.
- $args : array<string|int, mixed> = []
-
Route placeholders (expects Schema::ID and self::TARGET_ID).
- $init : array<string|int, mixed> = []
-
Optional settings.
Tags
Return values
mixed —200 on success, 404 if vertex or edge not found.
post()
Creates a new edge between two vertices.
public
post([ServerRequestInterface|null $request = null ][, ResponseInterface|null $response = null ][, array<string|int, mixed> $args = [] ][, array<string|int, mixed> $init = [] ]) : mixed
Reads both vertex IDs from the route placeholders:
{id}for the source vertex{targetId}for the target vertex
The request body is optional and can contain additional edge properties.
Parameters
- $request : ServerRequestInterface|null = null
-
The PSR-7 request object.
- $response : ResponseInterface|null = null
-
The PSR-7 response object.
- $args : array<string|int, mixed> = []
-
Route placeholders (expects Schema::ID and self::TARGET_ID).
- $init : array<string|int, mixed> = []
-
Optional settings.
Tags
Return values
mixed —201 on success, 400 if missing data, 404 if vertex not found, 409 if edge exists.
beforeModelCall()
Injects the request-scoped permission authorizer into the model `$init` before every hooked model call.
protected
beforeModelCall(ServerRequestInterface|null $request, array<string, mixed> &$init) : void
Overrides the no-op ModelCallTrait::beforeModelCall(). Strictly the behaviour of DocumentsController::beforeModelCall(), with the same two guards — an authorizer already in the init wins, and nothing is posed without an authorization stack or an authenticated user.
A subclass is what turns the seat into an actual scope; it should branch on self::CALL before appending anything, since the three hooked calls target three different collections.
Parameters
- $request : ServerRequestInterface|null
-
The current PSR-7 request.
- $init : array<string, mixed>
-
The init array forwarded to the model (by reference).
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
staticauthorized()
The caller init carrying the request-scoped authorizer — the projection gate, posed on the calls the consumer hook deliberately does not reach.
private
authorized(ServerRequestInterface|null $request, array<string, mixed> $init) : array<string, mixed>
An authorizer already supplied by the caller wins, and nothing is posed when there is no request, no enforcer, no resolver or no authenticated user: the projection then falls open, exactly as before the seat existed.
Parameters
- $request : ServerRequestInterface|null
-
The current PSR-7 request.
- $init : array<string, mixed>
-
The caller init.
Return values
array<string, mixed>vertexInit()
Builds the init of a vertex existence probe and runs the hook on it.
private
vertexInit(ServerRequestInterface|null $request, array<string, mixed> $init, string $value, string $call) : array<string, mixed>
Parameters
- $request : ServerRequestInterface|null
-
The current PSR-7 request.
- $init : array<string, mixed>
-
The caller init (definition-level conditions travel here).
- $value : string
-
The probed document key.
- $call : string
-
self::FROM or self::TO.
Return values
array<string, mixed> —The enriched probe init.