「PowerCMS X Advent Calendar 2021」の2日目です。当サイトはPowerCMS XとNext.jsを利用し、JamstackでWebサイト構築を実現するための研究・開発を目的とした個人サイトです。
当サイトでも開設当初から「問い合わせフォーム」を用意しています。「Decoupling」の原則もふまえどのサービスを使おうかと考えたのですが、APIを利用してPowerCMS Xのフォーム機能を利用するのが現状最も便利なのでは?と考えました。開設時は単純にフォームの入力内容を送信するだけでしたが、Reactの知識も少し深まり現在はエラーメッセージの表示もできるようになりました。
本稿ではNext.jsやAPIを利用し、PowerCMS Xのフォーム機能を利用する手順などを解説します。
PowerCMS X管理画面での事前準備
まずPowerCMS Xの管理画面にて、入力してほしい内容を1つ1つ設定する「設問」、そして設問をグループ化した「フォーム」を準備します。ドキュメントが「フォームの作成 | PowerCMS X」にありますので、それに沿って設定を行ってください。ただし、設問のビューは何も設定する必要はありません。(後ほどReactで記述します。)ベースネームがinput
要素のname
属性値になることに留意してください。
フォーム送信時に管理者とフォーム送信者にメールを送りたいので、PowerCMS Xのビューを書きました。以下が管理者宛メールのサンプルです。PAML3を有効にしTwig風な記述をしています。
PowerCMS X R&D Websiteにて問い合わせを受け付けました。
受付番号:
{{contact_id}}
{% for param in post_params %}
{{param.post_question}} :
{{param.post_value}}
{% endfor %}
--
PowerCMS X R&D Website
https://rd.powercmsx.anothersky.jp/
pages/api/contact.jsの作成
次にNext.jsのAPIルート機能でフロントエンドとPowerCMS XのAPIとの間をつなぐ機能を実装します。フロントエンドから/api/contact
にフォームの内容を送信するとそれをPowerCMS XのAPIに送信し、結果をフロントエンドに返すような動作をします。これによりPowerCMS XのURLを隠すことができますし、設定にもよりますがCORSのエラーも発生しません。
client
は以前「PTRESTfulAPIClientの使用方法」で紹介したもので、submitContact
メソッドにてフォームのPOSTが実行できます。フォームの入力内容はreq.body
にあるのでそれを利用します。入力内容にエラーがあった場合はPowerCMS XのAPIからのレスポンスerrors
にベースネームとエラーメッセージが入るので、後ほど表示に利用します。
import { client } from '../../libs/client';
import { formId, workspaceId, defaultErrors } from '../../constants/form';
export default async function handler(req, res) {
if(req.method !== 'POST') {
res.status(405).json({
errors: 'Method not allowed.',
});
return;
}
const data = {
name: req.body.name,
email: req.body.email,
subject: req.body.subject,
content: req.body.content,
MagicToken: req.body.token,
have_used_pcmsx: req.body.have_used_pcmsx,
};
const submitResponse = await client.submitContact(formId, workspaceId, data);
const submitObject = await submitResponse.json();
if (submitObject.Success) {
res.status(200).json({
result: 'success',
});
return;
}
res.status(400).json({
result: 'error',
messages: submitObject.message ? submitObject.message : submitObject.messages,
errors: submitObject.errors ? submitObject.errors : defaultErrors,
});
};
フォームのビューなどを実装する
次にpages/form.js
を実装していきます。まずはビューにあたるJSXやフォームの送信時に必要なトークンを取得するコードなどを実装します。まだまだ勉強中ですが「MUI: The React component library you always wanted」を利用し必要な入力項目分フィールドを書いていきました。MUIはerror
プロパティにエラーの有無を、helperText
にエラーメッセージを渡すとよしなに表示してくれるようです。
後ほどステートフックでmessages
にエラーメッセージを格納するので、messages
に内容がある場合はそれを列挙するようにしておきます。送信処理中を示すloading
もステートフックで状態をセットします。
import * as React from 'react';
import Head from 'next/head';
import {
Typography,
List,
ListItem,
ListItemText,
Box,
TextField,
Button,
CircularProgress,
FormControl,
FormControlLabel,
FormLabel,
RadioGroup,
Radio
} from '@mui/material';
import SendIcon from '@mui/icons-material/Send';
import { usePostContact } from '../hooks/usePostContact';
import { client } from '../libs/client';
import { formId, workspaceId } from '../constants/form';
export default function Form({ token }) {
const { postContact, loading, messages, errors } = usePostContact(); // 後ほど実装します
return (
<>
<Typography component="h1">
お問い合わせ
</Typography>
<Typography variant="body1">
当サイトに関するご質問・お問い合わせは下記フォームをご利用ください。
</Typography>
{messages.length && (
<div id="error" role="alert" tabIndex={-1}>
<Typography component="h2">
エラーが発生しました
</Typography>
<List>
{messages.map((message, index) => {
return(
<ListItem key={index}>
<ListItemText primary={message} />
</ListItem>
)
})}
</List>
</div>
)}
<Box
component="form"
onSubmit={postContact}
>
<TextField
required
fullWidth
id="name"
label="お名前"
error={errors.name ? true : false}
helperText={errors.name}
/>
<TextField
required
fullWidth
type="email"
id="email"
label="E-mail"
error={errors.email ? true : false}
helperText={errors.email}
/>
<TextField
required
fullWidth
id="subject"
label="件名"
error={errors.subject ? true : false}
helperText={errors.subject}
/>
<TextField
required
fullWidth
multiline
rows={4}
type="textarea"
id="content"
label="お問い合わせ内容"
error={errors.content ? true : false}
helperText={errors.content}
/>
<div>
<FormControl component="fieldset">
<FormLabel component="legend">PowerCMS Xの利用経験</FormLabel>
<RadioGroup
row
aria-label="PowerCMS Xの利用経験"
defaultValue="あり"
name="have_used_pcmsx"
>
<FormControlLabel value="あり" control={<Radio />} label="あり" />
<FormControlLabel value="なし" control={<Radio />} label="なし" />
</RadioGroup>
</FormControl>
</div>
<TextField type="hidden" id="token" value={token} sx={{ display: 'none' }} />
{loading && (
<Box>
<CircularProgress />
</Box>
)}
<Button type="submit" variant="contained" endIcon={<SendIcon />}>
送信する
</Button>
</Box>
</>
)
}
export async function getServerSideProps() {
const response = await client.getContactToken(formId, workspaceId);
const tokenObject = await response.json();
const token = tokenObject.magic_token;
return {
props: {
token,
},
}
}
フォームのロジックをカスタムフックで実装する
続いてhooks/usePostContact.js
にフォームをsubmit
した時に呼び出されるpostContact
メソッドやステートの処理を実装します。
postContact
メソッドは先に実装したpages/api/contact.js
に対してフォームの入力内容を送信する処理、またレスポンスに応じてサンクス画面に遷移させたり、ステートフックでmessages
やerrors
にエラーの情報を格納したりする処理を記述します。loading
に真偽値を設定し送信処理中にCircularProgress
(くるくる回って処理中であることを示すSVG)を表示する工夫もしました。
import Router from 'next/router';
import { useState } from 'react';
import { defaultErrors } from '../constants/form';
export function usePostContact() {
const [loading, setLoading] = useState(false);
const [messages, setMessages] = useState([]);
const [errors, setErrors] = useState(defaultErrors);
const postContact = async event => {
event.preventDefault();
setLoading(true);
const data = {
name: event.target.name.value,
email: event.target.email.value,
subject: event.target.subject.value,
content: event.target.content.value,
token: event.target.token.value,
have_used_pcmsx: event.target.have_used_pcmsx.value,
};
const response = await fetch('/api/contact', {
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
},
method: 'POST'
});
if (response.status === 200) {
Router.push('/form_send');
return;
}
const result = await response.json()
setLoading(false);
setMessages(Array.isArray(result.messages) ? result.messages : [result.messages]);
setErrors(result.errors);
document.getElementById('error').focus();
};
return { postContact, loading, messages, errors };
}
フォームの動作確認
フォームに入力をして送信が完了するとPowerCMS Xの管理画面で投稿が確認できます。
またPowerCMS Xからメールが送信されます。
フォームの入力内容に誤りがある場合はエラーメッセージを表示します。
これはPowerCMS Xの機能ですが、フォームの投稿を選択した後集計を実行するとラジオボタンの項目等を集計して表示することができます。
まとめ
PTRESTfulAPIClientを利用することでPowerCMS Xのフォーム機能を容易に利用でき、コンテンツのみならず閲覧者からの投稿も管理できることが分かりました。APIを利用した実装の参考になれば幸いです。
繰り返しになりますが「Decoupling」の原則は読みました。よりよいフォームを提供するサービス等があればAPIルートを書き換えてそれに差し替えれば良いのです。