In some cases, inheritance can help to not repeating yourself, better organisation, clearer schema, easier interfaces implementation...
Imagine you have something like that:

This is how we will implement it:
# First, let's define our mother class
Character:
type: object
heirs: # the opposite of « inherits »
# optional if « inherits » already exists on daughters classes
- CharacterWarrior
- CharacterWizard
config:
fields:
id: {type: Int!}
type: {type: String!}
name: {type: String!}
staminaPoints: {type: Int!}
# Then let's define our daughters classes
CharacterWarrior:
type: object
inherits: [Character] # the opposite of « heirs »
# optional if « heirs » already exists on mother class
config:
fields:
furyPoints: {type: Int!}
CharacterWizard:
type: object
inherits: [Character] # the opposite of « heirs »
# optional if « heirs » already exists on mother class
config:
fields:
magicPoints: {type: Int!}
This is equivalent to:
CharacterWarrior:
type: object
config:
fields:
id: {type: Int!}
type: {type: String!}
name: {type: String!}
staminaPoints: {type: Int!}
furyPoints: {type: Int!}
CharacterWizard:
type: object
config:
fields:
id: {type: Int!}
type: {type: String!}
name: {type: String!}
staminaPoints: {type: Int!}
magicPoints: {type: Int!}
« So, we defined our types, but how can I query furyPoints field for CharacterWarrior only? »
To do that, you should use Inline Fragments, e.g.:
{
characters {
id
type
name
staminaPoints
... on CharacterWarrior {
furyPoints
}
... on CharacterWizard {
magicPoints
}
}
}
« But it does not work??? »
Yes, we should refactor our types:
Character will be an interfaceCharacterWarrior and CharacterWizard will implement the interface CharacterCharacterWarrior and CharacterWizard will extend configuration fields of interface CharacterCharacter:
type: interface
config:
# depending of `value.type`, this resolver should
# returns `CharacterWarrior` or `CharacterWizard`
resolveType: "@=query('character_type_resolver', value.type)"
fields:
id: {type: Int!}
type: {type: String!}
name: {type: String!}
staminaPoints: {type: Int!}
CharacterWarrior:
type: object
inherits: [Character] # We don't have to implement all `Character` fields
config:
interfaces: [Character] # `CharacterWarrior` implements `Character` interface
fields:
furyPoints: {type: Int!}
CharacterWizard:
type: object
inherits: [Character] # We don't have to implement all `Character` fields
config:
interfaces: [Character] # `CharacterWizard` implements `Character` interface
fields:
magicPoints: {type: Int!}
Make sure to register CharacterWarrior and CharacterWizard as types in your overblog_graphql configuration, otherwise it cannot be found:
overblog_graphql:
definitions:
schema:
query: Query
types: [CharacterWarrior, CharacterWizard]
Notes:
object types can also inherit from
interface typeheirs is the inverse of inherits sectioninherits section.CharacterWizard config is the result of
array_replace_recursive(CharacterConfig, CharacterWizardConfig)Decorators:
You can also create decorator types to be used as reusable templates. Decorators are only virtual and will not exists in final schema. That is the reason why decorator should never be reference as type in schema definition.
Here is an example of a decorator type:
ObjectA:
decorator: true
config:
fields:
bar: {type: String!}
You can use interfaces with decorators. Because they are only virtual, you do not have to implement the interface on the decorator itself. But you have to implement it on the type you decorate.
Imagine the following situation:
Node:
type: 'interface'
config:
# [...] resolveType logic unimportant here
fields:
id:
type: 'ID!'
# [...] resolve logic unimportant here
NodeEditPermission:
type: 'object'
decorator: true
config:
# This interface must be implemented by type which inherits from this decorator
interfaces: ["Node"]
fields:
canEdit:
type: 'Boolean!'
# The `value` is enforced to implement "Node" no matter which type uses this decorator.
# The resolver could be for example a (cached) user of symfony/security authorization checker
# which does ->isGranted([attribute] $attribute, [subject] $value)
resolve: '@=query("Permission.nodeAttribute", "edit", value)'
Product:
type: 'object'
inherits:
- 'NodeEditPermission'
# - 'NodeRemovePermission'
# - [...]
config:
fields:
# Must implement "Node" interface because "NodeEditPermission" decorator requires it
# Not implementing would raise an graphql error
id:
type: 'ID!'
How can I help you explore Laravel packages today?