💭

Payloadの下書き・バージョン管理機能の設定とそれを考慮したフロント画面の実装

に公開

はじめに

Payloadには標準で下書き機能とバージョン管理機能があります。

下書き機能を使用することで、コンテンツ作成中は下書きで保存しておいて、コンテンツ作成が完了してから公開状態に変更することができます。また、公開済みのコンテンツを公開状態のまま下書き編集でき、編集が完了してから公開コンテンツに反映することができます。

下書き機能を使用するには、バージョン管理機能を有効化する必要があります。バージョン管理機能を使用することで、バージョン履歴を閲覧でき、各バージョンがどのステータスであったかを確認できます。また、バージョン間の差分を確認したり、過去のバージョンに戻すことも可能です。

この記事では、下書き・バージョン管理機能の設定とそれを考慮したフロント画面の実装について紹介します。

下書き・バージョン管理機能の設定

コレクションやグローバルの設定に下記の記述を行うことで、下書き機能とバージョン管理機能を有効化することができます。

  // ...
  versions: {
    drafts: true,
  },
  // ...

余談ですが、下書き機能を使用するにはバージョン管理機能の有効化が必須である一方、バージョン管理機能を使用するには下書き機能の有効化は必須ではありません。バージョン管理機能のみ有効化し、下書き機能を有効化しない場合は、すべてのバージョンが公開バージョンになるわけです。バージョン管理のみ有効化する場合は下記のようにversions: trueと記述します。

  // ...
  versions: true,
  // ...

下書き・バージョン管理機能を有効化した場合の管理画面

下書き・バージョン管理機能を有効化した場合、下記キャプチャのように画面右上に「Save Draft」と「Publish changes」のボタンが表示され、下書き保存と公開の2種類の操作が可能です。

下書き・バージョン管理機能を有効化した場合の管理画面

また、バージョン一覧画面では各バージョンがどのステータスであったかを確認できます。

バージョン一覧画面

さらに、バージョン一覧画面で各バージョンのリンクをクリックすると、クリックしたバージョンと他バージョンとの差分を確認することもできます。

バージョン間の差分確認の画面

下書き・バージョン管理機能を考慮したフロント画面の実装

下書き・バージョン管理機能を考慮したフロント画面を実装するにあたり、まずは下準備としてコレクションやグローバルの設定に権限制御のコードを追加しておく必要があります。

下記が権限制御のサンプルコードです。このコードは、ユーザーがログインしていれば無条件にコンテンツを閲覧でき、ログインしていなければコンテンツが公開済みの場合のみ閲覧できるように制御しています。閲覧権限について制御をしたいため、readキーに権限制御のコードを記述します。

  // ...
  access: {
    read: ({ req }) => {
      if (req.user) return true

      return {
        _status: {
          equals: 'published',
        },
      }
    },
  },
  // ...
})

次に、フロント画面でコンテンツデータを取得する処理でログインユーザーの情報を渡すようにします。この対応で、ユーザーに閲覧権限があればコンテンツを取得でき、ユーザーに権限がなければコンテンツを取得できないようになります。

下記がお知らせ詳細ページにおいてコンテンツデータを取得・表示するサンプルコードです。ポイントは、payload.find()のオプションにuseroverrideAccess: falseを指定していることです。payload.find()はデフォルトでは権限を考慮せずどんなコンテンツデータでも取得できる仕様となっており、overrideAccess: falseを指定することで権限チェックが行われるようになります。また、userでログインユーザーの情報を渡すことでユーザー情報に基づいたアクセス制御が行われます。

このコードはパフォーマンスを考慮していませんので、その点はご注意ください。

import configPromise from '@payload-config'
import { RichText } from '@payloadcms/richtext-lexical/react'
import { getPayload } from 'payload'
import { notFound } from 'next/navigation'
import { headers as getHeaders } from 'next/headers'
import type { Config } from '@/payload-types'

type Args = {
  params: Promise<{
    id: number
    locale: Config['locale']
  }>
}

export default async function Page({ params: paramsPromise }: Args) {
  const { id, locale } = await paramsPromise

  const payload = await getPayload({ config: configPromise })
  const headers = await getHeaders()
  const { user } = await payload.auth({ headers })

  const result = await payload.find({
    collection: 'news',
    limit: 1,
    where: {
      id: {
        equals: id,
      },
    },
    locale,
    user,
    overrideAccess: false,
  })

  const news = result.docs?.[0]

  if (!news) notFound()

  return (
    <article className="mx-40 py-20">
      <time className="block mb-5">{new Date(news.publishedAt).toLocaleDateString(locale)}</time>
      <h1 className="mb-12 text-4xl">{news.title}</h1>
      {news.body && <RichText data={news.body} />}
    </article>
  )
}

以上の対応で、ログインユーザーのみが下書きコンテンツを閲覧でき、ログインしてしないユーザーは公開済みのコンテンツしか閲覧できない動作を実現できます。

まとめ

この記事では、下書き・バージョン管理機能の設定とそれを考慮したフロント画面の実装について紹介しました。データ取得処理のオプションで権限を考慮するよう指定しないと、ログインしていないユーザーも下書き状態のコンテンツを閲覧できてしまうため、くれぐれもご注意ください。

参考

https://payloadcms.com/docs/versions/overview

Discussion