Porozumienie Gatsby-Wordpress

Gatsby, czyli generator stron statycznych nazywany przez niektorych następcą Wordpressa to narzędzie, które zyskało ostatnio wielką popularność. Ilość repozytoriów na Github, które z niego korzystają to ponad sto tysięcy.

Wraz z Wordpressem może on stworzyć duet, który choć na początku nie wydawał mi się godny uwagi, to ostatecznie okazał się znakomitym rozwiązaniem.

Narzędzia, których będziemy potrzebować

Wordpress

Na potrzeby artykułu zakładam, że jesteś w stanie zainstalować Wordpressa na dowolnym hostingu. Ja posłużę się darmowym hostingiem cba.pl. Jeśli masz jakieś wątpliwości, zajrzyj na stronę poświęconą temu zagadnieniu.

Utwórz na przykład domenę <nazwa_uzytkownika>.cba.pl/mywordpressblog i zainstaluj tam system Wordpress.

Gatsby

Jeśli nigdy wcześniej nie korzystałeś z Gatsbiego, to podążając za dokumentacją wystarczy, że zainstalujesz go globalnie poleceniem

npm install -g gatsby-cli

Od tej pory możesz tworzyć projekty używając polecenia

gatsby new <nazwa_strony>

Na tym etapie polecam jednak skorzystanie z szablonów przygotowanych przez innych programistów, aby otrzymać już pewną, gotową do pracy strukturę projektu.

Listę szablonów znajdziesz TUTAJ

Na potrzeby tego poradnika skorzystamy z najbardziej podstawowego szablonu, jakim jest gatsby-starter-default.

Aby go pobrać i zainicjować projekt, używamy polecenia

gatsby new <nazwa_strony> https://github.com/gatsbyjs/gatsby-starter-default

oraz przechodzimy do utworzonego folderu.

Gitlab

Jest to menedżer repozytoriów, który umożliwi nam przechowywanie aktualnej wersji naszej strony, jej aktualizację oraz hosting za pośrednictwem Gitlab Pages.

Po założeniu konta w serwisie, tworzymy nowy projekt. Wystarczy, że uzupełnimy nazwę projektu (w moim przypadku będzie to mywordpressblog) oraz automatycznie zaproponowany adres https://gitlab.com/wglugla/mywordpressblog).

Teraz wystarczy użyć przycisku clone i skopiować link z sekcji Clone with HTTPS.

W folderze, gdzie znajduje się utworzony przez nas projekt Gatsby, inicjujemy repozytorium

git init
git add .
git commit -m "Initial commit"

oraz dodajemy zdalne repozytorium. W moim przypadku będą to polecenia:

git remote add origin https://gitlab.com/wglugla/mywordpressblog.git
git push -u origin master

Konfiguracja projektu

Po wywołaniu polecenia

gatsby develop

na stronie localhost:8000 powinniśmy ujrzeć aktualną wersję strony.

WAŻNE: Port może ulec zmianie (na przykład, jeśli 8000 jest zajęty). Informację na ten temat można znaleźć po uruchomieniu serwera.

Podstawowe informacje, takie jak autor, tytuł bloga lub jego opis możesz uzupełnić w pliku gatsby-config.js znajdującym się w folderze głównym projektu.

Pobranie danych z Wordpressa

Pierwszym celem na naszej stronie będzie wyświetlenie listy postów, które zostały przez nas utworzone. Posłużymy się w tym celu pluginem gatsby-source-wordpress. Pozwoli on nam na pobranie danych JSON z Wordpressa.

Instalacja przebiega bardzo prosto, wystarczy użyć polecenia

npm install --save gatsby-source-wordpress

a następnie w pliku konfiguracyjnym gatsby-config.js dodać ten plugin zgodnie z podstawową konfiguracją zaproponowaną w dokumentacji.

{
  resolve: 'gatsby-source-wordpress',
  options: {
    // The base url to your WP site.
    // baseUrl: 'wpdemo.gatsbycentral.com',
    baseUrl: 'wglugla.cba.pl',
    // WP.com sites set to true, WP.org set to false
    hostingWPCOM: false,
    // The protocol. This can be http or https.
    protocol: 'http',
    // Use 'Advanced Custom Fields' Wordpress plugin
    useACF: false,
    auth: {},
    // Set to true to debug endpoints on 'gatsby build'
    verboseOutput: false,
  },
},

W miejscu baseUrl należy dodać adres swojej strony.

Po edycji pliku gatsby-config musimy ponownie uruchomić projekt poleceniem

gatsby develop

Aby przetestować, czy wszystko poszło po naszej myśli, przejdźmy pod adres localhost:8000/__graphql

Znajdziemy tam narzędzie pozwalające na testowanie zapytań Graphql, zanim użyjemy ich w naszej aplikacji.

Jeśli nie wiesz czym jest GraphQL lub nie czujesz się na siłach, aby samemu zrozumieć jak on działa, gorąco zachęcam Cię do obejrzenia filmu lub całej serii na Youtube stworzonej przez overmenta. Listę serii o GraphQL znajdziesz TUTAJ.

Dodam, że wystarczy naprawdę podstawowa wiedza na temat działania zapytań GraphQL, abyśmy byli w stanie uzyskać potrzebne informacje.

Dla testów, w polu tekstowym wpiszmy

query IndexQuery {
  allWordpressPost {
    edges {
      node {
        title
        slug
        content
      }
    }
  }
}

Po wciśnięciu kombinacji ctrl + enter naszym oczom powinien ukazać się wynik zapytania zawierający informacje o aktualnie istniejących postach.

Jaką ścieżkę nadać wpisom

Sposobem na prezentację wpisów na blogu będzie pobranie wszystkich artykułów, a następnie wygenerowanie, według szablonu, odpowiedniej podstrony dla każdego z nich.

Aby wszystko poszło sprawnie, należy przyjąć jedną zasadę, na podstawie której tworzone będą nazwy stron. Posłużymy się w tym celu właściwością slug, która stworzona będzie z tytułu artykułu, gdzie każde słowo napisane będzie małymi literami i oddzielone myślnikami. W tym celu musimy skonfigurować tę właściwość w ustawieniach Wordpressa.

Po zalogowaniu do panelu administratora wchodzimy w Ustawienia -> Bezpośrednie odnośniki i wybieramy opcję nazwa wpisu http://wglugla.cba.pl/przykladowy-wpis/, po czym zapisujemy zmiany.

Lista wpisów na blogu

Aby wylistować tytuły wszystkich wpisów, utworzymy komponent, który będzie pełnił funkcję szablonu, dzięki któremu Gatsby wygeneruje stronę główną dodając do niej pobrane dane.

W folderze src naszego projektu, utwórzmy katalog templates. Tam stwórzmy plik blog-list.js. Na tym etapie możesz usunąć plik index.html znajdujący się w folderze pages, gdyż wygenerujemy go sami.

Zacznijmy od podstawowej struktury komponentu klasowego w React.

blog-list.js

import React from "react"
import Layout from "../components/layout"

class BlogList extends React.Component {
  render() {
    return (
      <Layout>
        <h1> Oto wszystkie moje wpisy! </h1>
      </Layout>
    )
  }
}

export default BlogList

Zaimportujmy GraphQL i utwórzmy zapytanie, które wcześniej testowaliśmy.

import { graphql } from 'gatsby'
const allPagesQuery = graphql`
  query allPagesQuery {
    allWordpressPost {
      edges {
        node {
	  id
          title
          slug
          content
        }
      }
    }
  }
`

Od tej pory nasz komponent klasowy ma dostęp do odpowiedzi z zapytania za pośrednictwem props.

Użyjmy więc destrukturyzacji, aby wyciągnąć tablicę edges zawierającą wszystkie posty.

const { edges: posts } = this.props.data.allWordpressPost

oraz zmapujmy te dane, aby otrzymać listę tytułów

return (
  <Layout>
    <h1> Oto wszystkie moje wpisy! </h1>
    <ul>
      {posts.map(post => {
        const { id, title } = post.node
        return (
          <li key={id}>
            <h2>{title}</h2>
          </li>
        )
      })}
    </ul>
  </Layout>
)

Plikiem odpowiedzialnym za generowanie stron jest plik gatsby-node.js.

Zaimportujmy w nim path niezbędne do podawania ścieżki do naszego szablonu.

const path = require("path")

Kolejnym krokiem będzie zdefiniowanie funkcji createPages, utworzenie zapytania graphql o wszystkie posty oraz utworzenie strony głównej przy użyciu funkcji createPage.

exports.createPages = ({ actions, graphql }) => {
  const { createPage } = actions

  return graphql(`
    {
      allWordpressPost {
        edges {
          node {
            id
            slug
            status
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      result.errors.forEach(e => console.error(e.toString()))
      return Promise.reject(result.errors)
    }
    const blogListTemplate = path.resolve(`./src/templates/blog-list.js`)
    createPage({
      path: `/`,
      component: blogListTemplate,
      context: {},
    })
  })
}

Teraz wystarczy ponownie uruchomić serwer i przejść pod adres localhost:8000.

Naszym oczom powinien ukazać się następujący widok.

Aby sprawdzić, czy wszystko działa jak należy, przejdź do panelu administracyjnego Wordpress i dodaj nowy wpis. Następnie uruchom ponownie serwer. Na localhost:8000 powinien ukazać się tytuł nowego wpisu.

Zdalny build projektu

Utworzymy teraz plik, który pozwoli nam na zbudowanie naszego projektu bezpośrednio w repozytorium na Gitlabie. Konfiguracja ta sprawi, że po każdym wykonanym pushu na gałęzi master, wykona się build. Pozwoli nam to na statyczne generowanie stron bez potrzeby budowania projektu lokalnie i przenoszeniu zawartości do repozytorium zdalnego.

W tym celu w głównym katalogu naszego projektu utwórzmy plik .gitlab-ci.yml oraz uzupełnijmy jego zawartość według dokumentacji Gitlaba:

image: node:11.10.1

cache:
  paths:
    - node_modules/

pages:
  script:
    - yarn install
    - ./node_modules/.bin/gatsby build --prefix-paths
  artifacts:
    paths:
      - public
  only:
    - master

Skrypt ten sprawi, że każdy push na gałąź master wykona polecenia: yarn install oraz gatsby build.

Przetestujmy to dodając pliki do repozytorium poleceniem

git add .
git commit -m "Create blog list, add .gitlab-ci.yml"
git push origin master

Po pomyślnie wykonanym pushu wejdź na stronę https://gitlab.com/<twojanazwa>/<nazwarepozytorium>/pipelines

Twoim oczom powinien ukazać się proces, który został wywołany przez Twój push. Jest to moment, w którym wykonują się polecenia zdefiniowane w pliku .gitlab-ci.yml.

Po kilku minutach, gdy proces zakończy się pomyślnie na stronie https://<twojanazwa>.gitlab.io/<nazwarepozytorium> powinna ukazać się strona z listą Twoich wpisów.

UWAGA: przy pierwszym buildzie, czas do pojawienia się strony na hostingu Gitlab Pages może wydłużyć się nawet do 30 minut.

Reakcja na zmiany

Obecnie nasz projekt buduje się w momencie, gdy wykonamy commit i push do gałęzi master. Każdorazowy push po zmianach to niesamowicie niewygodne rozwiązanie. Istnieje jednak możliwość, aby wywołać build w odpowiedzi na zmiany dokonane w panelu administracyjnym Wordpress.

W tym celu posłużymy się pluginem wordpress-gitlab-trigger-pipeline.

Ma tylko JEDNĄ gwiazdkę na Githubie. Zróbmy coś z tym.

Idąc za plikiem README, w naszym folderze Wordpressa przechodzimy do wp_content/plugins i wklejamy tam plik gitlab-trigger-pipeline.php, który możemy pobrać z powyższego repozytorium.

Następnie przechodzimy do panelu administracyjnego Wordpress do zakładki Wtyczki i w sekcji zainstalowane wtyczki aktywujemy dodany przez nas plugin.

W repozytorium Gitlab przechodzimy do zakładki Settings -> CI/CD i rozwijamy sekcję Pipeline triggers.

Tworzymy nowy trigger, nadając mu dowolny opis i naciskając przycisk Add trigger.

Otrzymany token kopiujemy do schowka i wracamy do panelu administracyjnego Wordpress.

W zakładce Ustawienia ujrzymy dodatkowe opcje, które pojawiły się za sprawą aktywacji nowego pluginu. Uzupełniamy je następująco: GITLAB BRANCH: master GITLAB ID: id repozytorium (do znalezienia pod nazwą na stronie głównej repozytorium) GITLAB TRIGGER TOKEN: <token skopiowany do schowka>

Od tego momentu każdy nowy wpis spowoduje, że w sekcji Pipelines w repozytorium na Gitlab uruchomi się skrypt z buildem i po kilku minutach ujrzymy kolejny tytuł wpisu na naszej stronie.

Podstrony wpisów

Zajmiemy się teraz wygenerowaniem stron statycznych dla każdego z naszych wpisów. Zmodyfikujemy więc stronę główną, dodając do tytułu wpisu odnośnik, który powinien prowadzić do strony z treścią.

Po modyfikacji komponent w pliku blog-list.js będzie wyglądał następująco

blog-list.js

class BlogList extends React.Component {
  render() {
    const { edges: posts } = this.props.data.allWordpressPost
    return (
      <Layout>
        <h1> Oto wszystkie moje wpisy! </h1>
        <ul>
          {posts.map(post => {
            const { id, title, content, slug } = post.node
            return (
              <li key={id}>
                <h2>{title}</h2>
                <Link to={`/${slug}`}>Czytaj więcej</Link>
              </li>
            )
          })}
        </ul>
      </Layout>
    )
  }
}

Po uruchomieniu serwera faktycznie zauważymy dodane odnośniki.

Każdy z nich przenosi odpowiednio do postron: /oto-kolejny-wpis, moj-drugi-wpis, witaj-swiecie, jednak z racji, że nie wskazaliśmy jeszcze Gatsbiemu jak ma generować strony, po kliknięciu w odnośniki otrzymamy błąd 404.

Kolejnym krokiem będzie utworzenie nowego szablonu, na podstawie którego wygenerujemy podstrony.

W tym celu w folderze src/templates utwórzmy plik blog-post.js.

Wewnątrz pliku utwórzmy komponent funkcyjny o nazwie BlogPost i uzupełnijmy go przykładową treścią.

blog-post.js

import React from "react"
import { graphql } from "gatsby"

const BlogPost = () => {
  return <h1> Oto podstrona dla wpisu na blogu! </h1>
}

export default BlogPost

Tyle wystarczy, aby zająć się generowaniem podstron.

Jak pamiętasz, w pliku gatsby-node.js napisaliśmy już jedno zapytanie graphql, które zwróci nam wszystkie nasze posty.

Dla każdego otrzymanego posta, musimy wywołać funkcję createPage z odpowiednimi parametrami w następujący sposób:

blog-post.js

const blogPostTemplate = path.resolve("./src/templates/blog-post.js")
const posts = result.data.allWordpressPage.edges
posts.forEach(({ node: page }) => {
  createPage({
    path: `${page.slug}`,
    component: blogPostTemplate,
    context: {
      id: page.id,
    },
  })
})

Po ponownym uruchomieniu serwera, dla każdego wpisu powstanie strona z treścią Oto podstrona dla wpisu na blogu!

Teraz należy uzupełnić szablon postu tak, aby pobierał konkretne informacje. Jak mogłeś zauważyć, w wywołaniu funkcji createPage w obiekcie context przekazujemy id danego wpisu. Posłuży nam ono teraz, aby utworzyć zapytanie o wpis związany z tym id.

W pliku blog-post.js:

blog-post.js

export const pageQuery = graphql`
  query BlogPostByID($id: String!) {
    wordpressPost(id: { eq: $id }) {
      id
      title
      slug
      content
      date(formatString: "MMMM DD, YYYY")
    }
  }
`

Pozostaje nam tylko zmodyfikować komponent

blog-post.js

const BlogPost = props => {
  const { title, date, content } = props.data.wordpressPost
  return (
    <div>
      <h1>{title}</h1>
      <p>{date}</p>
      <div dangerouslySetInnerHTML={{ __html: content }} />
    </div>
  )
}

Dane zwrócone w polu content są opatrzone w znaczniki html, co sprawia, że w celu ich poprawnego sformatowania musimy przekazać je jako parametry dangerouslySetInnerHTML.

Po ponownym uruchomieniu serwera podstrony dla każdego wpisu powinny ukazywać odpowiednią treść.

UWAGA: Z racji, że nasza strona może być hostowana pod linkiem zawierającym prefix (w moim przypadku jest to mywordpressblog), informację o tym prefixie należy umieścić w pliku gatsby-config.js, dopisując do obiektu module.exports właściwość pathPrefix.

module.exports = {
  pathPrefix: `/mywordpressblog`,
}

Tak oto otrzymujemy w pełni sprawny blog, który pozwala nam na zarządzanie treścia z poziomu systemu zarządzania treścią Wordpress.

W celu dodatkowej pomocy, udostępniam dla Ciebie dostęp do repozytorium, które posłużyło mi do napisania tego artykułu oraz podgląd finalnej wersji zbudowanego bloga.

Tagi artykułu: