
概要
弊社では静的コーディングの際に Handlebars を使うケースが増えていますが、 WordPress 固定ページにインサートをするのが非常に大変です。
なので WordPress の固定ページを自動的に変換するコードを作りました。
一度 Handlebars から JSON に変換することによって、 PHP で簡単に読む事が出来るのでそのようにしました。
flowchart LR A[Handlebars] -->|Node.jsで処理| B[JSON] B -->|PHPで処理| C[WordPress]
1. HandlebarsをJSONに変換
フォルダ構成例
resources/handlebars/
に Handlebars コードを入れます。
dist/
に出力された JSON が入ります。
.
├── resources
│ └── handlebars
│ └── index.html
└── dist
 └── index.json
中間データとしてNode.jsでも PHP でも使える JSON に変換します。
'use strict';

const fs = require('fs');
const handlebars = require('handlebars');
const path = require('path');
const glob = require('glob');
const {JSDOM} = require('jsdom');
const prettier = require('prettier');
const makeDir = require('make-dir');

const srcDir = `${process.cwd()}/resources/handlebars`;
const distDir = `${process.cwd()}/dist`;

/**
 * @param {string} fileName
 * @param {string} srcDir
 * @param {string} distDir
 * @returns {Promise<void>}
 */
const convert = async (fileName, srcDir, distDir) => {
 try {
 const compile = await handlebars.compile(fs.readFileSync(path.resolve(srcDir, fileName)).toString());
 {
 const {document} = (new JSDOM(compile({}))).window;
 const output = {
 title: document.querySelector('title').innerHTML,
 html : prettier.format(document.querySelector('body').innerHTML, {parser: 'html'})
 };
 const distPath = path.resolve(distDir, fileName);
 makeDir(path.dirname(distPath)).then(() => {
 const htmlPath = path.format({
 dir : path.dirname(distPath),
 name: path.basename(distPath, '.hbs'),
 ext : '.json',
 });
 fs.writeFileSync(htmlPath, JSON.stringify(output));
 });
 }
 } catch (e) {
 console.error(e);
 }
};

glob(
 '**/*.hbs',
 {
 cwd : srcDir,
 ignore: '**/_*.hbs',
 },
 async (er, files) => {
 for (const fileName of files) {
 convert(fileName, srcDir, distDir);
 }
 }
);
2. JSONをWordPressの固定ページに入れる
JSON を WordPress の固定ページに入れます。
この時ページの親子関係も保存されます。
フォルダ構成例
PROHECT/
は WordPress インストール先です。
dist/
は Handlebars から変換された JSON が入ります。
.
├── PROJECT(WordPressインストール先)
│ ├── wp-blog-header.php
│ └── ・・・
├── dist
│ └── index.json
└── 下記PHPスクリプトファイル
<?phpconst DOING_AJAX = true;
$_SERVER['REQUEST_METHOD'] = 'GET';
require __DIR__ . '/PROJECT/wp-blog-header.php';
remove_filter('content_save_pre', 'wp_filter_post_kses'); // timeタグなどが削除されるのでフィルターを外す

function _scanDir($dir): array
{
 $list = [];
 foreach (glob($dir . '*/', GLOB_ONLYDIR) as $child) {
 if ($tmp = _scanDir($child)) {
 $list = array_merge($list, $tmp);
 }
 }
 foreach (glob($dir . '{*.json}', GLOB_BRACE) as $file) {
 $list[] = $file;
 }
 return $list;
}

function jsonToPage(array $json_files, string $root_dir)
{
 // 親ページを設定するため、親ページをなるべく前にソート
 usort($json_files, function ($a, $b): int {
 $a_c = substr_count($a, '/');
 $b_c = substr_count($b, '/');
 if ($a_c === $b_c) {
 if (substr($a, -10) === 'index.json') return -1;
 elseif (substr($b, -10) === 'index.json') return 1;
 return 0;
 }
 return $a_c <=> $b_c;
 });
 $parent_dirs = array_map(fn($dir) => str_replace('.json', '', str_replace('index.json', '', str_replace($root_dir, '', $dir))), $json_files);

 $set = []; // 親ページのコレクション
 foreach ($json_files as $key => $dir) {
 $json_str = file_get_contents($dir);
 $json = json_decode($json_str);
 $name = $parent_dirs[$key];
 $name_array = array_filter(explode('/', $name), fn($name) => $name !== '');
 if ($name !== '/' && substr_count($name, '/') >= 2) {
 // 第二階層以上
 $page_name = $name_array[array_key_last($name_array)]; // ページ名(ex: c)
 $current_name = implode('/', $name_array); // 親の名前を含めた現在のページの名前(ex: a/b/c)
 array_pop($name_array); // 現在のページを除外
 $parent_name = implode('/', $name_array); // 親のページの名前(ex: a/b)
 $parent_id = (array_key_exists($parent_name, $set)) ? $set[$parent_name] : 0; // 0の場合は、親無し(WordPressの仕様)
 // 親ページ
 $id = wp_insert_post([
 'post_title' => $json->title,
 'post_name' => $page_name,
 'post_content' => $json->html,
 'post_type' => 'page',
 'post_status' => 'publish',
 'post_parent' => $parent_id,
 ]);
 if (str_ends_with($name, '/')) {
 $set[$current_name] = $id;
 }
 } else {
 // 第一階層
 $id = wp_insert_post([
 'post_title' => $json->title,
 'post_name' => str_replace('/', '', $parent_dirs[$key]),
 'post_content' => $json->html,
 'post_type' => 'page',
 'post_status' => 'publish',
 ]);
 update_option('page_on_front', $id); // start page(ホームページ)に設定
 }
 }
}

$query = new WP_Query([
 'post_type' => 'page',
 'posts_per_page' => -1,
 ]);
while ($query->have_posts()) {
 $query->the_post();
 wp_delete_post($query->post->ID, true);
}

jsonToPage(_scanDir('dist'), 'dist');
これを実行すると、 JSON を読み込んで WordPress の中に自動的に固定ページが入っていきます。
実行例
Handlebars
<!doctype html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>TOP</title>
</head>
<body>
	<main>TOP</main>
</body>
</html>
出力されるJSON
{"title":"TOP","html":"<main>TOP</main>\n"}so
その後、出力された JSON を WordPress の固定ページに入れると管理画面では以下のようになります。

最後に
新規開発時に WordPress の管理画面上でコーディングをする事は非効率な事が多く、特にコーダーへ WordPress の説明や環境準備をする必要があり大変でした。
そのため WordPress と静的コーディングを分けていますが、今まではHTMLコーディングを WordPress へ取り込む際には管理画面に入り手動でやっていました。
ですがこのコードを開発・実行することにより、簡単に WordPress へ取り込む事が出来ました。
コードについては荒削りな所がありますが、是非使ってみてください。