基于许多 Contentful 引用使用 Gatsby 动态创建页面

Dynamically create pages with Gatsby based on many Contentful references

我目前正在使用 Gatsby collection routes API 使用来自 Contentful 的数据为一个简单的博客创建页面。

例如,为每个博文类别创建一个页面:

-- src/pages/categories/{contentfulBlogPost.category}.js

export const query = graphql`
  query categoriesQuery($category: String = "") {
    allContentfulBlogPost(filter: { category: { eq: $category } }) {
      edges {
        node {
          title
          category
          description {
            description
          }
        ...
        }
      }
    }
  }

...
[React component mapping all blogposts from each category in a list]
...

这工作正常。

但现在我希望每篇博文有多个类别,所以我切换到 Contentful 的 references, many 内容类型,它允许一个字段有多个条目:

现在,我在字段 category2 上的 graphQL 查询结果是每个博文的不同类别数组:

查询:

query categoriesQuery {
  allContentfulBlogPost {
    edges {
      node {
        category2 {
          id
          name
          slug
        }
      }
    }
  }
}

输出:

{
  "data": {
    "allContentfulBlogPost": {
      "edges": [
        {
          "node": {
            "category2": [
              {
                "id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
                "name": "Test",
                "slug": "test"
              },
              {
                "id": "568r9e48-t1i8-sx4t8-9742-cdf70c4ed789vtu",
                "name": "Test2",
                "slug": "test-2"
              }
            ]
          }
        },
        {
          "node": {
            "category2": [
              {
                "id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
                "name": "Test",
                "slug": "test"
              }
            ]
          }
        },
...

既然类别在数组中,我不知道如何:

对于我正在做的博文作者:

  query authorsQuery($author__slug: String = "") {
    allContentfulBlogPost(filter: { author: { slug: { eq: $author__slug } } }) {
      edges {
        node {
          id
          author {
            slug
            name
          }
          ...
        }
      ...
      }

并使用 src/pages/authors/{contentfulBlogPost.author__slug}.js

创建页面

我想我必须改用 createPages API。

您可以使用文件系统 API 获得结果,像这样的方法可能有效:

src/pages/category/{contentfulBlogPost.category2__name}.js

在这种情况下,似乎这种方法可能会导致一些注意事项,因为您可能会创建具有相同 URL (slug) 的重复页面,因为帖子可能包含多个重复的类别。

但是,我认为如您所说,使用 createPages API 更简洁,请记住您需要处理类别以避免重复,因为它们位于 一对多关系。

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
  const result = await graphql(`
    query {
     allContentfulBlogPost {
        edges {
          node {
            category2 {
              id
              name
              slug
            }
          }
        }
      }
    }
  `)
  
  let categories= { slugs: [], names: [] };

  result.data.allContentfulBlogPost.edges.map(({node}))=> {
   let { name, slug } = node.category2;
   // make some checks if needed here

    categories.slugs.push(slug);
    categories.names.push(name);

    return new Set(categories.slugs) && new Set(categories.names);
  });

  categories.slugs.forEach((category, index) => {
    let name = categories.names[index];

    createPage({
      path: `category/${category}`,
      component: path.resolve(`./src/templates/your-category-template.js`),
      context: {
        name
      }
    });
  });
}

该代码不言自明。基本上,您正在定义一个包含两个数组的空对象 (categories),slugsnames:

  let categories= { slugs: [], names: [] };

之后,您只需要循环查询结果(result)并将字段值(nameslug和其他需要的值推送到前一个数组,如果需要进行必要的检查(以避免推送空值,或匹配某些正则表达式等)和 return a new Set 以删除重复项。

然后,您只需要循环遍历 slug 以使用 createPage API 创建页面并通过上下文传递所需的数据:

  context: {
    name
  }

由于冗余,这与执行相同:

  context: {
    name: name
  }

因此,在您的模板中,您将获得 pageContext props 中的名称。如果需要,将其替换为 slug,具体取决于您的情况和用例,方法完全相同。