Gatsby.js + NetlifyCMSのデータの流れまとめ(+投稿画面、URLのカスタマイズ)

Gatsby(とNetlifyCMSを使ったとき)の**データの流れ**を書きたいと思います。
**どのディレクトリ/ファイルが何をしているか**、俯瞰できる記事がなかったので。
また、ある箇所をカスタマイズするためにはどのファイルをいじればいいかも逆引き的に書きました。
この記事では、**CMSでの投稿画面やURLのカスタマイズ**をしています。



Gatsbyの説明や、Netlifyとの連携については他の記事に任せます。
ちなみに使ったスターターはgatsby-starter-netlify-cmsです。(https://github.com/netlify-templates/gatsby-starter-netlify-cms)
このスターター特有の話があったらごめんなさい！


#データの流れ
![Gatsby概略図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/371615/c8d439ec-1842-dd22-7e80-21aba5c4644a.png)



CMSで記事を書く
↓
マークダウンファイルができる
↓
マークダウンファイルからデータを抜き出してGraphQLに入れる
↓
GraphQLからデータを取り出して、テンプレートに埋め込む

って感じです。
流れがわかったので、具体的に、ある箇所をいじるためにはどのファイルを触ればいいか見ていきます。

#逆引き ~これをするにはどこを触ればいいのか~

##CMSの投稿編集ページの管理 → static/admin/config.yml

このファイルでCMSの投稿画面をカスタマイズします。
デフォルトだと、PostとPagesしか編集できません(左側)。
この編集可能なページの種類のことをcollectionと言うらしいです。
![netlifycms.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/371615/5c89793a-e8ff-a4fb-fd28-793fe88f13f9.png)
static/admin/config.ymlのcollectionsに設定を追加することで、他のフォルダのページを作成/編集することができるようになります。
例えば、Post、Pagesの他にSpecialPostという種類のページを編集できるようにしたいときは以下を追加します。
簡単のため、fieldは、埋め込むテンプレートとタイトルだけにしています。
fieldのwidgetの種類などは以下を参照のこと。
[https://www.netlifycms.org/docs/widgets/](https://www.netlifycms.org/docs/widgets/)

```yml:static/admin/config.yml
collections:
  - name: "specialPost" #Gatsby(GraphQL)内でのフィールド名
    label: "SpecialPosts" #CMS上での項目名
    folder: "src/pages/SpecialPosts" #ページを置きたいディレクトリ
    create: true #CMS上で新規作成可能かどうか
    fields: #ページの持つフィールド(項目)名(widgetがhiddenだと編集できない)
      - {label: "Template Key", name: "templateKey", widget: "hidden", default: "index-page"}
      - {label: "Title", name: "title", widget: "string"}
```
<img width="1001" alt="スクリーンショット 2019-10-22 22.33.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/371615/2df09e98-fbaf-6a0f-4df4-3121c137e4dd.png">
無事、新たにSpecialPostがcollectionに追加されました！


##CMSでのプレビュー表示を変える → src/cms/cms.js と src/cms/preview-templates/
上記のカスタマイズでPostとPages以外も編集できるようになっても、プレビューがうまくいかないと思います。
まだプレビューテンプレートの登録がないからです。
ってことでsrc/cms/cms.jsでプレビューテンプレートを登録してあげます。

src/cms/cms.jsにsrc/cms/preview-templates/から使いたいプレビューテンプレートをimportし、
`CMS.registerPreviewTemplate(追加したcollectionのname, インポートしたプレビューテンプレート)`
を追加してあげればOK。


##テンプレートのレイアウトを変えたい → src/components/
この中にSassとかReactコンポーネントが入っているので、これをいじればよいです。

##ページに対して使うテンプレートを変えたい → static/admin/config.yml + src/templates/
各ページのmdファイルにはtemplateKeyが書いてあります。
このtemplateKeyと同名の、src/templates/以下のjsファイルをテンプレートに使っています(その設定はgatsby-node.jsに書いてあります)。
なので、mdファイルのtemplateKeyを、使いたいテンプレートの名前にしてあげましょう。

また、そもそもCMSでテンプレートの指定を変えてあげることもできます。

###CMSでデフォルトテンプレートを変える
mdファイルのtemplateKeyはもともとstatic/admin/config.ymlからきています。
例えばblogですと、collectionsのblogのfieldsに
`- {label: "Template Key", name: "templateKey", widget: "hidden", default: "blog-post"}`
があります。
このデフォルト値が設定されているのです。

widgetが"hidden"なので、CMSにはtemplateKeyの編集欄は表示されず、デフォルト値の"blog-post"が設定されたままです。
そしてmdファイルにtemplateKey: "blog-post"が記されるわけです。
なので、このwidgetを"hidden"から"string"に変えてあげます。
するとCMSにTemplate Keyというラベルで文字列が編集できる欄が現れます(一番上)。
<img width="1248" alt="スクリーンショット 2019-10-22 22.13.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/371615/c88be5d9-6e15-9648-60a5-48f3ba952e33.png">

この欄に、使いたいテンプレートの名前を書いてあげれば良いのです。
これで無事テンプレートを使い分けられるようになりました。

collectionごとに使うテンプレートを変えないのであれば、widgetを"hidden"のままにして、defaultだけ使いたいテンプレート名にしてあげてもいいと思います。

##サイトメタデータ、プラグイン管理 → gatsby-config.js

gatsby-config.jsではサイトのタイトルやディスクリプションの設定やプラグインの追加や削除等の管理ができます。

##URLのカスタマイズ → static/admin/config.yml + gatsby-node.js
URLをカスタマイズします。
まずデフォルトでは、ページのslugがURLになっているのが、gatsby-node.jsを見るとわかります。

```javascript:gatsby-node.js
posts.forEach(edge => {
      const id = edge.node.id
      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,
        },
      })
    }
```
で、このslugがどこから来ているのかと言うと、その下の箇所

```javascript:gatsby-node.js
exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  fmImagesToRelative(node) // convert image paths for gatsby images

  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}
```

createFilePathでファイルのパスを取得してるっぽいですね。
てことで、urlというフィールドをページごとに持たせて、それを前者のcreatePageのpathのvalueに持たせればいいわけです。

今回は、urlフィールドが設定されているページはそれをurlにして、設定されていないページはデフォルトの(slugを使った)URLにしようと思います。

###URLフィールドの作成


**1.CMSへのURLフィールドの追加**
static/admin/config.ymlの、URLを追加したいcollectionのfieldに
`- {label: "URL", name: "url", widget: "string"}`
を追加します。
これでCMS上でURLフィールドを編集できるようになりました。

**2.GraphQLにurlフィールド入れる**
CMSでURLフィールドを設定したら、それを　GraphQLに入れます。
node.frontmatter.urlでurlフィールドの値を取り出し、createNodeFieldでurlフィールドをGraphQLに入れます。
もしURL設定がなく、node.frontmatter.urlがないなら、代わりにデフォルトのslugを入れる設定もしています(valueにはslugが入っている)。

```javascript:gatsby-node.js
exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  fmImagesToRelative(node) // convert image paths for gatsby images

  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    const url = node.frontmatter.url
    createNodeField({
      name: `slug`,
      node,
      value:url || value //urlが設定されていなかったらデフォルトのslug
    })

  }
}
```

これでめでたく、URLをCMS上で自分で設定できるようになりました！

#まとめ
GraphQLもNetlifyCMSも初見で、何がどうなってるのか、どこに何があって何してるのかもさっぱりでしたが、だいぶ見通しが良くなって良かったです！
Gatsbyのこと書いてるブログはだいたいGatsbyなので、調べものも速くて気持ちいいですね！
流行ってほしいなGatsby。