Skip to main content

v4 代码迁移:更新内容类型架构

¥v4 code migration: Updating content-type schemas

本指南是 v4 代码迁移指南 的一部分,旨在帮助你将 Strapi 应用的代码从 v3.6.x 迁移到 v4.0.x

¥This guide is part of the v4 code migration guide designed to help you migrate the code of a Strapi application from v3.6.x to v4.0.x

Strapi v4 中的模型已被彻底修改:模型文件位于 /content-types/ 文件夹中,各种键和设置已被删除,并且关系语法已完全更改。

¥Models in Strapi v4 have been completely overhauled: model files are located in /content-types/ folders, various keys and settings have been removed, and the relation syntax has completely changed.

迁移到 Strapi v4 需要:

¥Migrating to Strapi v4 requires:

将模型转换为内容类型

¥Convert models to content-types

🤓 v3/v4 比较

Strapi v3 在 models 文件夹中找到的 <model-name>.settings.json 文件中声明模型。

¥Strapi v3 declares models in <model-name>.settings.json files found in a models folder.

在 Strapi v4 中,content-types./src/api/<apiName>/content-types/<contentTypeName> 文件夹中的 schema.json 文件中声明。schema.json 文件引入了一些新属性(请参阅 模式文档)。

¥In Strapi v4, content-types are declared in schema.json files found in ./src/api/<apiName>/content-types/<contentTypeName> folder. The schema.json files introduce some new properties (see schema documentation).

✏️ 注意

内容类型可以使用 交互式 CLI 命令 strapi generate 自动创建。

¥Content-types can be created automatically with the interactive CLI command strapi generate.

要将 Strapi v3 模型转换为 v4 内容类型:

¥To convert Strapi v3 models to v4 content-types:

  1. 将项目根目录下的 ./api 文件夹移动到 ./src 中:

    ¥Move the ./api folder at the root of your project into ./src:

    mkdir src # Only if you haven't created the `./src` folder
    mv api/ src/api/
  2. 将每个内容类型 models 文件夹移动/重命名为 ./src/api/<apiName>/content-types/

    ¥Move/rename each content-types models folder to ./src/api/<apiName>/content-types/:

    mv src/api/<apiName>/models/ src/api/<apiName>/content-types/
💡 提示

Strapi 代码模块 可用于将 v3 模型转换为 v4 内容类型。

¥Strapi codemods can be used to convert v3 models to v4 content-types.

  1. 将每个模型的 <modelName>.settings.json 文件移动/重命名为 ./src/api/<apiName>/content-types/<contentTypeName>/schema.json 文件。

    ¥Move/rename each model's <modelName>.settings.json file to ./src/api/<apiName>/content-types/<contentTypeName>/schema.json files.

  2. 在每个 <contentTypeName>/schema.json 文件中,更新 info 对象,现在需要声明 3 个新的 singularNamepluralNamedisplayName 键并遵守一些大小写格式约定:

    ¥In each <contentTypeName>/schema.json file, update the info object, which now requires declaring the 3 new singularName, pluralName and displayName keys and respecting some case-formatting conventions:

    ./src/api/<apiName>/content-types/<contentTypeName>/schema.json

    // ...
    "info": {
    "singularName": "content-type-name", // kebab-case required
    "pluralName": "content-type-names", // kebab-case required
    "displayName": "Content-type name",
    "name": "Content-type name",
    };
    // ...

更新内容类型关系

¥Updating content-type relations

🤓 v3/v4 比较

Strapi v3 使用模型设置中的 viamodelcollection 属性定义内容类型之间的关系。

¥Strapi v3 defines relations between content-types with the via, model and collection properties in the model settings.

在 Strapi v4 中,关系应在内容类型的 schema.json 文件中明确描述(参见 关系文档)。

¥In Strapi v4, relations should be explicitly described in the schema.json file of the content-types (see relations documentation).

如果内容类型有关系,则需要通过更新内容类型的 schema 来手动将它们迁移到 Strapi v4。

¥If the content-type has relations, it's required to manually migrate them to Strapi v4, by updating the schema of the content-types.

要更新内容类型关系,请使用以下过程更新每个内容类型的 ./src/api/<apiName>/content-types/<contentTypeName>/schema.json 文件:

¥To update content-type relations, update the ./src/api/<apiName>/content-types/<contentTypeName>/schema.json file for each content-type with the following procedure:

  1. 通过将 type 属性值设置为 "relation" 来显式声明关系。

    ¥Declare the relation explicitly by setting the type attribute value to "relation".

  2. 使用 relation 属性定义关系类型。
    该值应该是以下可能选项中的字符串:"oneToOne""oneToMany""manyToOne""manyToMany"

    ¥Define the type of relation with the relation property.
    The value should be a string among the following possible options: "oneToOne", "oneToMany", "manyToOne" or "manyToMany".

  3. 使用 target 属性定义内容类型目标。
    该值应该是遵循 api::api-name.content-type-nameplugin::plugin-name.content-type-name 语法约定的字符串。

    ¥Define the content-type target with the target property.
    The value should be a string following the api::api-name.content-type-name or plugin::plugin-name.content-type-name syntax convention.

  4. (可选)在 双向关系 中,为每个内容类型定义 mappedByinversedBy 属性。

    ¥(optional) In bidirectional relations, define mappedBy and inversedBy properties on each content-type.

Example of all possible relations between an article and an author content-types:
./src/api/article/content-types/article/schema.json

// Attributes for the Article content-type

// oneWay relation
"articleHasOneAuthor": {
"type": "relation",
"relation": "oneToOne",
"target": "api::author.author"
},
// oneToOne relation
"articleHasAndBelongsToOneAuthor": {
"type": "relation",
"relation": "oneToOne",
"target": "api::author.author",
"inversedBy": "article"
},
// oneToMany relation
"articleBelongsToManyAuthors": {
"type": "relation",
"relation": "oneToMany",
"target": "api::author.author",
"mappedBy": "article"
},
// manyToOne relation
"authorHasManyArticles": {
"type": "relation",
"relation": "manyToOne",
"target": "api::author.author",
"inversedBy": "articles"
},
// manyToMany relation
"articlesHasAndBelongsToManyAuthors": {
"type": "relation",
"relation": "manyToMany",
"target": "api::author.author",
"inversedBy": "articles"
},
// manyWay relation
"articleHasManyAuthors": {
"type": "relation",
"relation": "oneToMany",
"target": "api::author.author"
}
./src/api/author/content-types/author/schema.json

// Attributes for the Author content-type

// inversed oneToMany relation
"article": {
"type": "relation",
"relation": "manyToOne",
"target": "api::article.article",
"inversedBy": "articleBelongsToManyAuthors"
},
// inversed manyToOne or manyToMany relation
"articles": {
"type": "relation",
"relation": "manyToMany",
"target": "api::article.article",
"inversedBy": "articlesHasAndBelongsToManyAuthors"
}

更新生命周期钩子

¥Updating lifecycle hooks

🤓 v3/v4 比较

Strapi v3 在 models 文件夹中找到的 <model-name>.js 文件中声明模型生命周期钩子。

¥Strapi v3 declares model lifecycle hooks in <model-name>.js files found in a models folder.

在 Strapi v4 中,生命周期钩子./src/api/<apiName>/content-types/<contentTypeName>/ 文件夹中的 lifecycles.js 文件中声明。lifecycles.js 文件在结构上类似,但不再需要将生命周期封装在 lifecycles: {} 对象中,并且新参数将传递给钩子(请参阅 生命周期钩子文档)。

¥In Strapi v4, lifecycle hooks are declared in a lifecycles.js file found in ./src/api/<apiName>/content-types/<contentTypeName>/ folder. The lifecycles.js file is similar in structure but no longer needs lifecycles to be wrapped in a lifecycles: {} object, and new parameters are passed to the hooks (see lifecycle hooks documentation).

要将 Strapi v3 模型生命周期钩子转换为 v4 生命周期钩子:

¥To convert Strapi v3 model lifecycle hooks to v4 lifecycle hooks:

  1. ./src/api/<apiName>/content-types/ 中的 <modelName>.js 移动/重命名到你在内容类型迁移的 步骤 3 中创建的正确内容类型文件夹,同时将其名称更改为 lifecyles.js

    ¥Move/rename the <modelName>.js in ./src/api/<apiName>/content-types/ to the proper content-type folder you created in step 3 of the content-type migration, while changing its name to lifecyles.js:

    cd src/api/<apiName>
    mv content-types/<modelName>.js content-types/<contentTypeName>/lifecycles.js
  2. 在每个 lifecycles.js 文件中,调整结构并将每个生命周期移到旧 lifecycles: {} 对象之外,如下例所示:

    ¥In each lifecycles.js file, adjust the structure and move each lifecycle outside of the legacy lifecycles: {} object, like in the following examples:

Example of a Strapi v3 lifecycles file:
```jsx
module.exports = {
lifecycles: {
async beforeCreate() {
// ...
},
},
};
```

Example of a Strapi v4 lifecycles file:
```jsx
module.exports = {
async beforeCreate() {
// ...
},
};
```

  1. 重构模型生命周期钩子以使用新的输入变量(参见 钩子 event 对象文档):

    ¥Refactor the model lifecycle hooks to use the new input variables (see hook event object documentation):

  • 所有 Strapi v3 params 都放置在 Strapi v4 中的 event 对象中(例如 event.params)。

    ¥All Strapi v3 params are placed in an event object in Strapi v4 (e.g. event.params).

  • 嵌套在此 params 对象内部,你可以访问 dataselect(也称为字段)、where(也称为过滤器)、orderBy(也称为排序)、limitoffsetpopulate

    ¥Nested inside of this params object, you have access to data, select (also known as fields), where (also known as filters), orderBy (also known as sort), limit, offset, and populate.

  • 或者,对于所有 after* 事件,你可以访问包含数据库结果响应的 event.result

    ¥Optionally, for all after* events, you have access to event.result that contains the result response from the database.

Example of a Strapi v3 lifecycle:
module.exports = {
lifecycles: {
async beforeCreate(data) {
data.isTableFull = data.numOfPeople === 4;
},
async afterCreate(result, data) {
// do something with result
}
},
};
Example of a Strapi v4 lifecycle:
module.exports = {
beforeCreate(event) {
let { data, where, select, populate } = event.params;

data.isTableFull = data.numOfPeople === 4;
},

afterCreate(event) {
const { result, params } = event;

// do something to the result
},
};
🤓 下一步

Strapi 的 迁移后端代码 到 v4 还需要至少迁移 Strapi 服务器的核心功能,例如 configurationdependenciesroutescontrollersservices

¥Migrating the backend code of Strapi to v4 also requires to at least migrate the core features of the Strapi server, such as the configuration, dependencies, routes, controllers, and services.