Pulling multiple items from a collection for a blog post

Hello there!

I have reached a block in creating a blog post template. Basically all I want is to have the Author Name and Thumbnail.

In the Netlify CMS config file, I am able to get the Author Name from the relation widget.

- {label: "Author", name: "author", widget: "relation", collection: "authors", searchFields: ["title"], valueField: "title", displayFields: ["title"], required: true}

Issue with this is that I only get the Author Name to be referenced in the Blog Post.

So I looked around and there was a similar questioned referenced in the community

Get entire collection data from relation widget

And I agree that copying the entire collection would make unnecessary copies that are not needed.

So I tried making a query to see if I can solve this issue

markdownRemark(frontmatter: {title: {eq: "Jacob Byers"}}) {
    frontmatter {
      thumbnail {
          childImageSharp {
            fluid(maxWidth: 120, quality: 100) {
              ...GatsbyImageSharpFluid
            }
          }
        }
    }
  }

And the eq worked to get the Author Thumbnail that I manually entered. So I hoped that I could add a variable in the eq field but learned that Graphql does not allow interpolation.

So I am left scratching my head. I could do another query to loop through all of my authors but dont feel that it is a good idea for the long haul for this site will become cumbersome over time.

Has anyone dealt with this issue before? I am looking at other options but wanted to ask the community.

1 Like

Referencing Get entire collection data from relation widget - #9 by erez for context.

You could iterate during build time over the collection that contains the relation widget (the one with the - {label: "Author", name: "author", widget: "relation", collection: "authors", searchFields: ["title"], valueField: "title", displayFields: ["title"], required: true} field), get the author value, then use it to query the author data.

I think I understand what you mean. So I spent time understanding how tags and tag pages work in the original gatsby netlify template and found this helpful link.

Is there a way to query/gain access to other markdown files/nodes when resolving a Graphql query?

Basically I had to filter for the node for author since I already had a way to create author pages. I added the following to my gatsby-node.js file. This way I could access the thumbnail from the author page.

//Make Author Nodes
exports.sourceNodes = ({ actions, getNodes, getNode }) => {
  const { createNodeField } = actions

  const markdownNodes = getNodes()
    .filter(node => node.internal.type === `MarkdownRemark`)
    .forEach(node => {
      if (node.frontmatter.author) {
        const authorNode = getNodes().find(
          node2 =>
            node2.internal.type === `MarkdownRemark` &&
            node2.frontmatter.title === node.frontmatter.author
        )

        if (authorNode) {
          createNodeField({
            node,
            name: `author`,
            value: authorNode.id,
          })
        }
      }
    })

The second thing I had to do was map it to my gatsby config file by adding this section

mapping: {
    'MarkdownRemark.fields.author': `MarkdownRemark`,
  },

This allowed me to access the author’s thumbnail in a single query in the blog post’s markdownRemark’s field section. Here is my full query with the author’s details.

export const pageQuery = graphql`
  query BlogPostByID($id: String!) {
    markdownRemark(id: { eq: $id }) {
      id
      html
      timeToRead
      fields {
        author {
          frontmatter {
            thumbnail {
              childImageSharp {
                fluid(maxWidth: 120, quality: 100) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        }
      }
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        title
        description
        featuredimage {
          childImageSharp {
            fluid(maxWidth: 120, quality: 100) {
              ...GatsbyImageSharpFluid
            }
          }
        }
        author
        tags
      }
    }
  }
`

And now the author’s picture appeared on my blog post. Is this close to what you meant @erez?

2 Likes

That is much better than I had in mind! (I was thinking two queries, but your solution is much better :slight_smile:)

1 Like

Hello this is a great ppst and has helped out alot thanks! I do have one question, lets say for example instead of a single author you had multiple authors. In my case I have multiple links and I would like to have them both in a single query but the only way ive gotten it to work is like this.

  const markdownNodes = getNodes()
    .filter((node) => node.internal.type === `MarkdownRemark`)
    .forEach((node) => {
      if (node.frontmatter.links) {
        const linksNode = getNodes().filter(
          (node2) =>
            node2.internal.type === `MarkdownRemark` &&
            (node2.fields.slug === `/${node.frontmatter.links[0]}/` ||
              node2.fields.slug === `/${node.frontmatter.links[1]}/`)
        );

        if (linksNode) {
          linksNode.forEach((linkNode, i) => {
            createNodeField({
              node,
              name: `link${i}`,
              value: linkNode.id,
            });
          });
        }
      }
    });

And then in my gastby config I map like this

'MarkdownRemark.fields.link0': `MarkdownRemark`,
'MarkdownRemark.fields.link1': `MarkdownRemark`,

Would there be anyway that I could have

createNodeField({
             node,
             name: `links`,
             value: [`an array of nodes here`],
           });

Hey there!

The example above does enable you to create multiple authors. As for links, are you talking about adding features like linking the author to outside sites? Or are you trying to link multiple pages on your site to one author?

I am sorry I wasnt clear enough. I am basically trying to link to another blog post and have a small little preview for each linked blog post, so each link in node.frontmatter.links is the {{slug}} of another md file. So what I am trying to figure out is how I can loop through those slugs and create a node sorta like this

createNodeField({
            node,
            name: `links`,
            value: ['array of blogpost nodes'],
 })

So that I can have a query like this

markdownRemark(id: { eq: $id }) {
      id
      html
      fields {
        links {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }

That would hopefully return me an array with all the fields.slug and frontmatter.title of the other blog post that are linked by the relation widget. Thank you for your help!

1 Like

Hi @angelo-martinez, is this what you’re looking for?

1 Like

Hey @angelo-martinez

I have been reading this over and need a clarification on something. Are you trying

A) Link blog posts to a category or tag? So when you go to a category or tag page you see a list of posts that are linked to that criteria?

B) Are you trying to link other blog posts to a present post? Like for example when I am reading a tech article I see links that let me dig further into other articles. But instead of a link you want to have a preview for that post including the title and maybe an image?

Option A should have a working example for tags in the Netlify Gatsby Template. Option B is similar to the example above or the article the erez provided. Basically building the nodes and mapping them to others.

Something thing to consider is that the file name may be easier to query than slug. I found that to be true when linking other pages in the cms. However, I only went as far using it to create a link and not to pull an image or preview but that could be done with my example above.

Another thing to consider is to use multiple queries on your page. This can be done by adding variables in your query.

export const homePageQuery = graphql`
  query HomePageTemplate($id: String) {
    homepage: markdownRemark(id: { eq: $id } frontmatter: { templateKey: { eq: "home-page" } }) {
      html
      frontmatter {
        heading
        body
      }
    }
    blogPosts: allMarkdownRemark(
      sort: { order: DESC, fields: [frontmatter___date] }
      filter: { frontmatter: { templateKey: { eq: "blog-post" } } }
      limit: 3
    ) {
      edges {
        node {
          id
          fields {
            slug
          }
          timeToRead
          frontmatter {
            title
          }
        }
      }
    }
  }
`

That way when you query you can use data.homepage.frontmatter to query items specific to the page and then data.blogPosts.frontmatterto query from all blog posts.

And if you want to throw variables into your queries I would look into adding context to your gatsby-node.js file. For example, for my posts, I added title in mine so I can find it in my queries.

posts.forEach(edge => {
      const id = edge.node.id
      const title = edge.node.frontmatter.title
      createPage({
        path: edge.node.fields.slug,
        tags: edge.node.frontmatter.tags,
        component: path.resolve(
          `src/templates/${String(edge.node.frontmatter.templateKey)}.js`
        ),
        // additional data can be passed via context
        context: {
          id,
          title,
        },
      })
    })

So when I want to pull all posts for that category I can do so with pageContext

const CategoryPage = ({ pageContext, data }) => {
  const { title } = pageContext
  const { allMarkdownRemark: category} = data
  return (
    <Layout>
      <Container size={'regular'}>
        <h1>{title}</h1>
        <BlogRoll
          data={category}
        />
      </Container>
    </Layout>
  )
}

Then I use pageContext as a variable in my query.

export const query = graphql`
  query CategoryPage($title: String) {
    allMarkdownRemark(
      limit: 2000
      sort: { fields: [frontmatter___date], order: DESC }
      filter: {frontmatter: {templateKey: {eq: "blog-post"}, category: { in: [$title] }}}
    ) {
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
            templateKey
            author
          }
        }
      }
    }
  }
`

This query pulls all posts that are connected to the variable [$title] which is the name of the category.

2 Likes

@erez @byebyers Thank you guys so much for the help! Yes basically I am going for something like option B, I will rewrite my code with your suggestions, I think that looks like a clean solution. Once again thank you both for the guidance, much appreciated.