mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
DOCS: improve graphql inheritance docs
This commit is contained in:
parent
0537e769fc
commit
29320841c9
47
.idea/workspace.xml
generated
47
.idea/workspace.xml
generated
@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="49c2db15-d223-4efe-b1e8-2d4ecfdaa74a" name="Default Changelist" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/docs/en/00_Getting_Started/index.md" beforeDir="false" afterPath="$PROJECT_DIR$/docs/en/00_Getting_Started/index.md" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ComposerSettings" synchronizationState="SYNCHRONIZE">
|
||||
<pharConfigPath>$PROJECT_DIR$/composer.json</pharConfigPath>
|
||||
<execution />
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectId" id="1ueqw0dJJFnqPFjpLOJN1mIgTXB" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
||||
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
|
||||
<property name="vue.rearranger.settings.migration" value="true" />
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="49c2db15-d223-4efe-b1e8-2d4ecfdaa74a" name="Default Changelist" comment="" />
|
||||
<created>1625036637006</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1625036637006</updated>
|
||||
<workItem from="1625036638810" duration="456000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
@ -73,8 +73,8 @@ But what about when we want more than `title` and `content`? In some cases, we'l
|
||||
query {
|
||||
readPages {
|
||||
nodes {
|
||||
title
|
||||
content
|
||||
title # Common field
|
||||
content # Common field
|
||||
... on HomePage {
|
||||
heroImage {
|
||||
url
|
||||
@ -98,22 +98,23 @@ that only appear on some types, we need to be explicit.
|
||||
But let's take this a step further. What if there's another class in between? Imagine this ancestry:
|
||||
|
||||
```
|
||||
Page:
|
||||
EventPage:
|
||||
ConferencePage
|
||||
WebinarPage
|
||||
Page
|
||||
-> EventPage
|
||||
-> ConferencePage
|
||||
-> WebinarPage
|
||||
```
|
||||
|
||||
We can use the intermediary interface `EventPageInterface` to consolidate fields that are unique to
|
||||
`ConferencePage` and `WebinarPage`.
|
||||
|
||||
```
|
||||
```graphql
|
||||
query {
|
||||
readPages {
|
||||
nodes {
|
||||
title
|
||||
content
|
||||
title # Common to all types
|
||||
content # Common to all types
|
||||
... on EventPageInterface {
|
||||
# Common fields for WebinarPage, ConferencePage, EventPage
|
||||
numberOfTickets
|
||||
featuredSpeaker {
|
||||
firstName
|
||||
@ -147,10 +148,12 @@ which is usually the parent class with the "Interface" suffix.
|
||||
|
||||
### Inheritance: A deep dive
|
||||
|
||||
There are two components to the way inheritance is handled at build time:
|
||||
There are several ways inheritance is handled at build time:
|
||||
|
||||
* Implicit field / type exposure
|
||||
* Interface generation and assignment to types
|
||||
* Interface generation
|
||||
* Assignment of generated interfaces to types
|
||||
* Assignment of generated interfaces to queries
|
||||
|
||||
We'll look at each of these in detail.
|
||||
|
||||
@ -180,7 +183,7 @@ GalleryPage:
|
||||
|
||||
This results in these two types being exposed with the fields as shown, but also results in a `Page` type:
|
||||
|
||||
```
|
||||
```graphql
|
||||
type Page {
|
||||
id: ID! # always exposed
|
||||
title: String
|
||||
@ -189,7 +192,7 @@ type Page {
|
||||
}
|
||||
```
|
||||
|
||||
#### Interface generation and assignment to types
|
||||
#### Interface generation
|
||||
|
||||
Any type that's part of an inheritance chain will generate interfaces. Each applicable ancestral interface is added
|
||||
to the type. Like the type inheritance pattern shown above, interfaces duplicate fields from their ancestors as well.
|
||||
@ -202,16 +205,16 @@ All of this is serviced by: `SilverStripe\GraphQL\Schema\DataObject\InterfaceBui
|
||||
##### Example
|
||||
|
||||
```
|
||||
Page:
|
||||
BlogPage extends Page
|
||||
EventsPage extends Page
|
||||
ConferencePage extends EventsPage
|
||||
WebinarPage extends EventsPage
|
||||
Page
|
||||
-> BlogPage extends Page
|
||||
-> EventsPage extends Page
|
||||
-> ConferencePage extends EventsPage
|
||||
-> WebinarPage extends EventsPage
|
||||
```
|
||||
|
||||
This will create the following interfaces:
|
||||
|
||||
```
|
||||
```graphql
|
||||
interface PageInterface {
|
||||
title: String
|
||||
content: String
|
||||
@ -249,9 +252,11 @@ interface WebinarPageInterface {
|
||||
}
|
||||
```
|
||||
|
||||
Types then get these interfaces applied, like so:
|
||||
#### Interface assignment to types
|
||||
|
||||
```
|
||||
The generated interfaces then get applied to the appropriate types, like so:
|
||||
|
||||
```graphql
|
||||
type Page implements PageInterface {}
|
||||
type BlogPage implements BlogPageInterface & PageInterface {}
|
||||
type EventsPage implements EventsPageInterface & PageInterface {}
|
||||
@ -261,17 +266,46 @@ type WebinarPage implements WebinarPageInterface & EventsPageInterface & PageInt
|
||||
|
||||
Lastly, for good measure, we create a `DataObjectInterface` that applies to everything.
|
||||
|
||||
```
|
||||
```graphql
|
||||
interface DataObjectInterface {
|
||||
id: ID!
|
||||
# Any other fields you've explicitly exposed in config.modelConfig.DataObject.base_fields
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
```graphql
|
||||
type Page implements PageInterface & DataObjectInterface {}
|
||||
```
|
||||
|
||||
#### Interface assignment to queries
|
||||
|
||||
Queries, both at the root, and nested as fields on types, will have their types
|
||||
updated if they refer to a type that has had any generated interfaces added to it.
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
readPages: [Page]
|
||||
}
|
||||
|
||||
type BlogPage {
|
||||
download: File
|
||||
}
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
readPages: [PageInterface]
|
||||
}
|
||||
|
||||
type BlogPage {
|
||||
download: FileInterface
|
||||
}
|
||||
```
|
||||
|
||||
All of this is serviced by: `SilverStripe\GraphQL\Schema\DataObject\InterfaceBuilder`
|
||||
|
||||
#### Elemental
|
||||
|
||||
Almost by definition, content blocks are always abstractions. You're never going to query for a `BaseElement` type
|
||||
|
Loading…
Reference in New Issue
Block a user