4.0.0 alpha 1
This is the first alpha release of GraphQL 4
Changelog
For more a high-level view of what has changed in GraphQL 4, see the upgrade guide.
Changes since 3 Feb 2021
-
Refactor context provider API so that they now plug into
QueryHandlerInterface
. They
can set and get their own context based on their own internal logic. Before:
$user = $context['currentUser'];
after:$user = UserContextProvider::get($context)
-
BuildState
API is gone. Use newSchemaContextProvider
in resolvers. PassSchemaContext
objects to the small number of classes that needed it in their internals. -
AbstractNestedInputPlugin
is gone. This never made any sense to tie something
so utilitarian to an arbitrary inheritance chain. It has been refactored asNestedInputBuilder
and is far more single-purpose. No longer concerned with model and field mapping, but rather
just building recursive input types and adding them to the schema. -
Refactor
QuerySort
,QueryFilter
to use input builder -
Field mapping is no longer exhaustively encoded in the resolvers. New
SchemaContextProvider
allows resolvers to look at the schema as persisted to a
PHP array in the code gen and invoke methods like$schema->mapField($type, $fieldName)
-
No more
resolveContext
for DataObject\Resolver since field mapping is handled externally now -
Migrate type
Controller::introspectTypes
to standalone service (SchemaTranscriber
)
invoked through event dispatcher -
Remove
IntrospectionProvider
extension. Static only. Leaving it to the user to decide never made any sense, and it just slowed down the UI in the CMS. Now that we're doing code gen, it seems like the static solution is more consistent. -
Remove
ModelConfigurationProvider
: Didn't make any sense. Models are now
responsible for fetching their own configurations. -
Remove
defaultResolver
: Didn't make any sense. Just set a defaultresolver
property and allow it to be overridden. -
Resolver discovery is now lazy to avoid race conditions in config. If resolvers
are null at code gen time, discovery is invoked and applied. -
Controller now only accepts a schema key and uses
SchemaFactory
to boot. -
Schema::quiet()
is gone. Schema is quiet by default to better support autobuilding
and test suites. Opt into verbose withsetVerbose(true)
. -
CSRFMiddleware
andHTTPMethodMiddleware
now rely on new ContextProviders
to better encapsulate their state concerns. -
Better error reporting:
- QueryHandler now pretty prints the stack trace
- Resolver level errors now throw
ResolverFailure
which express tons of useful
context, including the query path, the field, the type, the args, and most importantly,
the resolver chain. No more guessing which closure failed. - ResolverFailure relies on new
resolverComposition
field in encoded types. - Updates to code gen templates to support the above, and other tidiness like
variable assignment to save code execution and comments to show closing bracket relationships.
-
ComposedResolver is now stateful, mostly for diagnostic purposes. Before:
ComposedResolver::create(): Closure
after:ComposedResolver::create($list)->toClosure()
-
All resolvers use
ComposedResolver
for consistency, even when no middle/afterwares -
ComposedResolver
no longer needs separate arguments for primary, before, and after
resolvers. Just a stack that leaves ordering a concern of the code composing the class.
(this was a @todo annotation FWIW) -
SchemaModelInterface
now must declaregetPropertyForField
(gql field -> object property mapping) -
SchemaModelInterface
now must declaregetSchemaContext
andgetModelConfiguration
-
New
hasNativeField
inFieldAccessor
helps with knowing what fields are
filterable/sortable. Basically ORM insight. -
Fields using the
model
property (a bit of a polymorphic hack to support
cases like CMS needing to know what the SiteTree model is named), will
get just-in-time assignment before code generation time to prevent race conditions
in the config. -
New suite of relational
FakeProduct
objects for testing nested queries, filters, mutations. -
New top-to-bottom integration test that creates, updates, and reads nested data structures with filters/sorting, field aliases, etc.
-
NB:
CodeGenerationStore
is now responsible for type mapping and field mapping. This
is a leaky abstraction as it's tightly coupled to SchemaContext now. -
New
StorableSchema
layer eliminates unpredictable state ofSchema
throughprocess()
. The readonly value object returned bycreateStorableSchema()
is expected to
be a complete set of primitive objects that are consumable by a storage service. -
SchemaFactory
is nowSchemaBuilder
, which has four clearly defined functions, and eliminates the confusion around usingSchema
at query time:getConfig(string $schemaKey): ?SchemaContext
-- Get the cached configuration of a Schema. Useful for resolversboot(string $schemaKey): Schema
-- Load all the configuration in to a Schema object. Useful for the configuration/build process.getSchema(string $schemaKey): ?GraphQLSchema
-- Retrieve a queryable graphql-php Schema object from the storage service. Useful at query time.build(Schema $schema, bool $clear = false)
: GraphQLSchema -- put theSchema
through a storage service and return its queryable schema
-
SchemaStorageInterface is now the concern of
SchemaBuilder
rather thanSchema
, which results in a diminished API surface forSchema
. -
Validation of Schema moved to
StorableSchema
, as this is the only time the schema truly needs to be valid (before storage) -
Type/field mapping done just-in-time for schema storage
-
Prefer event dispatcher over extend() hooks in controller
-
SchemaContext
is nowSchemaConfig
-
Schema
has newgetState()
for persisting state during build -
Improved handling of "empty" schemas that shouldn't be built, but also shouldn't error (e.g.
default
out of the box)