diff --git a/docs/en/02_Developer_Guides/19_GraphQL/01_getting_started/02_configuring_your_schema.md b/docs/en/02_Developer_Guides/19_GraphQL/01_getting_started/02_configuring_your_schema.md index 62e0f907e90..49980cb8118 100644 --- a/docs/en/02_Developer_Guides/19_GraphQL/01_getting_started/02_configuring_your_schema.md +++ b/docs/en/02_Developer_Guides/19_GraphQL/01_getting_started/02_configuring_your_schema.md @@ -118,6 +118,8 @@ types: # your generic types here models: # your dataobjects here +bulkLoad: + # your bulk loader directives here queries: # your queries here mutations: @@ -154,6 +156,11 @@ be implicitly placed in the corresponding section of the schema. # my type definitions here ``` +**app/_graphql/bulkLoad/bulkLoad.yml** +```yaml +# my bulk loader directives here +``` + ##### Namespacing by filename If the filename is named one of the four keywords above, it will be implicitly placed @@ -168,6 +175,12 @@ in the corresponding section of the schema. **This only works in the root source ```yaml # my models here ``` + +**app/_graphql/bulkLoad.yml** +```yaml +# my bulk loader directives here +``` + #### Going even more granular These special directories can contain multiple files that will all merge together, so you can even diff --git a/docs/en/02_Developer_Guides/19_GraphQL/02_working_with_dataobjects/01_dataobject_model_type.md b/docs/en/02_Developer_Guides/19_GraphQL/02_working_with_dataobjects/01_adding_dataobjects_to_the_schema.md similarity index 67% rename from docs/en/02_Developer_Guides/19_GraphQL/02_working_with_dataobjects/01_dataobject_model_type.md rename to docs/en/02_Developer_Guides/19_GraphQL/02_working_with_dataobjects/01_adding_dataobjects_to_the_schema.md index 042c41f3ef5..b639356526a 100644 --- a/docs/en/02_Developer_Guides/19_GraphQL/02_working_with_dataobjects/01_dataobject_model_type.md +++ b/docs/en/02_Developer_Guides/19_GraphQL/02_working_with_dataobjects/01_adding_dataobjects_to_the_schema.md @@ -1,5 +1,5 @@ --- -title: The DataObject model type +title: Adding DataObjects to the schema summary: An overview of how the DataObject model can influence the creation of types, queries, and mutations --- @@ -199,6 +199,132 @@ convention in GraphQL APIs to use lowerCamelCase fields, so this is given by def [/notice] +### Bulk loading models + +It's likely that in your application, you have a whole collection of classes you want exposed to the API, with roughly +the same fields and operations exposed on them. It can be really tedious to write a new declaration for every single +dataobject in your project, and as you add new ones, there's a bit of overhead in remembering to add it to the +GraphQL schema. + +Common use cases might be: + +* Add everything in `App\Models` +* Add every implementation of `BaseElement` +* Add anything with the `Versioned` extension +* Add everything that matches `src/*Model.php` + +You can create logic like this using the `bulkLoad` configuration file, which allows you to specify groups of directives +that load a bundle of classes and apply the same set of configuration to all of them. + + +**_graphql/bulkLoad.yml** +```yaml +elemental: # An arbitrary key to define what these directives are doing + # Load all content blocks + load: + inheritanceLoader: + include: + - DNADesign\Elemental\Models\BaseElement + exclude: + - MyApp\Elements\MySecretElement + # Add all fields and read operations + apply: + fields: + '*': true + operations: + read: true + readOne: true +app: + # Load everything in our app that has the Versioned extension + load: + namespaceLoader: + include: + - MyApp\Models\* + extensionLoader: + include: + - SilverStripe\Versioned\Versioned + filepathLoader: + exclude: + - app/src/Models/*.secret.php + apply: + fields: + '*': true + operations: + '*': true +``` + +By default, four loaders are provided to you to help gather specific classnames: + +#### By namespace +* **Identifier**: `namespaceLoader` +* **Description**: Include or exclude classes based on their namespace +* **Example**: `include: [MyApp\Models\*]` + +#### By inheritance + +* **Identifier**: `inheritanceLoader` +* **Description**: Include or exclude everything that matches or extends a given base class +* **Example**: `include: [DNADesign\Elemental\Models\BaseElement]` + +#### By applied extension + +* **Identifier**: `extensionLoader` +* **Description**: Include or exclude any class that has a given extension applied +* **Example**: `include: [SilverStripe\Versioned\Versioned]` + +#### By filepath + +* **Identifier**: `filepathLoader` +* **Description**: Include or exclude any classes in files matching a given glob expression, relative to the base path. Module syntax is allowed. +* **Examples**: + - `include: [ 'src/models/*.model.php' ]` + - `include: [ 'somevendor/somemodule: src/Models/*.php' ]` + +Each block starts with a collection of all classes that gets filtered as each loader runs. The primary job +of a loader is to _remove_ classes from the entire collection, not add them in. + +[info] +`exclude` directives will always supersede `include` directives. +[/info] + +[info] +If you find that this paints with too big a brush, you can always override individual models explicitly in `models.yml`. +The bulk loaders run _before_ the models.yml config is loaded. +[/info] + +#### DataObjects subclasses are the default starting point + +Because this is Silverstripe CMS, and it's likely that you're using dataobject models only, the bulk loaders start with an +initial filter, which is defined as follows: + +```yaml +inheritanceLoader: + include: + - SilverStripe\ORM\DataObject +``` + +This ensures that at a bare minimum, you're always filtering by dataobject classes only. If, for some reason, you +have a non-dataobject class in `App\Models\*`, it will automatically be filtered out due to this default setting. + +This default is configured in the `defaultBulkLoad` setting in your schema config. Should you ever want to disable +that, just set it to `false`. + +**_graphql/config.yml** +``` +defaultBulkLoad: false +``` + +#### Creating your own bulk loader + +Bulk loaders must extend `SilverStripe\GraphQL\Schema\BulkLoader\AbstractBulkLoader`. They need to declare an +identifier (e.g. `namespaceLoader`) to be referenced in the config, and they must provide a +`collect(Collection $collection): Collection` which returns a new `Collection` instance once the loader has done its +work parsing through the `include` and `exclude` directives. + +Bulk loaders are automatically registered. Just creating the class is all you need to do to have it available for use +in your `bulkLoad.yml` file. + + ### Customising model fields You don't have to rely on the model to tell you how fields should resolve. Just like @@ -241,9 +367,9 @@ Page: paginateList: false # don't paginate the read operation ``` -### Blacklisted fields {#blacklisted-fields} +### Disallowed fields {#disallowed-fields} -While selecting all fields via `*` is usedful, there are some fields that you +While selecting all fields via `*` is useful, there are some fields that you don't want to accidentally expose, especially if you're a module author and expect models within this code to be used through custom GraphQL endpoints. For example, a module might add a secret "preview token" to each `SiteTree`. @@ -251,8 +377,8 @@ A custom GraphQL endpoint might have used `fields: '*'` on `SiteTree` to list pa on the public site, which now includes a sensitive field. The `graphql_blacklisted_fields` property on `DataObject` allows you to -blacklist fields globally for all GraphQL schemas. -This blacklist applies for all operations (read, update, etc). +disallow fields globally for all GraphQL schemas. +This block list applies for all operations (read, update, etc). **app/_config/graphql.yml** ```yaml