GridsomeでNetlifyCMSのRelationを設定する方法

NetlifyCMSではcollection同士を関連付けることができます。
このブログを作成するにあたって、記事とタグのコレクションを作成してそれぞれを一対多で紐づけたかったのですがすんなり行きませんでした。

ひとまず実装してみる

NetlifyCMSでタグと記事のCollectionを定義し、記事側でタグへの参照をもたせます。

Gridsomeでも同様に記事とタグを関連付けて、GraphQLで取得してみます。

が、以下の設定ではうまくいきませんでした。

NetlifyCMSの設定

# config.yml

collections:

  # タグの定義
  - name: 'tags'
    label: 'Tags'
    folder: 'content/tags'
    create: true
    slug: '{{name}}'
    identifier_field: 'name'
    fields:
      - { label: 'Name', name: 'name', widget: 'string' }

  # 記事の定義
  - name: 'posts'
    label: 'Posts'
    folder: 'content/posts'
    create: true
    slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
    fields:
      - { label: 'Title', name: 'title', widget: 'string' }
      - { label: 'Body', name: 'body', widget: 'markdown' }
      # タグへの参照を設定
      - name: 'tags'
        label: 'Tags'
        widget: 'relation'
        multiple: true
        collection: 'tags'
        searchFields: ['name']
        displayFields: ['name']
        valueField: 'name'

Gridsomeの設定

Gridsome側でも同様に関連を設定します。

// gridsome.config.js

module.exports = {
  plugins: [
    {
      use: '@gridsome/source-filesystem',
      options: {
        path: 'content/posts/*.md',
        typeName: 'Post',
        route: ':slug',
        // タグへの参照を設定
        refs: {
          tags: 'Tag',
        },
        remark: {
          plugins: ['@gridsome/remark-prismjs'],
        },
      },
    },
    {
      use: '@gridsome/source-filesystem',
      options: {
        path: 'content/tags/*.md',
        typeName: 'Tag',
      },
    },
  ]

GraphQLからの読み込み

記事とそれに紐づくタグを取得するため、以下のクエリを定義します。

query Posts {
  posts: allPost {
    edges {
      node {
        path
        title
        content
        tags {
          path
          name
        }
      }
    }
  }
}

記事は取得できるが紐づくタグが取得できない

上記の設定では、タグのデータは取得できません。

NetlifyCMS側ではPostsとTagsをnameで関連付けていますが、Gridsome側ではその情報を持っていないようです。

NetlifyCMS上の関連をGridsome上でも認識させる

NetlifyCMS上ではfieldを指定して記事とタグを関連付けていましたが、Gridsome側で関連付けを設定できる項目が見当たりません。

relationを使うのは諦めようかな。。と思っていたところに以下の記事を発見。


Gridsome + NetlifyCMS Collection Relations


Gridsomeは手動でデータソースを定義する際にノード間の参照関係をできるようですが、source-filesystemプラグインではidというフィールドを外部キーとして保持することになっているようです。


そこで上記の記事では、

  1. NetlifyCMS上にランダムな文字列を生成するカスタムウィジェットを作成
  2. 関連付けたいCollectionにidというフィールドを追加して生成した文字列をセット
  3. 予めNetlifyCMS側でもその文字列を通して参照を設定する

というアプローチで解決していました。


早速試してみたところ、見事望み通りの動きをさせることができました!

めでたし!

タグ名を一意のidとして定義する

なんですが、idを別フィールドで用意するのはなんとなく二度手間感があります。。。

タグ名が重複することはありませんし、敢えて別フィールドでランダムな文字列を生成せずともタグ名自体をidとして定義すればうまくいきそうです。

# config.yml

collections:

  # タグの定義
  - name: 'tags'
    label: 'Tags'
    folder: 'content/tags'
    create: true
    # ここから
    slug: '{{id}}'
    identifier_field: 'id'
    fields:
      - { label: 'ID', name: 'id', widget: 'string' }
    # ここまで修正

  # 記事の定義
  - name: 'posts'
    label: 'Posts'
    folder: 'content/posts'
    create: true
    slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
    fields:
      - { label: 'Title', name: 'title', widget: 'string' }
      - { label: 'Body', name: 'body', widget: 'markdown' }
      # タグへの参照を設定
      - name: 'tags'
        label: 'Tags'
        widget: 'relation'
        multiple: true
        collection: 'tags'
        # ここから
        searchFields: ['id']
        displayFields: ['id']
        valueField: 'id'
        # ここまで修正

こうすることで、NetlifyCMSにカスタムウィジェットを追加することなく実現が可能そうです。

実際にうごかしてみる

うごきました!

めでたし!

ちなみに...

上述のサイトを参考にNetlifyCMSにカスタムウィジェットを追加する際に少しハマったのですが、gridsome.config.jsのプラグイン設定に設定ファイルのパスを書いておかないと読み込んでくれないみたいです。

module.exports = {
  plugins: [
    {
      use: 'gridsome-plugin-netlify-cms',
      options: {
        // modulePathを定義しないとカスタムウィジェット等の設定が読み込まれない
        modulePath: `src/admin/index.js`,
        // 以下は任意で設定
        htmlPath: `src/admin/index.html`,
        configPath: `src/admin/config.yml`,
        publicPath: '/admin',
        htmlTitle: `Content Editor`,
      },
    },
  ]

詳細は公式サイトのgridsome-plugin-netlify-cms紹介ページに書いてありました。