Loving Tina? us on GitHub0.0k

文档

学习

v.Latest
Documentation
从GraphQL网关到TinaCMS
目录

包重组

在我们试用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@latest
yarn remove tina-graphql-gateway
- import { useGraphqlForms } from 'tina-graphql-gateway'
+ import { useGraphqlForms } from 'tinacms'
注意,useGraphqlForms现在被认为是一个较低级别的API。您可能希望改为使用tinacms的默认导入来配置内容。了解更多

tina-graphql-gateway-cli现在是@tinacms/cli

yarn add @tinacms/cli
yarn 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中类型定义的更原始概念,并因此对模式定义方式引入一些重大更改。

集合现在接受fieldstemplates属性

您现在可以为集合提供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中的每个属性都必须是可序列化的。这意味着函数将不起作用。例如,无法在此级别定义validateparse函数。但是,您可以使用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,它是多态集合。今后,我们将引入一个更全面的类型,它涵盖了groupblocks的行为,并且由于_每个_字段都可以是list,这也使得group-list变得多余。

注意:我们之前假设blocks的使用_总是_作为一个数组。为了兼容性,我们将保留这种假设,但object将允许非数组的多态对象。
定义object类型

object类型接受fieldstemplates属性(就像collections定义一样)。如果您提供fields,您将得到一个本质上是group的项目。如果您说list: true,您将得到以前的group-list定义。

同样,如果您提供一个templates字段和list: true,您将获得与blocks相同的API。然而,您也可以说list: false(或完全省略它),您将拥有一个非数组的多态对象。

注意 - type: objecttemplates: []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"
}
}
}

列表查询现在将遵循GraphQL连接规范

阅读规范

以前,列表会返回一个简单的项目数组:

{
getPostList {
id
}
}

这将导致:

{
"data": {
"getPostList": [
{
"id": "content/posts/voteForPedro.md"
}
]
}
}

在新的API中,您需要通过edgesnodes

{
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 {
title
author {
...on Author_Document {
name
}
...on Post_Document {
title
}
}
}
}

多态对象(以前称为_blocks_)上的template字段现在是_template

旧API:

---
myBlocks:
- template: hero
title: Hello
---

新API:

---
myBlocks:
- _template: hero
title: Hello
---

data __typename值已更改

它们现在包括适当的命名空间以防止命名冲突,并且不再需要_Doc_Data后缀。所有生成的__typename属性将略有不同。我们没有完全命名空间字段,因此无法保证不会发生冲突。在查询和过滤块时,这里的痛苦可能最为明显。这确保了此类型在未来的稳定性

{
getPageDocument(relativePath: "home.md") {
data {
title
myBlocks {
...on Page_Hero_Data { # 以前这会是Hero_Data
# ...
}
}
}
}
}

未定义的列表字段将返回null

以前,未在文档中定义的可列字段被视为一个空数组。今后,API响应将导致null而不是[]

---
title: 'Hello, World'
categories: []
---

响应将是categories: []。如果您完全省略该字段:

---
title: 'Hello, World'
---

响应将是categories: null。以前这会是[],这是不正确的。