在Contentful CMS中,我有两种不同的内容类型:BigCaseStudy
和BigCaseStudySection
。为了让这些内容出现在我的Gatsby 2.x网站上,我的想法是:
BigCaseStudy
字段,还包含内容的ID
字段作为元数据。ID
,匹配查询2中的Contentful引用字段(包含ID
)BigCaseStudySection
字段最终目标是显示所有BigCaseStudy
的原始BigCaseStudySection
(通常在3-5个之间编号)。您可以查看我的查询以查看字段,有一堆。
我认为GraphQL变量和查询的某些组合会让我(可能是一个变异)?目前尚不清楚,我还没有看到任何查询一组内容然后使用响应进行另一次调用的复杂示例,如JS链接的promises或async / await。
关于正确建设的任何想法?
带有GraphQL查询的bigcasestudy.js
组件:
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { graphql } from 'gatsby'
import Img from 'gatsby-image'
import Layout from '../Layout/layout'
/**
* Hero Section
*/
const HeroContainer = styled.header`
align-items: center;
background-image: url(${ props => props.bgImgSrc });
background-position: center center;
background-size: cover;
display: flex;
flex-direction: column;
justify-content: center;
height: calc(100vh - 128px);
`
const HeroTitle = styled.h1`
color: #fff;
font-size: 70px;
font-weight: 700;
letter-spacing: 2px;
margin-bottom: 15px;
`
const HeroSubtitle = styled.h2`
color: #fff;
font-size: 24px;
font-weight: 300;
letter-spacing: 5px;
text-transform: uppercase;
`
/**
* Intro Section
*/
const IntroBG = styled.section`
background-color: ${ props => props.theme.lightGray };
padding: 50px 0;
`
const IntroContainer = styled.div`
padding: 25px;
margin: 0 auto;
max-width: ${ props => props.theme.sm };
@media (min-width: ${ props => props.theme.sm }) {
padding: 50px 0;
}
`
const IntroTitle = styled.h2`
font-size: 50px;
font-weight: 700;
letter-spacing: 2px;
margin-bottom: 45px;
text-align: center;
`
const IntroText = styled.p`
font-size: 22px;
line-spacing: 4;
text-align: center;
`
const IntroButton = styled.a`
background-color: #fff;
color: ${ props => props.theme.darkGray };
border: 1px solid ${ props => props.theme.mediumGray };
border-radius: 25px;
display: block;
font-size: 16px;
letter-spacing: 5px;
margin: 30px auto;
padding: 15px 45px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
width: 300px;
`
// BigCaseStudy Component
class BigCaseStudy extends React.Component {
render() {
// Setup destructured references to each Contentful object passed in through the GraphQL call
const { caseStudyTitle } = this.props.data.contentfulBigCaseStudy
const { caseStudySubtitle } = this.props.data.contentfulBigCaseStudy
const { caseStudyIntroTitle } = this.props.data.contentfulBigCaseStudy
const { caseStudyIntro } = this.props.data.contentfulBigCaseStudy.caseStudyIntro
const { caseStudyLink } = this.props.data.contentfulBigCaseStudy
console.log(this)
return (
<Layout>
<HeroContainer
bgImgSrc={ this.props.data.contentfulBigCaseStudy.caseStudyHero.fixed.src }>
<HeroTitle>{ caseStudyTitle }</HeroTitle>
<HeroSubtitle>{ caseStudySubtitle }</HeroSubtitle>
</HeroContainer>
<IntroBG>
<IntroContainer>
<IntroTitle>{ caseStudyIntroTitle }</IntroTitle>
<IntroText>{ caseStudyIntro }</IntroText>
</IntroContainer>
<IntroButton href={ caseStudyLink } target="_blank" rel="noopener noreferrer">
Visit the site >
</IntroButton>
</IntroBG>
</Layout>
)
}
}
// Confirm data coming out of contentful call is an object
BigCaseStudy.propTypes = {
data: PropTypes.object.isRequired
}
// Export component
export default BigCaseStudy
// Do call for the page data
// This needs to mirror how you've set up the dynamic createPage function data in gatsby-node.js
export const BigCaseStudyQuery = graphql`
query BigCaseStudyQuery {
contentfulBigCaseStudy {
id
caseStudyTitle
caseStudySubtitle
caseStudyIntroTitle
caseStudyIntro {
caseStudyIntro
}
caseStudyLink
caseStudyHero {
fixed {
width
height
src
srcSet
}
}
},
contentfulBigCaseStudySection (id: $postId) {
title
order
images {
fixed {
width
height
src
srcSet
}
}
bigCaseStudyReference {
id
}
body {
body
}
stats {
stat1 {
word
number
}
stat2 {
word
number
}
stat3 {
word
number
}
stat4 {
word
number
}
}
id
}
}
`
gatsby-node.js
文件:
/**
* Implement Gatsby's Node APIs in this file.
*
* ######################################################
* BIG CASE STUDY BACKEND CODE
* ######################################################
*
* We are using the .createPages part of the Gatsby Node API: https://next.gatsbyjs.org/docs/node-apis/#createPages
* What this does is dynamically create pages (surprise) based on the data you feed into it
*
* Feed the contentful API call into the promise
* Here I'm calling BigCaseStudy, which is a custom content type set up in contentful
* This is briefly explained over here: https://www.gatsbyjs.org/packages/gatsby-source-contentful/
*
* Also, note the caseStudyIntro field is long text `markdown`
* Gatsby returns the long text field as an object
* Calling it's name inside of the object returns the HTML
* Read more here: https://github.com/gatsbyjs/gatsby/issues/3205
*/
// Set Gatsby path up to be used by .createPages
const path = require('path')
// Using Node's module export, Gatsby adds in a createPages factory
exports.createPages = ({ graphql, actions }) => {
// We setup the createPage function takes the data from the actions object
const { createPage } = actions
// Setup a promise to build pages from contentful data model for bigCaseStudies
return new Promise((resolve, reject) => {
// Setup destination component for the data
const bigCaseStudyComponent = path.resolve('src/components/BigCaseStudy/bigcasestudy.js')
resolve(
graphql(`
{
allContentfulBigCaseStudy {
edges {
node {
id
caseStudySlug
caseStudyTitle
caseStudySubtitle
caseStudyIntroTitle
caseStudyIntro {
caseStudyIntro
}
caseStudyLink
caseStudyHero {
fixed {
width
height
src
srcSet
}
}
}
}
}
allContentfulBigCaseStudySection {
edges {
node {
title
order
images {
fixed {
width
height
src
srcSet
}
}
bigCaseStudyReference {
id
}
body {
body
}
stats {
stat1 {
word
number
}
stat2 {
word
number
}
stat3 {
word
number
}
stat4 {
word
number
}
}
id
}
}
}
}
`).then((result) => {
// Now we loop over however many caseStudies Contentful sent back
result.data.allContentfulBigCaseStudy.edges.forEach((caseStudy) => {
const caseStudySections = result.data.allContentfulBigCaseStudySection.edges
let caseStudySectionMatches = caseStudySections.filter(
caseStudySection => caseStudySection.bigCaseStudyReference.id === caseStudy.id
)
createPage ({
path: `/work/${caseStudy.node.caseStudySlug}`,
component: bigCaseStudyComponent,
context: {
id: caseStudy.node.id,
slug: caseStudy.node.caseStudySlug,
title: caseStudy.node.caseStudyTitle,
subtitle: caseStudy.node.caseStudySubtitle,
hero: caseStudy.node.caseStudyHero,
introTitle: caseStudy.node.caseStudyIntroTitle,
intro: caseStudy.node.caseStudyIntro.caseStudyIntro,
link: caseStudy.node.caseStudyLink,
caseStudySection: caseStudySectionMatches.node
}
})
})
})
// This is the error handling for the calls
.catch((errors) => {
console.log(errors)
reject(errors)
})
) // close resolve handler
}) // close promise
}
我也遇到了这个挑战,并没有找到一个很好的解决方案来实现这一目标(虽然我没有使用Contentful),但我确实已经完成了它并认为我可以提供帮助。你需要改变一下你的想法。
基本上,GraphQL实际上不是要查询运行另一个查询所需的数据。它更像是一种“问你需要什么”的工具。 GraphQL希望针对您的需求运行单个查询。
查询所需的参数实际上来自您的gatsby-node.js
文件。具体来说,createPages()
的上下文属性(gatsby提供的Node API)。
这足以让你指向正确的方向吗?如果你需要多一点手,那么我需要知道两件事:
1.关于你想要完成的事情的更多背景。您希望最终用户获得哪些具体数据?
2.你的gatsby-node.js
文件是什么样的。
简短的回答:你不用GraphQL做回调。您只需一个查询即可获得所需的所有内容。
更长的答案:我必须重建gatsby-node.js
文件如何获取Contentful内容然后通过它进行过滤。在Gatsby中,您希望在gatsby-node.js
中设置查询以从数据源中获取所有内容,因为它是静态站点生成器。它的架构将所有数据都包含在内,然后相应地解析出来。
我原来问题的GraphQL查询很好。我更改了.then()
的承诺,在我的结果上使用.filter()
,将子节点的关系字段与父节点的id进行比较。
gatsby-node.js
:
// Set Gatsby path up to be used by .createPages
const path = require('path')
// Using Node's module export, Gatsby adds in a createPages factory
exports.createPages = ({ graphql, actions }) => {
// We setup the createPage function takes the data from the actions object
const { createPage } = actions
// Setup a promise to build pages from contentful data model for bigCaseStudies
return new Promise((resolve, reject) => {
// Setup destination component for the data
const bigCaseStudyComponent = path.resolve('src/components/BigCaseStudy/bigcasestudy.js')
resolve(
graphql(`
{
allContentfulBigCaseStudy {
edges {
node {
id
caseStudySlug
caseStudyTitle
caseStudySubtitle
caseStudyIntroTitle
caseStudyIntro {
caseStudyIntro
}
caseStudyLink
caseStudyHero {
fixed {
width
height
src
srcSet
}
}
}
}
}
allContentfulBigCaseStudySection {
edges {
node {
title
order
images {
fixed {
width
height
src
srcSet
}
}
bigCaseStudyReference {
id
}
body {
body
}
stats {
stat1 {
word
number
}
stat2 {
word
number
}
stat3 {
word
number
}
stat4 {
word
number
}
}
id
}
}
}
}
`).then((result) => {
// Now we loop over however many caseStudies Contentful sent back
result.data.allContentfulBigCaseStudy.edges.forEach((caseStudy) => {
let matchedCaseStudySections = result.data.allContentfulBigCaseStudySection.edges.filter(
caseStudySection =>
caseStudySection.node.bigCaseStudyReference.id === caseStudy.node.id
)
createPage ({
path: `/work/${caseStudy.node.caseStudySlug}`,
component: bigCaseStudyComponent,
context: {
id: caseStudy.node.id,
slug: caseStudy.node.caseStudySlug,
title: caseStudy.node.caseStudyTitle,
subtitle: caseStudy.node.caseStudySubtitle,
hero: caseStudy.node.caseStudyHero,
introTitle: caseStudy.node.caseStudyIntroTitle,
intro: caseStudy.node.caseStudyIntro.caseStudyIntro,
link: caseStudy.node.caseStudyLink,
caseStudySection: matchedCaseStudySections.node
}
})
})
})
// This is the error handling for the calls
.catch((errors) => {
console.log(errors)
reject(errors)
})
) // close resolve handler
}) // close promise
}
一旦你设置好了,Gatsby Node API的createPage
部分就会将父节点及其所有节点发送到你设置的component
param。
在我的组件内部,我现在可以为所有子节点进行GraphQL查询。这现在返回我想要的东西,并符合GraphQL提出一个请求而不是像我试图做的多个请求的想法。唯一棘手的部分是你必须在组件的渲染部分中使用.map()
来遍历从Contentful发回的所有子节点。
bigcasestudy.js
组件:
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { graphql } from 'gatsby'
import Img from 'gatsby-image'
import Layout from '../Layout/layout'
/**
* Hero Section
*/
const HeroContainer = styled.header`
align-items: center;
background-image: url(${ props => props.bgImgSrc });
background-position: center center;
background-size: cover;
display: flex;
flex-direction: column;
justify-content: center;
height: calc(100vh - 128px);
`
const HeroTitle = styled.h1`
color: #fff;
font-size: 70px;
font-weight: 700;
letter-spacing: 2px;
margin-bottom: 15px;
`
const HeroSubtitle = styled.h2`
color: #fff;
font-size: 24px;
font-weight: 300;
letter-spacing: 5px;
text-transform: uppercase;
`
/**
* Intro Section
*/
const IntroBG = styled.section`
background-color: ${ props => props.theme.lightGray };
padding: 50px 0;
`
const IntroContainer = styled.div`
padding: 25px;
margin: 0 auto;
max-width: ${ props => props.theme.sm };
@media (min-width: ${ props => props.theme.sm }) {
padding: 50px 0;
}
`
const IntroTitle = styled.h2`
font-size: 50px;
font-weight: 700;
letter-spacing: 2px;
margin-bottom: 45px;
text-align: center;
`
const IntroText = styled.p`
font-size: 22px;
line-spacing: 4;
text-align: center;
`
const IntroButton = styled.a`
background-color: #fff;
color: ${ props => props.theme.darkGray };
border: 1px solid ${ props => props.theme.mediumGray };
border-radius: 25px;
display: block;
font-size: 16px;
letter-spacing: 5px;
margin: 30px auto;
padding: 15px 45px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
width: 300px;
`
// BigCaseStudy Component
class BigCaseStudy extends React.Component {
render() {
// Destructure Case Study Intro stuff
const {
caseStudyHero,
caseStudyIntro,
caseStudyIntroTitle,
caseStudyLink,
caseStudySubtitle,
caseStudyTitle
} = this.props.data.contentfulBigCaseStudy
// Setup references to Case Study Sections, destructure BigCaseStudySection object
const caseStudySections = this.props.data.allContentfulBigCaseStudySection.edges.map(
(currentSection) => {
return currentSection.node
}
)
// Case Study Section can be in any order, so we need to sort them out
const caseStudySectionsSorted = caseStudySections.sort( (firstItem, secondItem) => {
return firstItem.order > secondItem.order ? 1 : -1
})
console.log(caseStudySectionsSorted)
return (
<Layout>
<HeroContainer
bgImgSrc={ caseStudyHero.fixed.src }>
<HeroTitle>{ caseStudyTitle }</HeroTitle>
<HeroSubtitle>{ caseStudySubtitle }</HeroSubtitle>
</HeroContainer>
<IntroBG>
<IntroContainer>
<IntroTitle>{ caseStudyIntroTitle }</IntroTitle>
<IntroText>{ caseStudyIntro.caseStudyIntro }</IntroText>
</IntroContainer>
<IntroButton href={ caseStudyLink } target="_blank" rel="noopener noreferrer">
Visit the site >
</IntroButton>
</IntroBG>
{
caseStudySectionsSorted.map( (caseStudySection, index) => {
return <IntroTitle key={ index }>{ caseStudySection.title }</IntroTitle>
})
}
</Layout>
)
}
}
// Confirm data coming out of contentful call is an object
BigCaseStudy.propTypes = {
data: PropTypes.object.isRequired
}
// Export component
export default BigCaseStudy
// Do call for the page data
// This needs to mirror how you've set up the dynamic createPage function data in gatsby-node.js
export const BigCaseStudyQuery = graphql`
query BigCaseStudyQuery {
contentfulBigCaseStudy {
id
caseStudyTitle
caseStudySubtitle
caseStudyIntroTitle
caseStudyIntro {
caseStudyIntro
}
caseStudyLink
caseStudyHero {
fixed {
width
height
src
srcSet
}
}
}
allContentfulBigCaseStudySection {
edges {
node {
title
order
images {
fixed {
width
height
src
srcSet
}
}
bigCaseStudyReference {
id
}
body {
body
}
stats {
stat1 {
word
number
}
stat2 {
word
number
}
stat3 {
word
number
}
stat4 {
word
number
}
}
id
}
}
}
}
`
H / t:感谢@ taylor-krusen重新安排我如何解决这个问题。