So erstellen Sie Taxonomie-Seiten mit Gatsby und Sanity.io

Avatar of Knut Melvær
Knut Melvær am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

In diesem Tutorial erfahren Sie, wie Sie mit Gatsby Taxonomie-Seiten mit strukturierten Inhalten aus Sanity.io erstellen. Sie lernen, wie Sie die Node-Erstellungs-APIs von Gatsby verwenden, um Felder zu Ihren Inhaltstypen in Gatsbys GraphQL-API hinzuzufügen. Speziell erstellen wir Kategorie-Seiten für den Sanity-Blog-Starter.

Das heißt, nichts von dem, was wir hier behandeln, ist Sanity-spezifisch. Sie können dies unabhängig von Ihrer Inhaltsquelle tun. Wir verwenden Sanity.io nur zur Demonstration.

Blog einrichten und loslegen

Wenn Sie dieses Tutorial mit Ihrem eigenen Gatsby-Projekt durcharbeiten möchten, überspringen Sie den Abschnitt zur Erstellung einer neuen Seiten-Vorlage in Gatsby. Wenn nicht, gehen Sie zu sanity.io/create und starten Sie den Gatsby-Blog-Starter. Dies platziert den Code für Sanity Studio und das Gatsby-Frontend in Ihrem GitHub-Konto und richtet die Bereitstellung für beides auf Netlify ein. Die gesamte Konfiguration, einschließlich Beispielinhalten, ist vorhanden, sodass Sie direkt mit dem Erlernen der Erstellung von Taxonomie-Seiten beginnen können.

Sobald das Projekt initialisiert ist, klonen Sie das neue Repository von GitHub lokal und installieren Sie die Abhängigkeiten.

git clone [email protected]:username/your-repository-name.git
cd your-repository-name
npm i

Wenn Sie sowohl Sanity Studio (das CMS) als auch das Gatsby-Frontend lokal ausführen möchten, können Sie dies tun, indem Sie den Befehl npm run dev in einem Terminal im Projektverzeichnis ausführen. Sie können auch in den Webordner wechseln und Gatsby mit demselben Befehl ausführen.

Sie sollten auch die Sanity CLI installieren und sich im Terminal bei Ihrem Konto anmelden: npm i -g @sanity/cli && sanity login. Dies gibt Ihnen Werkzeuge und nützliche Befehle zur Interaktion mit Sanity-Projekten. Sie können das Flag --help hinzufügen, um weitere Informationen zu Funktionen und Befehlen zu erhalten.

Wir werden einige Anpassungen an der Datei gatsby-node.js vornehmen. Um die Ergebnisse der Änderungen zu sehen, starten Sie den Gatsby-Entwicklungsserver neu. Dies geschieht auf den meisten Systemen, indem Sie CTRL + C im Terminal drücken und npm run dev erneut ausführen.

Einführung in das Content-Modell

Schauen Sie in den Ordner /studio/schemas/documents. Dort finden Sie Schema-Dateien für unsere Hauptinhaltstypen: Autor, Kategorie, Website-Einstellungen und Beiträge. Jede der Dateien exportiert ein JavaScript-Objekt, das die Felder und Eigenschaften dieser Inhaltstypen definiert. In post.js befindet sich die Felddefinition für Kategorien.

{
  name: 'categories',
  type: 'array',
  title: 'Categories',
  of: [
    {
      type: 'reference',
      to: [{
        type: 'category'
      }]
    }
  ]
},

Dies erstellt ein Array-Feld mit Referenzobjekten zu Kategorie-Dokumenten. Im Blog-Studio sieht das dann so aus.

An array field with references to category documents in the blog studio
Ein Array-Feld mit Referenzen zu Kategorie-Dokumenten im Blog-Studio

Slugs zum Kategorie-Typ hinzufügen

Gehen Sie zu /studio/schemas/documents/category.js. Dort gibt es ein einfaches Inhaltsmodell für eine Kategorie, das aus einem Titel und einer Beschreibung besteht. Da wir nun dedizierte Seiten für Kategorien erstellen, wäre ein Slug-Feld ebenfalls praktisch. Wir können es im Schema wie folgt definieren.

// studio/schemas/documents/category.js
export default {
  name: 'category',
  type: 'document',
  title: 'Category',
  fields: [
    {
      name: 'title',
      type: 'string',
      title: 'Title'
    },
    {
      name: 'slug',
      type: 'slug',
      title: 'Slug',
      options: {
        // add a button to generate slug from the title field
        source: 'title'
      }
    },
    {
      name: 'description',
      type: 'text',
      title: 'Description'
    }
  ]
}

Nachdem wir das Content-Modell geändert haben, müssen wir auch die GraphQL-Schema-Definition aktualisieren. Tun Sie dies, indem Sie npm run graphql-deploy (alternativ: sanity graphql deploy) im Studio-Verzeichnis ausführen. Sie erhalten Warnungen über Breaking Changes, aber da wir nur ein Feld hinzufügen, können Sie ohne Bedenken fortfahren. Wenn das Feld in Ihrem Studio auf Netlify zugänglich sein soll, committen Sie die Änderungen in Git (mit git add . && git commit -m"add slug field") und pushen Sie sie in Ihr GitHub-Repository (git push origin master).

Nun müssen wir die Kategorien durchgehen und Slugs für sie generieren. Denken Sie daran, auf den Veröffentlichen-Button zu klicken, um die Änderungen für Gatsby zugänglich zu machen! Und wenn Sie den Entwicklungsserver von Gatsby ausgeführt haben, müssen Sie diesen ebenfalls neu starten.

Kurzer Hinweis, wie das Sanity-Source-Plugin funktioniert

Beim Starten von Gatsby im Entwicklungsmodus oder beim Erstellen einer Website ruft das Source-Plugin zuerst die GraphQL-Schema-Definitionen von der bereitgestellten GraphQL-API von Sanity ab. Das Source-Plugin verwendet dies, um Gatsby mitzuteilen, welche Felder verfügbar sein sollen, und um zu verhindern, dass es abstürzt, falls Inhalte für bestimmte Felder verschwinden. Anschließend greift es auf den Export-Endpunkt des Projekts zu, der alle zugänglichen Dokumente an den In-Memory-Datenspeicher von Gatsby streamt.

Mit anderen Worten, die gesamte Website wird mit zwei Anfragen aufgebaut. Wenn der Entwicklungsserver läuft, wird auch ein Listener eingerichtet, der alle Änderungen von Sanity in Echtzeit an Gatsby weiterleitet, ohne zusätzliche API-Abfragen durchzuführen. Wenn wir dem Source-Plugin ein Token mit Berechtigung zum Lesen von Entwürfen geben, sehen wir die Änderungen sofort. Dies kann auch mit Gatsby Preview erlebt werden.

Eine Kategorie-Seiten-Vorlage in Gatsby hinzufügen

Nachdem wir nun die GraphQL-Schema-Definition und einige Inhalte bereit haben, können wir mit der Erstellung von Kategorie-Seiten-Vorlagen in Gatsby fortfahren. Wir müssen zwei Dinge tun:

  • Gatsby mitteilen, dass es Seiten für die Kategorie-Knoten (das ist Gatsbys Begriff für "Dokumente") erstellen soll.
  • Gatsby eine Vorlagendatei geben, um das HTML mit den Seitendaten zu generieren.

Beginnen Sie damit, die Datei /web/gatsby-node.js zu öffnen. Dort befindet sich bereits Code, der zur Erstellung der Blogbeitragsseiten verwendet werden kann. Wir werden diesen Code weitgehend übernehmen, aber für Kategorien. Gehen wir Schritt für Schritt vor.

Zwischen der Funktion createBlogPostPages und der Zeile, die mit exports.createPages beginnt, können wir den folgenden Code hinzufügen. Ich habe Kommentare eingefügt, um zu erklären, was passiert.

// web/gatsby-node.js

// ...

async function createCategoryPages (graphql, actions) {
  // Get Gatsby‘s method for creating new pages
  const {createPage} = actions
  // Query Gatsby‘s GraphAPI for all the categories that come from Sanity
  // You can query this API on https://:8000/___graphql
  const result = await graphql(`{
    allSanityCategory {
      nodes {
        slug {
          current
        }
        id
      }
    }
  }
  `)
  // If there are any errors in the query, cancel the build and tell us
  if (result.errors) throw result.errors

  // Let‘s gracefully handle if allSanityCatgogy is null
  const categoryNodes = (result.data.allSanityCategory || {}).nodes || []

  categoryNodes
    // Loop through the category nodes, but don't return anything
    .forEach((node) => {
      // Desctructure the id and slug fields for each category
      const {id, slug = {}} = node
      // If there isn't a slug, we want to do nothing
      if (!slug) return

      // Make the URL with the current slug
      const path = `/categories/${slug.current}`

      // Create the page using the URL path and the template file, and pass down the id
      // that we can use to query for the right category in the template file
      createPage({
        path,
        component: require.resolve('./src/templates/category.js'),
        context: {id}
      })
    })
}

Zuletzt wird diese Funktion am Ende der Datei benötigt.

// /web/gatsby-node.js

// ...

exports.createPages = async ({graphql, actions}) => {
  await createBlogPostPages(graphql, actions)
  await createCategoryPages(graphql, actions) // <= add the function here
}

Nun, da wir die Maschinerie zur Erstellung des Kategorie-Seitenknotens haben, müssen wir eine Vorlage hinzufügen, wie diese tatsächlich im Browser aussehen soll. Wir werden sie auf der vorhandenen Blogbeitrags-Vorlage basieren, um ein konsistentes Styling zu erzielen, halten sie aber dabei recht einfach.

// /web/src/templates/category.js
import React from 'react'
import {graphql} from 'gatsby'
import Container from '../components/container'
import GraphQLErrorList from '../components/graphql-error-list'
import SEO from '../components/seo'
import Layout from '../containers/layout'

export const query = graphql`
  query CategoryTemplateQuery($id: String!) {
    category: sanityCategory(id: {eq: $id}) {
      title
      description
    }
  }
`
const CategoryPostTemplate = props => {
  const {data = {}, errors} = props
  const {title, description} = data.category || {}

  return (
    <Layout>
      <Container>
        {errors && <GraphQLErrorList errors={errors} />}
        {!data.category && <p>No category data</p>}
        <SEO title={title} description={description} />
        <article>
          <h1>Category: {title}</h1>
          <p>{description}</p>
        </article>
      </Container>
    </Layout>
  )
}

export default CategoryPostTemplate

Wir verwenden die ID, die im Kontext in gatsby-node.js übergeben wurde, um den Kategorie-Inhalt abzufragen. Dann verwenden wir sie, um die Felder title und description abzufragen, die auf dem Kategorie-Typ vorhanden sind. Stellen Sie sicher, dass Sie nach dem Speichern dieser Änderungen mit npm run dev neu starten und rufen Sie im Browser localhost:8000/categories/structured-content auf. Die Seite sollte ungefähr so aussehen.

A barebones category page with a site title, Archive link, page title, dummy content and a copyright in the footer.
Eine spartanische Kategorie-Seite

Coole Sache! Aber es wäre noch cooler, wenn wir tatsächlich sehen könnten, welche Beiträge zu dieser Kategorie gehören, denn das ist ja irgendwie der Sinn von Kategorien, oder? Idealerweise sollten wir einen "Seiten"-Feld auf dem Kategorieobjekt abfragen können.

Bevor wir lernen, wie das geht, müssen wir einen Schritt zurückgehen, um zu verstehen, wie Sanitys Referenzen funktionieren.

Sanitys Referenzen abfragen

Auch wenn wir die Referenzen nur in einem Typ definieren, indiziert Sanitys Datenspeicher sie "bidirektional". Das bedeutet, dass das Erstellen einer Referenz auf das Kategorie-Dokument "Strukturierter Inhalt" von einem Beitrag aus Sanity wissen lässt, dass die Kategorie diese eingehenden Referenzen hat, und verhindert, dass Sie sie löschen, solange die Referenz existiert (Referenzen können als "schwach" gesetzt werden, um dieses Verhalten zu überschreiben). Wenn wir GROQ verwenden, können wir Kategorien abfragen und Beiträge, die sie enthalten, wie folgt verknüpfen (die Abfrage und das Ergebnis finden Sie im Aktion auf groq.dev).

*[_type == "category"]{
  _id,
  _type,
  title,
  "posts": *[_type == "post" && references(^._id)]{
    title,
    slug
  }
}
// alternative: *[_type == "post" && ^._id in categories[]._ref]{

Dies gibt eine Datenstruktur aus, die uns eine einfache Vorlage für Kategoriebeiträge ermöglicht.

[
  {
    "_id": "39d2ca7f-4862-4ab2-b902-0bf10f1d4c34",
    "_type": "category",
    "title": "Structured content",
    "posts": [
      {
        "title": "Exploration powered by structured content",
        "slug": {
          "_type": "slug",
          "current": "exploration-powered-by-structured-content"
        }
      },
      {
        "title": "My brand new blog powered by Sanity.io",
        "slug": {
          "_type": "slug",
          "current": "my-brand-new-blog-powered-by-sanity-io"
        }
      }
    ]
  },
  // ... more entries
]

Das ist gut für GROQ, aber was ist mit GraphQL?

Hier ist der Haken: Diese Art von Abfrage ist mit Gatsbys GraphQL-API out-of-the-box bisher nicht möglich. Aber keine Sorge! Gatsby verfügt über eine leistungsstarke API zur Änderung seines GraphQL-Schemas, mit der wir Felder hinzufügen können.

Mit createResolvers die GraphQL-API von Gatsby bearbeiten

Gatsby hält beim Erstellen Ihrer Website alle Inhalte im Speicher und stellt einige APIs bereit, mit denen wir abgreifen können, wie diese Informationen verarbeitet werden. Unter diesen sind die Node-APIs. Es ist wahrscheinlich gut zu verdeutlichen, dass, wenn wir von "Knoten" in Gatsby sprechen – nicht zu verwechseln mit Node.js. Die Entwickler von Gatsby haben "Kanten und Knoten" aus der Graphentheorie entlehnt, wobei "Kanten" die Verbindungen zwischen den "Knoten" sind, die die "Punkte" sind, an denen die eigentlichen Inhalte stehen. Da eine Kante eine Verbindung zwischen Knoten ist, kann sie eine "nächste" und "vorherige" Eigenschaft haben.

The edges with next and previous, and the node with fields in GraphQL’s API explorer
Die Kanten mit "nächste" und "vorherige" und der Knoten mit Feldern im GraphQL-API-Explorer.

Die Node-APIs werden in erster Linie von Plugins verwendet, aber sie können auch verwendet werden, um anzupassen, wie unsere GraphQL-API funktioniert. Eine dieser APIs heißt createResolvers. Sie ist relativ neu und ermöglicht es uns, darauf zuzugreifen, wie die Knoten eines Typs erstellt werden, damit wir Abfragen erstellen können, die ihnen Daten hinzufügen.

Nutzen wir sie, um die folgende Logik hinzuzufügen.

  • Prüfen Sie auf Übereinstimmungen mit dem Typ SanityCategory bei der Erstellung der Knoten.
  • Wenn ein Knoten diesem Typ entspricht, erstellen Sie ein neues Feld namens posts und setzen Sie dessen Typ auf SanityPost.
  • Führen Sie dann eine Abfrage aus, die alle Beiträge filtert, die eine Kategorie auflisten, die mit der ID der aktuellen Kategorie übereinstimmt.
  • Wenn es übereinstimmende IDs gibt, fügen Sie den Inhalt der Beitrags-Knoten zu diesem Feld hinzu.

Fügen Sie den folgenden Code zur Datei /web/gatsby-node.js hinzu, entweder unter oder über dem bereits vorhandenen Code.

// /web/gatsby-node.js
// Notice the capitalized type names
exports.createResolvers = ({createResolvers}) => {
  const resolvers = {
    SanityCategory: {
      posts: {
        type: ['SanityPost'],
        resolve (source, args, context, info) {
          return context.nodeModel.runQuery({
            type: 'SanityPost',
            query: {
              filter: {
                categories: {
                  elemMatch: {
                    _id: {
                      eq: source._id
                    }
                  }
                }
              }
            }
          })
        }
      }
    }
  }
  createResolvers(resolvers)
}

Lassen Sie uns nun den Entwicklungsserver von Gatsby neu starten. Wir sollten ein neues Feld für Beiträge innerhalb der Typen sanityCategory und allSanityCategory finden können.

A GraphQL query for categories with the category title and the titles of the belonging posts

Liste der Beiträge zur Kategorie-Vorlage hinzufügen

Nachdem wir nun die benötigten Daten haben, können wir zu unserer Kategorie-Seiten-Vorlage (/web/src/templates/category.js) zurückkehren und eine Liste mit Links zu den Beiträgen hinzufügen, die zur Kategorie gehören.

// /web/src/templates/category.js
import React from 'react'
import {graphql, Link} from 'gatsby'
import Container from '../components/container'
import GraphQLErrorList from '../components/graphql-error-list'
import SEO from '../components/seo'
import Layout from '../containers/layout'
// Import a function to build the blog URL
import {getBlogUrl} from '../lib/helpers'

// Add “posts” to the GraphQL query
export const query = graphql`
  query CategoryTemplateQuery($id: String!) {
    category: sanityCategory(id: {eq: $id}) {
      title
      description
      posts {
        _id
        title
        publishedAt
        slug {
          current
        }
      }
    }
  }
`
const CategoryPostTemplate = props => {
  const {data = {}, errors} = props
  // Destructure the new posts property from props
  const {title, description, posts} = data.category || {}

  return (
    <Layout>
      <Container>
        {errors && <GraphQLErrorList errors={errors} />}
        {!data.category && <p>No category data</p>}
        <SEO title={title} description={description} />
        <article>
          <h1>Category: {title}</h1>
          <p>{description}</p>
          {/*
            If there are any posts, add the heading,
            with the list of links to the posts
          */}
          {posts && (
            <React.Fragment>
              <h2>Posts</h2>
              <ul>
                { posts.map(post => (
                  <li key={post._id}>
                    <Link to={getBlogUrl(post.publishedAt, post.slug)}>{post.title}</Link>
                  </li>))
                }
              </ul>
            </React.Fragment>)
          }
        </article>
      </Container>
    </Layout>
  )
}

export default CategoryPostTemplate

Dieser Code erzeugt diese einfache Kategorie-Seite mit einer Liste verlinkter Beiträge – genau wie wir es wollten!

The category page with the category title and description, as well as a list of its posts

Geh und erstelle Taxonomie-Seiten!

Wir haben gerade den Prozess der Erstellung neuer Seitentypen mit benutzerdefinierten Seiten-Vorlagen in Gatsby abgeschlossen. Wir haben eine der Node-APIs von Gatsby namens createResolver behandelt und sie verwendet, um dem Kategorie-Knoten ein neues posts-Feld hinzuzufügen.

Dies sollte Ihnen das geben, was Sie brauchen, um andere Arten von Taxonomie-Seiten zu erstellen! Haben Sie mehrere Autoren in Ihrem Blog? Nun, Sie können die gleiche Logik verwenden, um Autorenseiten zu erstellen. Das Interessante am GraphQL-Filter ist, dass Sie ihn verwenden können, um über die explizite Beziehung, die mit Referenzen hergestellt wird, hinauszugehen. Er kann auch verwendet werden, um andere Felder mithilfe von regulären Ausdrücken oder Zeichenkettenvergleichen abzugleichen. Es ist ziemlich flexibel!