Next.jsとWordPressのREST APIで記事検索を実装する

2024/11/27 (水) - 09:00 JavaScriptProgram

WordPressでヘッドレスCMSを作成する時、記事検索結果ページを動的に実装したのでそのメモです。環境は以下を想定。

  • WordPress 6.5
  • Next.js v14.x(App Router)

記事一覧と検索ボックスの例

まずWordPressのREST APIのエンドポイントを指定しておきます。例では.envファイルにはAPIのエンドポイントを指定しました。これは記事一覧や詳細でも同じAPIを呼び出します。

BLOG_JSON_URL='https://example.com/wp-json/wp/v2/posts/'
BLOG_NAME='あいかの日記帳'

次に検索ボックスのinput要素を用意します。name属性はsとしておき、GETパラメータで検索ワードを送信する形にします。

<form method="get" action="/search/">
 <input type="search" value="" placeholder="キーワードを入力" name="s" id="s" />
</form>

検索結果一覧ページです。/search/page.jsとし、以下のざっくりとしたコードです(アイキャッチがない場合の処理やエラーなど詳細は省いています)。記事詳細などはSSG
でいいのですが、この部分だけはSSRで動的レンダリングする想定で組んでおります。fetchするときAPIにsearchパラメータでキーワードを指定することで、該当するキーワードに該当する記事のみを取得することが出来ます。

import Link from 'next/link';
async function getPostSearch(url) {
 const res = await fetch(url);
 return res.json()
}
async function getPostSearchTotal(url) {
 const count = await fetch(url).
 then((res) => {return parseInt(res.headers.get('X-WP-Total'))});
 return count;
}
export default async function SearchPage( props ) {
 const search = props.searchParams.s;
 const url = `${process.env.BLOG_JSON_URL}?_embed&search=${search}`;
 const posts = await getPostSearch(url);
 const length = await getPostSearchTotal(url);
 if(!length){
  return (
   <div>
    <h1>{search}は見つかりませんでした</h2>
    <p><Link href='/'>一覧に戻る</Link></p>
   </div>
  )
 }else{
  return (
   <div>
    <h1>{search} の検索結果:{ length }件</h1>
    <ul>
    { posts.map((post) =>
     <li key={post.id}>
      <Link href={`/${post.slug}`}>
       <img src={post._embedded['wp:featuredmedia'][0].source_url} width="100" height="100" alt="" loading="lazy" />
       <p>{post.title.rendered}</p>
      </Link>
     </li>
    )}
    </ul>
   </div>
  );
 }
}

記事一覧と検索ボックスの例

ポイントとしてはWordPressからJSONを受け取る際にレスポンスヘッダーのX-WP-Totalから記事数を取得します。また、この例ではアイキャッチ画像を取得するため、エンドポイントに_embedパラメータを指定しています。

[CSSのみ]View Transitions APIで画面遷移アニメーションの実装

2024/11/20 (水) - 09:00 CSSJavaScript

View Transitions APIで画面遷移するときのトランジションアニメーションがCSSだけでも動くようになりました。CSSをに@view-transitionを付与するだけで、簡単にページ遷移時のトランジションが発火します。ただし、現時点(2024年11月現在)では最新のGoogle Chrome(126)とEdgeだけで動作し、SafarやFirefoxは未対応のようです。実装するにはCSSで以下のように指定をします。

@view-transition {
 navigation: auto;
}

要素ごとのアニメーションの実装

遷移前と遷移後のページで特定の要素を連携させてアニメーションすることも可能です。以下のような動きもHTMLとCSSだけで再現できます。

遷移のイメージ

遷移前と遷移後の連携したいHTML要素に一意のclass属性などを指定します。例では記事一覧のli要素と、記事詳細のarticleの動きを連動させるものです。

遷移前HTML

<li class="list-1"><a href="#">****</a></li>

遷移先HTML

<article class="list-1">****</article>

対象の要素のCSSセレクタに、view-transition-nameプロパティを指定し、同一の値を設定します。注意点としては同じページ内に複数のview-transition-name値があると正常に動かないことです。必ず個別に指定する必要があります。

.list-1{
 view-transition-name: list-1;
}

動的に処理する場合とか

CMSの記事一覧などを動的に処理する場合でもview-transition-nameはユニークな値をつけてあげる必要があります。例えばNext.jsでWordPressの記事一覧を取得するときなどは値に記事IDを付与するなどで実装します。

const postList = ({post}) => {
 return (<li key={post.id} style={{ 'view-transition-name':`list-${post.id}` }}>
  <Link href={`/${post.slug}`}>
   要素
  </Link>
 </li>);
}

早くほかのブラウザでも自由に使えるようになると良いですね。ちなみに、非対応のブラウザでは無視されてプログレッシブエンハンスメントで通常の動作になるため、サイトの閲覧の弊害になることはありません。

clipboard.writeTextでクリップボードにテキストをコピーするときはhttpsで行う

2024/11/15 (金) - 09:00 HTMLJavaScript

Webページでテキストをクリップボードにコピーさせる実装はよくあると思うのですが、なぜか動かなかったのでメモ。HTMLは以下のような感じ。

<textarea id="txtCopy" readonly>「YOASOBI DOME LIVE」
■大阪公演
xx月xx日(土) 京セラドーム大阪
開場 15:30 / 開演 18:00

■東京公演
xx月xx日(土) 東京ドーム
開場 15:30 / 開演 18:00</textarea>
<button id="btnCopy">ライブ情報をコピーする</button>

JavaScriptは以下のようにしました。

const txtCopy = document.getElementById('txtCopy');
const btnCopy = document.getElementById('btnCopy');
btnCopy.addEventListener('click', () => {
 if (!navigator.clipboard) {
  console.log('コピー出来ません',navigator.clipboard);
  return;
 }
 navigator.clipboard.writeText(txtCopy.value).then(
 () => { alert('ライブ情報をコピーしました'); },
 () => { alert('ライブ情報のコピーに失敗しました'); });
});

こちらインターネットにアップしたWebサーバ上だと動いたのに、なぜかlocalhostで動かそうと思ったら動かずnavigator.clipboardundefinedとなっていました。実はこのAPIはhttp(非SSL)では動かず、https環境下でのみ動作します。

http(非SSL)でクリップボードコピーを実装する

httpでもexecCommand('copy')を使えばクリップボードへのコピーが使えます。しかし、このメソッドは現在非推奨です。

const txt = document.getElementById('txtCopy');
const btn = document.getElementById('btnCopy');
btn.addEventListener('click', () => {
 txtCopy.select();
 document.execCommand('copy');
 alert('ライブ情報をコピーしました');
});

現在はほぼhttpsで運用するケースが多いともうので、よほどのことがない限りはnavigator.clipboard.writeTextを使いましょう。

Reactでボタン<button>の有効・無効(disabled属性)を切り替える

2024/11/11 (月) - 09:00 JavaScript

Reactでボタンの有効・無効(disabled属性)を切り替えたい…。以下のように記述すると可能のようです。

<button disabled={true or false}>ボタン</button>

disabled=trueでボタン無効化、disabled=falseでボタン有効化…となります。これをReactのスクリプト側で制御する場合は単純にuseStateを使うのが一般的かと思います。

条件によってdisabledを変更する

例:画像更新ボタン押下後、画像描写が完了するまでボタンを無効化にする

猫の画像をランダムに出すTheCatAPIとfetchを使い、読み込みが完了するまで更新ボタンを無効化にするみたいな実装。これでボタン連打を禁止にさせ、APIへの過剰アクセスを防止させてみました。エラー対策など細かい処理は省略してますが大雑把にするとこんな感じになると思います。

import { useEffect, useState } from 'react';
export default function APP() {
 const [data, setData] = useState(null);
 const [disable, setDisable] = useState(true); //useStateでdisableを制御
 const getCAT = async () => {
 const res = await fetch(`https://api.thecatapi.com/v1/images/search`).then((res) => res.json());
  setData(res[0].url);
 }
 useEffect(() => {
  getCAT();
 }, [])
 const onClick = async () => {
  setDisable(true); //ボタン押下でボタンをdisableにする
  await getCAT();
 }
 const onLoad = () => {
  setDisable(false); //読み込み完了でボタンをenableをにする
 }
 if(data){
  return (
   <div>
   <p><img src={data} alt="" onLoad={onLoad} /></p>
   <button onClick={onClick} disabled={disable}>更新するにゃん</button>
   </div>
  );
 }else{
  return (<p>Loading</p>);
 }
}

動きのイメージ。CSSでbutton[disabled]のセレクタでボタンの色とカーソルを意図的に変えています。

動きのイメージ

ねこかわいいよねこ

Astro + microCMSでOGPタグを設定する(トップ・記事詳細)

2024/11/08 (金) - 09:00 JavaScriptProgram

フロントサイドのフレームワークのAstroとヘッドレスCMSのmicroCMSでブログを作った時の話。トップページと記事詳細ページにそれぞれOGPを実装したときのメモです。もっと完結でシンプルないい方法があればいいなァ…などと。

環境は以下の通りで、microcms-js-sdkを利用。実装方法は公式ドキュメントに準じました。

  • microcms-js-sdk 3.1.x
  • Astro 4.16.x

ずんだもんのずんずんブログ

やりたいこと

  • トップページと記事詳細でog:typeを分ける(トップページ:website、記事詳細ページ:article
  • トップページと記事詳細でog:imageを分ける(トップページ:決められた看板画像、記事詳細ページ:記事のアイキャッチ画像)
  • トップページと記事詳細でog:descriptionを分ける(トップページ:決められた文字、記事詳細ページ:記事の抜粋)
  • canonical(カノニカル)も指定する

microCMSで取得するフィールド

  • 記事ID(id)<記事詳細のURLのスラッグとする>
  • ページタイトル(title)
  • 記事本文(content)
  • 記事の抜粋(excerpt)
  • アイキャッチ画像URL(eyecatch)
  • 公開日(publishedAt)

.envファイル

サイト名やディスクリプション、APIのエンドポイントを指定します。

SiteTitle=ずんずんブログ
SiteDescription=カワイイずんだもんのブログをみるのだ
MICROCMS_SERVICE_DOMAIN=*****
MICROCMS_API_KEY=*****

astro.config.mjsファイル

公開するドメインを指定します。

import { defineConfig } from 'astro/config';
export default defineConfig({
  site: 'https://example.com'
})

Layout.astroファイル

head要素内のSEO関連タグなどをPropsから取得し指定します。さらに、Astro.url.pathnameでトップページか否かをを判定し、og:typeの分岐をしています。

---
const SiteTitle = import.meta.env.SiteTitle;
const Url = new URL(Astro.url.pathname, Astro.site);
const {Meta} = Astro.props;
const IsHome = Astro.url.pathname === '/';
const OgType = IsHome ? 'website' : 'article';
const PageTitle = IsHome ? SiteTitle : `${Meta.title} - ${SiteTitle}`;
---
<!DOCTYPE html>
<html lang="ja">
 <head prefix="og: https://ogp.me/ns#">
  <meta charset="UTF-8" />
  <meta content="width=device-width" name="viewport" />
  <meta content={Astro.generator} name="generator" />
  <meta content={Meta.description} name="description" />
  <meta content="max-image-preview:large" name="robots" />
  <meta property="og:title" content={Meta.title} />
  <meta property="og:type" content={OgType} name="og_type" />
  <meta property="og:site_name" content={SiteTitle} name="og_site_name" />
  <meta property="og:url" content={Url} name="og_url" />
  <meta property="og:image" content={Meta.image} name="og_image" />
  <meta property="og:description" content={Meta.description} name="og_description" />
  <meta content="summary_large_image" name="twitter:card" />
  <meta content={Meta.title} name="twitter:title" />
  <meta content={Meta.description} name="twitter:description" />
  <meta content={Meta.image} name="twitter:image" />
  <link rel="canonical" href={Url} />
  <title>{PageTitle}</title>
 </head>
 <body>
  <header>
   <h1>{SiteTitle}</h1>
  </header>
  <slot />
 </body>
</html>

CSSの読み込みやtouch-icon、faviconなどの指定は省略しています。

index.astroファイル(トップページ)

metaのOGPに指定するディスクリプションとOGP画像を指定した値に設定します。

---
import Layout from '../layouts/Layout.astro';
import { getBlogs } from '../library/microcms';
import { format } from 'date-fns';
const Meta = {
 title: 'トップページ',
 description: import.meta.env.SiteDescription,
 image: new URL('/asset/images/ogp.png', Astro.site)
}
const res = await getBlogs({ fields: ['id','title','excerpt','publishedAt'] });
---
<Layout Meta={Meta}>
 <main>
  <ul>
  {
  res.contents.map((content) => (
   <li><a href={content.id}>
    <h2>{content.title}</h2>
    <p><time>{format(new Date(content.publishedAt), 'yyyy年MM月dd日')}</time></p>
    <p>{content.excerpt}</p>
   </a></li>
  ))
  }
  </ul>
 </main>
</Layout>

[id].astroファイル(記事詳細ページ)

SSGのためのgetStaticPathsを記述するほか、APIから取得した記事データを表示し、metaのOGPに指定する情報を格納します。

---
import Layout from '../layouts/Layout.astro';
import { getBlogs, getBlogDetail } from '../library/microcms';
import { format } from 'date-fns';
export async function getStaticPaths() {
 const response = await getBlogs({ fields: ['id'] });
  return response.contents.map((content) => ({
   params: {
   id: content.id,
  }
 }));
}
const { id } = Astro.params;
const res = await getBlogDetail(id);
const Meta = {
 title: res.title,
 description: res.excerpt,
 image: res.eyecatch.url
}
---
<Layout Meta={Meta}>
 <main>
  <h1>{res.title}</h1>
  <p><time>{format(new Date(res.publishedAt), 'yyyy年MM月dd日')}</time></p>
  <div set:html={res.content}></div>
 </main>
</Layout>

あとは巡回して正常にOGPが指定されているか確認すればOKです。

Googleカレンダー&Office 365 カレンダーでイベントを発行するリンクを作成

2024/11/05 (火) - 09:00 HTML

Webサイト上からセミナーやイベントや会議のイベントを、簡単に自分のカレンダーに登録できる昨日を実装します。例として以下のイベントを仮定します。

  • イベント名:シェフ大泉による車内クリスマスパーティー
  • イベントの詳細:パイ食わねぇか?
  • 日時:2024年12月24日19:30〜2024年12月24日23:30
  • 場所:HTB駐車場
  • 招待したい人のメールアドレス:onちゃん(安田さん)<on-chan@example.com>

予定を書き込む

Googleカレンダーリンク発行

以下のフォーマットでパラメータを指定します。

https://www.google.com/calendar/render?action=TEMPLATE&dates=[開始日時]/[終了日時]&location=[場所]&text=[イベント名]&details=[イベントの詳細テキスト]&add=[招待したい人のメールアドレス]
例:https://www.google.com/calendar/render?action=TEMPLATE&dates= 20241224T193000/20241224T233000&location=HTB駐車場&text=シェフ大泉による車内クリスマスパーティー&details=パイ食わねぇか?&add=on-chan@example.com

開始日時と終了日時の指定は、2024年12月24日 19時半であれば、20241224T193000と指定します。メールアドレスはカンマ(,)区切りで複数指定できます。

Office 365 カレンダー(Outlook)のイベントリンク発行

以下のフォーマットでパラメータを指定します。

https://outlook.office.com/calendar/deeplink/compose?startdt=[開始日時]&enddt=[終了日時]&location=[場所]&subject=[イベント名]&body=[イベントの詳細テキスト]&to=[招待したい人のメールアドレス]
例:https://outlook.office.com/calendar/deeplink/compose?startdt=2024-12-24T19:30:00+09:00&enddt=2024-12-24T23:30:00+09:00&location=HTB駐車場&subject=シェフ大泉による車内クリスマスパーティー&body=パイ食わねぇか?&to=on-chan@example.com

開始日時と終了日時の指定は、2024年12月24日 19時半であれば、2024-12-24T19:30:00+09:00と指定します。メールアドレスはセミコロン(;)区切りで複数指定できます。

いずれのリンクもWebに掲載する際は日本語部分のパラメータ値はエンコードしてあげる必要があります。大泉洋→%E5%A4%A7%E6%B3%89%E6%B4%8Bのように。

SSHに接続したら WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! と出たときの対処

2024/11/01 (金) - 09:00 Server

SSHに接続しようとしたら以下の警告が表示されて先に進めなくなりました。

$ ssh user@192.168.0.1
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:********
Please contact your system administrator.
Add correct host key in /****/.ssh/known_hosts to get rid of this message.
Offending ED25519 key in /****/.ssh/known_hosts:***
Host key for 192.168.0.1 has changed and you have requested strict checking.
Host key verification failed.

この場合、ssh-keygenコマンドで該当するホストの情報を消す。

$ ssh-keygen -R 192.168.0.1

あるいは、警告文で指定されている/****/.ssh/known_hostsを開き、該当の行を削除して保存すれば接続できると思います。

192.168.0.1 ssh-ed25519 ******

ヨシ!

microCMS+Next.js で記事検索ページを作る

2024/10/31 (木) - 09:00 JavaScriptProgram

和製のヘッドレスCMSであるmicroCMSとフロントエンドのNext.jsを使って記事の検索ページを作ったときの備忘録メモ。環境は以下でApp Routerで実装しました。

  • microcms-js-sdk 3.1.x
  • Next.js 14.0.x

まずはHTMLで検索フォームを用意します。コンポーネント化して使い回すのも良き。

<form action="/search/" method="GET" name="form">
 <input type="search" name="q" placeholder="馬名を入れてね" />
</form>

検索結果ページを用意します。ファイルは/search/page.jsとしました。コードは以下のように記述し、内容やエンドポイントはCMSのフィールドに応じて修正します。

import { client } from '@/app/libs/client';
import Link from 'next/link';
export default async function SearchPage( props ) {
 const q = props.searchParams.q;
 const data = await client.get({
  endpoint: 'post',
  queries: { q: decodeURI(q) }
 });
 if(!data.totalCount){
  <!-- 見つからない時 -->
  return <h2><em>({q})</em>ないです。</h2>
 }else{
  return (
   <div>
    <!-- 見つかった時 -->
    <h2><em>{q}</em>は<em>{data.totalCount}</em>件 見つかりました!</h2>
    <ul>
     {data.contents.map((data) => (
      <li key={data.id}>
       <Link href={`/horse/${data.id}`}>{data.title}</Link>
      </li>
     ))}
    </ul>
   </div>
  );
 }
}

上記のサンプルコードのポイントとしてはqパラメータを利用して検索を使用するところです。

また、filtersパラメータを利用して特定のフィールドに限って検索することも出来ます。その場合は、filters: 【フィールド名】[contains]【検索値】で指定します。例えばtitleおよびcontentというフィールド値に対し該当のキーワードを含む記事のみ絞り込みたい場合は、filters: `title[contains]${q}[or]content[contains]${q}`と指定します。filtersパラメータの詳細は以下を参考に。

qパラメータはmicroCMSで予め用意されているので動作が高速ですが、filtersパラメータは少し時間がかかるのがデメリットです。

検索結果画面の例

記事詳細ページや一覧ページなどはSSGでもいいのですが、上記のような動的のページを作る場合はSSRでNode.jsをデーモンで実行する必要があります。

関連記事

JavaScriptのFetchAPIでXMLファイルを読み込む

2024/10/24 (木) - 09:00 JavaScript

JavaScriptのFetchAPIを使い、XMLを読み込みたい。fetchを使う場合、JSONやテキストを読み込むことが多いと思いますが、XMLを読み込み処理する場合はDOMParserでパースすると良いみたいです。

<?xml version="1.0" encoding="UTF-8"?>
<pokemon>
 <item>
  <name>ゼラオラ</name>
  <type>でんき</type>
 </item>
 <item>
  <name>ルカリオ</name>
  <type>かくとう/はがね</type>
 </item>
 <item>
  <name>リザードン</name>
  <type>ほのお/ひこう</type>
 </item>
 <item>
  <name>ザングース</name>
  <type>ノーマル</type>
 </item>
</pokemon>

上記のようなXMLファイルがあったとします。以下のスクリプトでXMLを読み込み解析します。

const getURL = 'sample.xml';
const parser = new DOMParser();
const xmlLoad = async () => {
 const res = await fetch(getURL,{'mode':'cors'})
 .then((r) => r.text())
 .then((d) => {
  const xml = parser.parseFromString(d,'text/xml');
  const item = xml.querySelectorAll('item');
  item.forEach((el) => {
   console.log(el.querySelector('name').textContent);
  });
 });
};
xmlLoad();

実行結果。

ゼラオラ
ルカリオ
リザードン
ザングース

もちろん、HTML側に出力して表示させることも可能です。

<ul id="list"></ul>
const list = document.getElementById('list');
/*〜中略〜*/
item.forEach((el) => {
 list.insertAdjacentHTML('beforeend',`<li>${el.querySelector('title').textContent}</li>`);
});

なお、Webブラウザから外部ドメインに通信する場合は配信側がCORSに対応している必要があります。対応していない場合はクロスオリジン制約によってエラーとなるので注意が必要です。

Access to fetch at '{リクエスト先}' from origin '{リクエスト元}' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

上記はエラーの例。

PHPとGASを使ってフォームの内容をGoogleスプレッドシートに書き込む

2024/10/18 (金) - 09:00 JavaScriptProgram

PHPとGASを使って、HTMLの入力フォームから送信した内容を任意のGoogleスプレッドシートに反映させてみるとです。今回はかなり簡易的に実装した例ですので、エラー判定やデータチェックなどの処理は省略しています。

まずフォームのHTMLを用意します。今回は簡易的に名前、メールアドレス、性別という項目としました。name属性を忘れずに。

<form action="" method="post">
 <p>名前:<input type="text" name="name" placeholder="姓 名"></p>
 <p>メールアドレス:<input type="email" name="mail" placeholder="hoge@example.com"></p>
 <p>性別:<select name="sex">
  <option value="女性">女性</option>
  <option value="男性">男性</option>
  <option value="その他">その他</option>
  <option value="未回答">未回答</option>
 </select></p>
 <p><input type="submit" name="submit" value="送信する"></p>
</form>

次に、対象となるスプレッドシートを用意します。こちらも名前、メールアドレス、性別で項目を作ります。そのスプレッドシートを開いた状態で、メニューから[拡張機能]→[Apps Script]を選択します。コード入力欄が表示されるので以下のようにコードを入力します。

function doPost(e) {
 const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
 const getLastRow = spreadSheet.getSheets()[0].getLastRow(); //最後の行を取得
 let getName,getMail,getSex;
 let res;
 if(e){
  //POSTデータを取得
  getName = e.parameter.name;
  getMail = e.parameter.mail;
  getSex = e.parameter.sex;
  //行を追加してセルに書き込む
  spreadSheet.getSheets()[0].getRange((getLastRow + 1), 1).setValue(getName);
  spreadSheet.getSheets()[0].getRange((getLastRow + 1), 2).setValue(getMail);
  spreadSheet.getSheets()[0].getRange((getLastRow + 1), 3).setValue(getSex);
  res = JSON.stringify({result: 'Success'});
 }else{
  res = JSON.stringify({result: 'Error'});
 }
 return ContentService.createTextOutput(res).setMimeType(ContentService.MimeType.JSON);
}

今回使うdoPostは、POSTパラメータのデータを処理するときに使う関数です。要となるのはスプレッドシートの行数を取得し、最後の行の次にPHPから受け取った値をセルごとで書き込むところです。POSTデータを取得するには、e.parameter.input要素のname属性としてパラメータ取得します。最後に正常に処理されたかをJSON形式で返します。

上記をのコードを書き込んだら、スクリプトをデプロイします。ヘッダーの[デプロイ]ボタンから、[新しいデプロイ]を選択し任意の名前をつけます。

歯車マークから[ウェブアプリ]を選択し、[アクセスできるユーザー]を[全員]にして、[デプロイ]ボタンを押下します。正常にデプロイできると完了画面が表示されるので、発行された[ウェブアプリURL]を控えておきます(例:https://script.google.com/macros/s/*****/exec

最後にPHPでHTMLのフォームから送信されたパラメータをGoogleスプレッドシートに連携するコードを記述します。ウェブアプリURLには前段で発行したウェブアプリURLを記入します。input要素から取得した値をPOSTパラメータを処理して受け取り、ウェブアプリURLに連携します。

<?php
$urlGS = 'ウェブアプリURL'; //先ほど控えたウェブアプリURL
if($_POST){
 $getData = array(
  'name' => htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8'),
  'mail' => htmlspecialchars($_POST['mail'], ENT_QUOTES, 'UTF-8'),
  'sex' => htmlspecialchars($_POST['sex'], ENT_QUOTES, 'UTF-8')
 );
 $setData = array(
  'http' => array(
   'method'  => 'POST',
   'content' => http_build_query($getData)
  )
 );
 $resData = file_get_contents($urlGS, false, stream_context_create($setData));
 if(json_decode($resData)->result === 'Success'){
  echo '<p>正常に送信できました</p>';
 }else{
  echo '<p>正常に送信できませんでした</p>'; 
 }
}
?>

処理が正常に行われたかはJSONでレスポンスを受け取っています。正常に動くと以下のようにスプレッドシートに送信された内容が追記されます。

以上、自分用メモです。

ページの先頭へ