3

假设我在创建为 Strapi 内容类型的 Author 类型架构上有“ firstName ”和“ lastName ”属性。

我可以使用 graphql 查询它们,但是如果我想查询“ fullName ”属性而不在我的内容类型上添加该字段怎么办?

由于字段不存在,现在它说:Cannot query field \"fullName\" on type \"Author\"

如何使用附加的“虚拟”字段扩展现有类型模式?

4

3 回答 3

6

我设法使用位于api/author/config文件夹中的schema.graphql文件中的以下代码来做到这一点:

module.exports = {
  definition: `type AuthorOverride {
  firstName: String
  lastName: String
  fullName: String
}`,
  query: `
    authors: [AuthorOverride]
  `,
  type: {
    Author: false
  },
  resolver: {
    Query: {
      authors: {
        description: 'Return the authors',
        resolver: 'Author.find'
      }
    }
  }
};

关键是在使用不同类型名称(AuthorOverride)的同时使用附加字段定义模式以避免重复类型错误。

此外,设置 type: { Author: false } 以便原始类型不可查询。

现在,在我的解析器函数“Author.find”(放置在我的 Author.js 控制器中)中,我可以映射 fullName 值。

如果有人有更合适的解决方案来在 Strapi 中扩展 graphql 模式,请随时发布。

于 2019-11-02T19:06:24.620 回答
2

刚刚找到这个帖子,也找到了合适的解决方案。这个示例 repo演示了如何使用带有自定义控制器方法和自定义 GraphQL 模式的服务函数来获得你想要的东西。我刚刚在自己的项目中实现了相同的功能。

您的案例不需要服务功能。你只需要做两件事:

  1. 如下定义fullName属性/api/authors/config/schema.graphql.js
module.exports = {
  definition:
    extend type Author {
      fullName: AuthorFullName
    }

    type AuthorFullName {
      firstName: String
      lastName: String
    }
  `,
};
  1. 接下来,您需要重写findfindOne控制器方法,Author如下所示:
module.exports = {
  async find( ctx ) {
    let entities;

    if ( ctx.query._q ) {
      entities = await strapi.services.author.search( ctx.query );
    } else {
      entities = await strapi.services.author.find( ctx.query );
    }

    // Add computed field `fullName` to entities.
    entities.map( entity => {
      entity.fullName = `${entity.firstName} ${entity.lastName}`;

      return entity;
    } );

    return entities.map( entity => sanitizeEntity( entity, { model: strapi.models.author } ) );
  },

  async findOne( ctx ) {
    const { id } = ctx.params;
    let entity = await strapi.services.author.findOne( { id } );

    if ( ! entity ) {
      return ctx.notFound();
    }

    // Add computed field `fullName` to entity.
    entity.fullName = `${entity.firstName} ${entity.lastName}`;

    return sanitizeEntity( entity, { model: strapi.models.author } );
  },
};

这允许 REST API 调用获取fullName返回值,并告诉 GraphQL 也将其包含fullName在其架构中,以便正确findfindOne将其传递给 GraphQL。

我希望这会有所帮助,因为我觉得我在学习了这个之后就升级了很多!

于 2021-03-27T05:01:19.420 回答
0

这些都不适合我。从docs看来,它们似乎已更改v4为在全局上下文中扩展,而不是按模块/实体 ( src/api/...)。像这样:

// src/index.js

"use strict";

module.exports = {
  register({ strapi }) {
    const extensionService = strapi.plugin("graphql").service("extension");

    const extension = () => ({
      typeDefs: `
      type Author {
        fullName: String
      }
      `,
      resolvers: {
        Author: {
          fullName(author) {
            return strapi.service("api::author.author").getFullName(author);
          },
        },
      },
    });

    extensionService.use(extension);
  },
};

这可能是错误的,我希望是这样。在我看来,从架构上讲,这是一个后退阶段。但是,您始终可以将此逻辑添加到每个实体文件夹(例如src/api/graphql/index.js)中的文件中,然后将其导入此全局文件。有点像上面的服务逻辑为了保持关注点分离而发生的事情。它只是有点“手动”。

对于那些想看看什么strapi.service("api::author.author").getFullName(author)在召唤的人:

// src/api/author/services/author.js

"use strict";

const { createCoreService } = require("@strapi/strapi").factories;

module.exports = createCoreService("api::author.author", () => ({
  getFullName(author) {
    return `${author.firstName} ${author.lastName}`;
  },
}));

于 2022-01-15T12:04:52.583 回答