Skip to content

Introduce scope-aware Context and #[MapContext]#131

Merged
jerowork merged 1 commit into
jerowork:mainfrom
ruudk:context
Jun 10, 2026
Merged

Introduce scope-aware Context and #[MapContext]#131
jerowork merged 1 commit into
jerowork:mainfrom
ruudk:context

Conversation

@ruudk

@ruudk ruudk commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Resolvers often need per-request data that is neither a GraphQL input nor a DI service: the current HTTP request, the authenticated user, or a value computed by an ancestor field. Until now the generated resolver closures ignored webonyx's context value entirely, so the only escape hatches were #[Arg] (client input) and #[Autowire] (container), and there was no way to thread request state to a resolver.

This adds a first-class Context value object and a #[MapContext] parameter attribute. Context is a webonyx ScopedContext that stores objects by class (attachObject/getObject/maybeGetObject) and also implements ArrayAccess; because it is scoped, a value attached by a resolver is visible to its descendants without leaking to siblings. #[MapContext] injects an object from the Context into a resolver parameter by its type — nullable parameters resolve to null when absent, non-nullable parameters throw.

The generated query, mutation, and field closures now receive webonyx's context as a mixed value and pass it through to ArgumentNodeResolver. The value is left untouched (it can be anything the server provides); only #[MapContext] resolution requires it to be a Context, throwing a ContextException otherwise. Wiring a Context onto the server is up to the consumer via the webonyx context option (see the docs).

Comment thread docs/usage.md
Comment thread src/Node/MutationNode.php
Resolvers often need per-request data that is neither a GraphQL input
nor a DI service: the current HTTP request, the authenticated user, or a
value computed by an ancestor field. Until now the generated resolver
closures ignored webonyx's context value entirely, so the only escape
hatches were `#[Arg]` (client input) and `#[Autowire]` (container), and
there was no way to thread request state to a resolver.

This adds a first-class `Context` value object and a `#[MapContext]`
parameter attribute. `Context` is a webonyx `ScopedContext` that stores
objects by class (`attachObject`/`getObject`/`maybeGetObject`) and also
implements `ArrayAccess`; because it is scoped, a value attached by a
resolver is visible to its descendants without leaking to siblings.

Resolvers reach it in two ways. A parameter type-hinted `Context` (no
attribute) receives the whole context object, recognised automatically
by type, so a resolver can read several values or `attachObject()`
something for its descendant fields. `#[MapContext]` instead injects a
single object from the `Context` by the parameter type — nullable
parameters resolve to `null` when absent, non-nullable parameters throw.

The generated query, mutation, and field closures now receive webonyx's
context as a `mixed` value and pass it through to `ArgumentNodeResolver`.
The value is left untouched (it can be anything the server provides);
only `Context`/`#[MapContext]` resolution requires it to be a `Context`,
throwing a `ContextException` otherwise. Wiring a `Context` onto the
server is up to the consumer via the webonyx `context` option (see the
docs).
@jerowork jerowork merged commit eac7853 into jerowork:main Jun 10, 2026
6 of 7 checks passed
@ruudk ruudk deleted the context branch June 10, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants