Динамически создавайте страницы с помощью Gatsby на основе множества содержательных ссылок.

В настоящее время я использую 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 content-type, который позволяет иметь несколько записей для поля:

модель содержательного контента для сообщений в блогах

Теперь результат моего запроса graphQL в поле category2 представляет собой массив разных категорий для каждого сообщения в блоге:

Запрос :

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

Думаю, мне придется вместо этого использовать API createPages.


person ozenK    schedule 06.04.2021    source источник


Ответы (1)


Добиться результата можно с помощью 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), который содержит два массива, slugs и names:

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

После этого вам нужно только перебрать результат запроса (result) и передать значения полей (name, slug и другие, если необходимо) в предыдущий массив, выполнив необходимые проверки, если хотите (чтобы избежать нажатия пустых значений , или которое соответствует какому-либо регулярному выражению и т. д.) и вернуть new Set, чтобы удалить дубликаты.

Затем вам нужно всего лишь перебрать ярлыки для создания страниц с использованием createPage API и передать необходимые данные через контекст:

  context: {
    name
  }

Из-за избыточности это то же самое, что:

  context: {
    name: name
  }

Итак, в вашем шаблоне вы получите имя в pageContext props. При необходимости замените его на slug, в зависимости от вашей ситуации и вашего варианта использования подход точно такой же.

person Ferran Buireu    schedule 06.04.2021
comment
Спасибо за подробный ответ! - person ozenK; 06.04.2021