silverstripe-framework/docs/en/02_Developer_Guides/19_GraphQL/02_working_with_dataobjects/04_inheritance.md

141 lines
4.1 KiB
Markdown
Raw Normal View History

WIP: Add new graphql 4 docs (#9652) * DOCS: Add new graphql 4 docs * Reorganise docs * Docs done * Basic graphql index page * TOC for getting started * show folders on graphql index page * Add middleware note * Docs update * Update docs to reflect flushless schema * Docs updates * Docs for getByLink * Query caching docs * Docs on nested operations * update docs for new graphql dev admin * Docs for configurable operations * Replace readSiteTrees with readPages * Schema defaults docs * Docs for inherited plugins * Docs for customising * * Docs for field whitelisting * Change whitelist word * New docs on modelConfig * Document dev/build extension * Document default/global plugins * Document new input type fields config * Apply suggestions from code review Co-authored-by: Andre Kiste <bergice@users.noreply.github.com> * Note about when procedural schema gets built * Fix link * Apply suggestions from code review Co-authored-by: Andre Kiste <bergice@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Andre Kiste <bergice@users.noreply.github.com> * DOCS Note about plugins in custom queries * DOCS Note about filter and custom resolvers * DOCS Note about canview paging * DOCS Updated guidance on _extend See https://github.com/silverstripe/silverstripe-graphql/issues/296 * Apply suggestions from code review Co-authored-by: Andre Kiste <bergice@users.noreply.github.com> * DOCS Pre-release warning Co-authored-by: Ingo Schommer <ingo@silverstripe.com> Co-authored-by: Andre Kiste <bergice@users.noreply.github.com> Co-authored-by: Ingo Schommer <me@chillu.com>
2020-10-19 23:56:17 +02:00
---
title: DataObject inheritance
summary: Learn how inheritance is handled in DataObject types
---
# Working with DataObjects
[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]
## DataObject inheritance
The inheritance pattern used in the ORM is a tricky thing to navigate in a GraphQL API, mostly owing
to the fact that there is no concept of inheritance in GraphQL types. The main tools we have at our
disposal are [interfaces](https://graphql.org/learn/schema/#interfaces) and [unions](https://graphql.org/learn/schema/#union-types) to deal with this type of architecture, but in practise, it quickly becomes cumbersome.
For instance, just adding a subclass to a DataObject can force the return type to change from a simple list
of types to a union of multiple types, and this would break frontend code.
While more conventional, unions and interfaces introduce more complexity, and given how much we rely
on inheritance in Silverstripe CMS, particularly with `SiteTree`, inheritance in GraphQL is handled in a less
conventional but more ergonomic way using a plugin called `inheritance`.
### Introducing pseudo-unions
Let's take a simple example. Imagine we have this design:
```
> SiteTree (fields: title, content)
> Page (fields: pageField)
> NewsPage (fields: newsPageField)
> Contact Page (fields: contactPageField)
```
Now, let's expose `Page` to graphql:
*app/_graphql/models.yml*
```yaml
Page:
fields:
title: true
content: true
pageField: true
operations: '*'
NewsPage:
fields:
newsPageField: true
```
Here's how we can query the inherited fields:
```graphql
query readPages {
nodes {
title
content
pageField
_extend {
NewsPage {
newsPageField
}
}
}
}
```
The `_extend` field is semantically aligned with is PHP counterpart -- it's an object whose fields are the
names of all the types that are descendants of the parent type. Each of those objects contains all the fields
on that type, both inherited and native.
[info]
The `_extend` field is only available on base classes, e.g. `Page` in the example above.
[/info]
### Implicit exposure
By exposing `Page`, we implicitly expose *all of its ancestors* and *all of its descendants*. Adding `Page`
to our schema implies that we also want its parent `SiteTree` in the schema (after all, that's where most of its fields
come from), but we also need to be mindful that queries for page will return descendants of `Page`, as well.
But these types are implicitly added to the schema, what are their fields?
The answer is *only the fields you've already opted into*. Parent classes will apply the fields exposed
by their descendants, and descendant classes will only expose their ancestors' exposed fields.
If you are opting into all fields on a model (`fields: "*"`), this only applies to the
model itself, not its subclasses.
In our case, we've exposed:
* `title` (on `SiteTree`)
* `content` (on `SiteTree`)
* `pageField` (on `Page`)
* `newsPageField` (on `NewsPage`)
The `Page` type will contain the following fields:
* `id` (required for all DataObject types)
* `title`
* `content`
* `pageField`
And the `NewsPage` type would contain the following fields:
* `newsPageField`
[info]
Operations are not implicitly exposed. If you add a `read` operation to `SiteTree`, you will not get one for
`NewsPage` and `ContactPage`, etc. You have to opt in to those.
[/info]
### Pseudo-unions fields are de-duped
To keep things tidy, the pseudo unions in the `_extend` field remove any fields that are already in
the parent.
```graphql
query readPages {
nodes {
title
content
_extend {
NewsPage {
title <---- Doesn't exist
newsPageField
}
}
}
}
```
### Further reading
[CHILDREN]