在我们试用GraphQL API及其与TinaCMS的工作方式时,很明显这是我们希望为Tina用户开辟的主要路径。我们将把tina-graphql-gateway
API移入tinacms
包中,以反映其在Tina工作流程中的更核心角色。其他后端集成仍然可以通过新的@tinacms/toolkit
包进行构建。您可以在这里阅读有关这些更改的更多详细信息。
tinacms
吸收tina-graphql-gateway
因此,升级到新的改进的GraphQL体验将需要您迁移到tinacms@0.50.0
并完全移除tina-graphql-gateway
包。
yarn add tinacms@latestyarn remove tina-graphql-gateway
- import { useGraphqlForms } from 'tina-graphql-gateway'+ import { useGraphqlForms } from 'tinacms'
tina-graphql-gateway-cli
现在是@tinacms/cli
yarn add @tinacms/cliyarn remove tina-graphql-gateway-cli
- import { defineSchema } from 'tina-graphql-gateway-cli'+ import { defineSchema } from 'tinacms'
注意:defineSchema
API也已更改。继续阅读以了解如何升级
tina-gql
命令行命令现在是tinacms
- yarn tina-gql server:start+ yarn tinacms server:start
defineSchema
API我们将依赖于Tina中类型定义的更原始概念,并因此对模式定义方式引入一些重大更改。
fields
或templates
属性您现在可以为集合提供fields
而不是templates
,这样做将导致更直接的模式定义:
{collections: [{name: 'post',label: 'Post',path: 'content/posts',fields: [{name: 'title',label: 'Title',type: 'string', // 了解下面关于_type_更改的更多信息},],},]}
为什么?
以前,一个集合可以定义多个模板,这一特性引入的模糊性意味着您的文档需要一个_template
字段,以便我们知道它们属于哪个模板。这也意味着必须在graphql中消除查询的歧义:
getPostDocument(relativePage: $relativePath) {data {...on Article_Doc_Data {title}}}
今后,如果您在集合上使用fields
,可以省略_template
键并简化查询:
getPostDocument(relativePage: $relativePath) {data {title}}
type
更改类型看起来会有些不同,旨在反映它们可以代表的最低形式。今后,ui
字段将代表您可能期望的UI部分。对于博客文章的“描述”字段,您可以这样定义:
{type: "string",label: "Description",name: "description",}
默认情况下,string
将使用text
字段,但您可以通过指定component
来更改:
{type: "string",label: "Description",name: "description",ui: {component: "textarea"}}
在大多数情况下,UI属性会添加到字段中,并遵循Tina核心字段插件的现有功能。但您可以提供自己的组件——只需确保在前端注册这些组件到CMS对象:
{type: "string",label: "Description",name: "description",ui: {component: "myMapField"someAdditionalMapConfig: 'some-value'}}
注册您的myMapField
到Tina:
cms.fields.add({name: 'myMapField',Component: MapPicker,})
defineSchema
API中的每个属性都必须是可序列化的。这意味着函数将不起作用。例如,无法在此级别定义validate
或parse
函数。但是,您可以使用formifyCallback API来访问Tina表单,或者通过指定您选择的插件来提供自己的逻辑:
{type: "string",label: "Description",name: "description",ui: {component: "myText"}}
然后在注册插件时,在这里提供您的自定义逻辑:
import { TextFieldPlugin } from 'tinacms'// ...cms.fields.add({...TextFieldPlugin, // 展开现有的文本插件name: 'myText',validate: value => {someValidationLogic(value)},})
为什么?
实际上,在底层这对后端没有任何影响,因此我们将其作为一个摩擦点移除。相反,type
是真正定义字段_形状_的定义,而ui
可以用于自定义字段UI的外观和行为。
type
都可以是一个列表以前,我们有一个list
字段,允许您提供一个field
属性。相反,_每个_原始类型都可以表示为一个列表:
{type: "string",label: "Categories",name: "categories",list: true}
此外,可枚举列表和选择项是从options
属性推断出来的。以下示例由一个select
字段表示:
{type: "string",label: "Categories",name: "categories",options: ["fitness", "movies", "music"]}
而这个,则是一个checkbox
字段
{type: "string",label: "Categories",name: "categories"list: true,options: ["fitness", "movies", "music"]}
object
类型Tina目前以两种方式表示_对象_的概念:group
(和group-list
),它是字段的统一集合;以及blocks
,它是多态集合。今后,我们将引入一个更全面的类型,它涵盖了group
和blocks
的行为,并且由于_每个_字段都可以是list
,这也使得group-list
变得多余。
注意:我们之前假设blocks
的使用_总是_作为一个数组。为了兼容性,我们将保留这种假设,但object
将允许非数组的多态对象。
object
类型object
类型接受fields
或templates
属性(就像collections
定义一样)。如果您提供fields
,您将得到一个本质上是group
的项目。如果您说list: true
,您将得到以前的group-list
定义。
同样,如果您提供一个templates
字段和list: true
,您将获得与blocks
相同的API。然而,您也可以说list: false
(或完全省略它),您将拥有一个非数组的多态对象。
注意 -type: object
与templates: []
和list: false
尚不支持表单生成。您可以在API中使用它,但无法编辑该字段。
这与当前的blocks
定义相同:
{type: "object",label: "Page Sections",name: "pageSections",list: true,templates: [{label: "Hero",name: "hero",fields: [{label: "Title",name: "title",type: "string"}]}]}
这是一个group
的例子:
{type: "object",label: "Hero",name: "hero",fields: [{label: "Title",name: "title",type: "string"}]}
dataJSON
字段您现在可以请求dataJSON
作为整个数据对象的单个查询键。这对于像主题文件这样繁琐的查询非常有用,因为在结果中包含每个项目很麻烦。
注意,目前此功能没有TypeScript帮助
getThemeDocument(relativePath: $relativePath) {dataJSON}
{"getThemeDocument": {"dataJSON": {"every": "field","in": {"the": "document"},"is": "returned"}}}
请记住,dataJSON
不会跨多个文档解析。相反,它将返回引用的外键:
{"getPostDocument": {"data": {"title": "Hello, World!","author": "path/to/author.md"}}}
以前,列表会返回一个简单的项目数组:
{getPostList {id}}
这将导致:
{"data": {"getPostList": [{"id": "content/posts/voteForPedro.md"}]}}
在新的API中,您需要通过edges
和nodes
:
{getPostList {edges {node {id}}}}
{"data": {"getPostList": {"edges": [{"node": {"id": "content/posts/voteForPedro.md"}}]}}}
为什么?
GraphQL连接规范打开了一个更具前瞻性的结构,允许我们将更多信息放入_连接_本身,例如返回了多少结果,以及如何请求下一页数据。
阅读详细解释以了解连接规范如何提供更丰富的功能集。
注意:列表查询仍然不支持排序和过滤。
_body
不再默认包含相反,可以将isBody
布尔值添加到任何string
字段
为什么?
由于markdown文件有一种隐含的“主体”,我们自动填充了一个代表markdown文件主体的字段。这并不是那么有用,而且有点烦人。相反,只需将isBody
附加到您希望代表markdown“主体”的字段:
{collections: [{name: "post",label: "Post",path: "content/posts",fields: [{name: "title",label: "Title",type: "string"}{name: "myBody",label: "My Body",type: "string",component: 'textarea',isBody: true}]}]}
这将导致一个名为My Body
的表单字段被保存到您的markdown文件的主体中(如果您使用的是markdown):
---title: Hello, World!---这是文件的主体,通过表单中的“My Body”字段进行编辑。
您现在必须定义一个collections
字段,它是一个数组,而不是一个collection
属性:
{type: "reference",label: "Author",name: "author",collections: ["author"]}
{getPostDocument(relativePath: "hello.md") {data {titleauthor {...on Author_Document {name}...on Post_Document {title}}}}
template
字段现在是_template
旧API:
---myBlocks:- template: herotitle: Hello---
新API:
---myBlocks:- _template: herotitle: Hello---
data
__typename
值已更改它们现在包括适当的命名空间以防止命名冲突,并且不再需要_Doc_Data
后缀。所有生成的__typename
属性将略有不同。我们没有完全命名空间字段,因此无法保证不会发生冲突。在查询和过滤块时,这里的痛苦可能最为明显。这确保了此类型在未来的稳定性
{getPageDocument(relativePath: "home.md") {data {titlemyBlocks {...on Page_Hero_Data { # 以前这会是Hero_Data# ...}}}}}
null
以前,未在文档中定义的可列字段被视为一个空数组。今后,API响应将导致null
而不是[]
:
---title: 'Hello, World'categories: []---
响应将是categories: []
。如果您完全省略该字段:
---title: 'Hello, World'---
响应将是categories: null
。以前这会是[]
,这是不正确的。