mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-09-19 16:06:32 +02:00
f3e1cd4599
* DOCS: new union and interface inheritance pattern * Update docs for interface queries * Remove config references in updateSchema
154 lines
5.6 KiB
Markdown
154 lines
5.6 KiB
Markdown
---
|
|
title: What are plugins?
|
|
summary: An overview of how plugins work with the GraphQL schema
|
|
---
|
|
|
|
# Plugins
|
|
|
|
[CHILDREN asList]
|
|
|
|
[alert]
|
|
You are viewing docs for a pre-release version of silverstripe/graphql (4.x).
|
|
Help us improve it by joining #graphql on the [Community Slack](https://www.silverstripe.org/blog/community-slack-channel/),
|
|
and report any issues at [github.com/silverstripe/silverstripe-graphql](https://github.com/silverstripe/silverstripe-graphql).
|
|
Docs for the current stable version (3.x) can be found
|
|
[here](https://github.com/silverstripe/silverstripe-graphql/tree/3)
|
|
[/alert]
|
|
|
|
## What are plugins?
|
|
|
|
Plugins are used to distribute reusable functionality across your schema. Some examples of commonly used plugins include:
|
|
|
|
* Adding versioning arguments to versioned DataObjects
|
|
* Adding a custom filter/sort arguments to DataObject queries
|
|
* Adding a one-off `VerisionedStage` enum to the schema
|
|
* Ensuring `Member` is in the schema
|
|
* And many more...
|
|
|
|
### Default plugins
|
|
|
|
By default, all schemas ship with some plugins installed that will benefit most use cases:
|
|
|
|
* The `DataObject` model (i.e. any dataobject based type) has:
|
|
* An `inheritance` plugin that builds the interfaces, unions, and merges ancestral fields.
|
|
* An `inheritedPlugins` plugin (a bit meta!) that merges plugins from ancestral types into descendants.
|
|
installed).
|
|
* The `read` and `readOne` operations have:
|
|
* A `canView` plugin for hiding records that do not pass a `canView()` check
|
|
* The `read` operation has:
|
|
* A `paginateList` plugin for adding pagination arguments and types (e.g. `nodes`)
|
|
|
|
In addition to the above, the `default` schema specifically ships with an even richer set of default
|
|
plugins, including:
|
|
|
|
* A `versioning` plugin that adds `version` fields to the dataobject type (if `silverstripe/versioned` is installed)
|
|
* A `readVersion` plugin (if `silverstripe/versioned` is installed) that allows versioned operations on
|
|
`read` and `readOne` queries.
|
|
* A `filter` plugin for filtering queries (adds a `filter` argument)
|
|
* A `sort` plugin for sorting queries (adds a `sort` argument)
|
|
|
|
|
|
All of these are defined in the `modelConfig` section of the schema (see [configuring your schema](../getting_started/configuring_your_schema)). For reference, see the graphql configuration in `silverstripe/admin`, which applies
|
|
these default plugins to the `default` schema.
|
|
|
|
#### Overriding default plugins
|
|
You can override default plugins generically in the `modelConfig` section.
|
|
|
|
**app/_graphql/modelConfig.yml**
|
|
```yaml
|
|
DataObject:
|
|
plugins:
|
|
inheritance: false # No dataobject models get this plugin unless opted into
|
|
operations:
|
|
read:
|
|
plugins:
|
|
paginateList: false # No dataobject models have paginated read operations unless opted into
|
|
```
|
|
|
|
You can override default plugins on your specific dataobject type and these changes will be inherited by descendants.
|
|
|
|
**app/_graphql/models.yml**
|
|
```yaml
|
|
Page:
|
|
plugins:
|
|
inheritance: false
|
|
MyProject\MyCustomPage: {} # now has no inheritance plugin
|
|
```
|
|
|
|
Likewise, you can do the same for operations:
|
|
|
|
**app/_graphql/models.yml**
|
|
```yaml
|
|
Page:
|
|
operations:
|
|
read:
|
|
plugins:
|
|
readVersion: false
|
|
MyProject\MyCustomPage:
|
|
operations:
|
|
read: true # has no readVersion plugin
|
|
```
|
|
|
|
|
|
### What plugins must do
|
|
|
|
There isn't a huge API surface to a plugin. They just have to:
|
|
|
|
* Implement at least one of several plugin interfaces
|
|
* Declare an identifier
|
|
* Apply themselves to the schema with the `apply(Schema $schema)` method
|
|
* Be registered with the `PluginRegistry`
|
|
|
|
|
|
### Available plugin interfaces
|
|
|
|
Plugin interfaces are all found in the namespace `SilverStripe\GraphQL\Schema\Interfaces`
|
|
|
|
* `SchemaUpdater`: Make a one-off, context-free update to the schema
|
|
* `QueryPlugin`: Update a generic query
|
|
* `MutationPlugin`: Update a generic mutation
|
|
* `TypePlugin`: Update a generic type
|
|
* `FieldPlugin`: Update a field on a generic type
|
|
* `ModelQueryPlugin`: Update queries generated by a model, e.g. `readPages`
|
|
* `ModelMutationPlugin`: Update mutations generated by a model, e.g. `createPage`
|
|
* `ModelTypePlugin`: Update types that are generated by a model
|
|
* `ModelFieldPlugin`: Update a field on types generated by a model
|
|
|
|
Wow, that's a lot of interfaces, right? This is owing mostly to issues around strict typing between interfaces,
|
|
and allows for a more expressive developer experience. Almost all of these interfaces have the same requirements,
|
|
just for different types. It's pretty easy to navigate if you know what you want to accomplish.
|
|
|
|
### Registering plugins
|
|
|
|
Plugins have to be registered with Injector.
|
|
|
|
```yaml
|
|
SilverStripe\Core\Injector\Injector:
|
|
SilverStripe\GraphQL\Schema\Registry\PluginRegistry:
|
|
constructor:
|
|
- 'MyProject\Plugins\MyPlugin'
|
|
```
|
|
|
|
[info]
|
|
The key `myPlugin` is arbitrary. The identifier of the plugin is obtained procedurally.
|
|
[/info]
|
|
|
|
### Resolver middleware and afterware
|
|
|
|
The real power of plugins is the ability to distribute not just configuration across the schema, but
|
|
more importantly, _functionality_.
|
|
|
|
Fields have their own resolvers already, so we can't really get into those to change
|
|
their functionality without a massive hack. This is where the idea of **resolver middleware** and
|
|
**resolver afterware** comes in really useful.
|
|
|
|
**Resolver middleware** runs _before_ the field's assigned resolver
|
|
**Resolver afterware** runs _after_ the field's assigned resolver
|
|
|
|
Middlewares and afterwares are pretty straightforward. They get the same `$args`, `$context`, and `$info`
|
|
parameters as the assigned resolver, but the first argument, `$result` is mutated with each resolver.
|
|
|
|
### Further reading
|
|
|
|
[CHILDREN]
|