以下の内容は、WordPressの正しい使い方ではないので、その程度のものとしてお読みください。
また、単に静的サイトを作るなら、Simply Staticというプラグインが使いやすいようです。
やりたいこと
- WordPressで静的サイトを管理したい
- ブロックエディタで記事を書きたい
- 外部からのサイト閲覧者には、
wp-admin/
ディレクトリ等にアクセスしてほしくない - 一括生成(SSG)のような、時間のかかることはやりたくない
基本的なアイディア
- save_postフックを利用して、記事が保存される度にfile_put_contents()する
現時点で静的化できていないこと
以下のページには、動的処理が必要。
- archiveページ(ブログ投稿の一覧ページ)
- sitemap.xml
- 記事検索
- メールフォーム等
不安要素
WordPressの本来の使い方ではないので、将来的なバージョンアップによって、htmlの構造やcssが変化した場合、追従できないかもしれません。
作業手順1:サブドメインを3つ用意する
サブドメインを3つ用意します。
- www.example.com -> 外部からアクセスされるドメイン
- img.example.com -> アップロードしたメディアファイル用のドメイン
- admin.example.com -> 管理画面用のドメイン
作業手順2:フォルダ構成を考える
以下のようなフォルダ構成を作っていきます。
~/
└── admin_example_com/
├── wp-admin/
└── wp-content/
├── uploads/
└── plugins/
└── my_plugin/
├── www/
└── page_build.php
私は、この記事を書くためにロリポップレンタルサーバーで試作しましたが、他のレンタルサーバーでも同様のことができるはずです。
たとえば、ConoHa wingの場合は、サブドメインの割り当てができない代わりに、シンボリックリンクが張れます。
admin_example_com/ ディレクトリに、admin.example.comのサブドメインを割り当てます。(サブドメインの設定方法は、ロリポップレンタルサーバーのヘルプを参照してください)
admin_example_com/wp-content/plugins/my_plugin/www/ ディレクトリに、www.example.comのサブドメインを割り当てます。
admin_example_com/wp-content/uploads/ ディレクトリに、img.example.comのサブドメインを割り当てます。
作業手順3:WordPressのテーマは外部から隠す
WordPressのテーマは、何も表示させないテーマにしておくと良いです。
なお、表示の無いテーマにしたとしても、管理画面用のサブドメインには、外部からアクセス可能なので、WordPressのセキュリティプラグインを入れて対策しておいた方が良いです。
<?php
// index.php
if ( ! defined( 'ABSPATH' ) ) exit;
if ( ! current_user_can( 'edit_posts' ) ) exit;
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>確認用</title>
<?php wp_head(); ?>
</head>
<body>
<?php
if ( have_posts() ) { // wpの内容表示
while ( have_posts() ) {
the_post();
if(is_page() || is_single()) {
?>
<main style="max-width:30rem;">
<h1><?php the_title(); ?></h1>
<p><?php the_time('Y-m-d G:i'); ?></p>
<?php the_content(); ?>
</main>
<?php
}else {
?>
<section style="max-width:30rem;">
<p><?php the_time('Y-m-d'); ?></p>
<h3>タイトル:<a href="<?php the_permalink(); ?>">
<?php the_title(); ?></a></h3>
<p>要約:<br/><?php the_excerpt(); ?></p>
</section>
<hr/>
<?php
}
}
} else { // wpの内容が無いとき
echo '記事がありませんでした';
}
?>
<?php wp_footer(); ?>
</body>
</html>
作業手順4:HTMLファイルの出力用プラグインを作る
save_postというフックを使います。
独自プラグインとして、以下のような関数を登録します。
<?php
/*
* Plugin Name: static page builder plugin
*/
if ( ! defined( 'ABSPATH' ) ) exit;
function my_save_post ($post_id) {
// auto saveのとき何もしない
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// publish でないとき(下書きなど)何もしない
$post_status = get_post_status($post_id);
if ($post_status !== 'publish') {
return;
}
// 保存先
$file_name = ABSPATH . 'wp-content/plugins/my_plugin/www/' . $post_id . '.html';
$post = get_post($post_id);
$html_src = '<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>' . esc_html($post->post_title) . '</title>
</head>
<body>' . PHP_EOL . wp_kses_post($post->post_content) . PHP_EOL . '</body>';
file_put_contents($file_name, $html_src);
}
add_action('save_post', 'my_save_post');
ここでのポイントは、body
タグ内をすべて$post->post_content
にしてしまうことです。
WordPressは、ブロックエディタの出現によって、表現力が大きく向上しました。
このことを最大限有効に使いたいです。
共通ヘッダーやフッター、サイドメニューなどをブロックにしてしまえば、使いまわしできます。
※ただし、静的ファイルに出力してしまうので、後から動的に変更させることはできません。
もし後から一括変更のようなことがしたい場合は、HTMX等を検討したほうが良いと思います。
なお、file_put_contents()は、WordPress的には非推奨のようで、WP_Filesystemを利用した方が良いようです。
上記の例では、記事IDを名前にしてファイル保存していますが、slug等を利用しても良いと思います。
slugが、home
のときはindex.htmlとして保存するなど、ルーティング設定のようなものを書いていくと、メンテナンスが楽になりそうです。
投稿記事の保存時にはsave_post_post
、固定ページの保存時にはsave_post_page
等のフックが使えます。
また、ゴミ箱に移動するときのフックは、trashed_post
があり、記事が削除されたときにはbefore_delete_post
があります。
またcomposer等が使える環境なら、Twig等のテンプレートエンジンを使うと、HTMLの出力が便利になるはずです。
作業手順5:画像やその他メディアの取扱い
WordPressでは、アップロードした画像データは、wp-content/uploads/
ディレクトリに保存されます。
そして、記事中の画像のsrcも、admin.example.com/wp-content/uploads/ディレクトリにリンクされてしまいます。
これを変更するためには、wp-admin/options.php
にアクセスし、upload_url_path
を設定変更します。
先ほど用意した、img.example.com/ を、upload_url_path
に設定しておけば、画像がリンクされるはずです。
(未完成部分)ブログ投稿の一覧ページや、検索ページをどうするか
ここまでで、投稿ページや固定ページ等の出力はできるはずです。
問題は、一覧ページや、検索機能の作り方です。
ネット上を調べると、以下のような方法が見つかります。
- /wp-json/を使って、JavaScriptのfetch()等で取得する
- /wp-json/を使って、phpのcurl()等で取得する
- wp-load.phpで、WordPressを呼び出して、wp-query()等で取得する
- phpのPDOで、直接データベースに問い合わせる
いずれも、動的処理をすることになるので、メンテナンスが面倒そうです。
wp-jsonを使う方法が、比較的安全かなとは思いますが、結局はadmin.example.comにアクセスすることになるので、あまりやりたくありません。
理想は、完全に静的化することなので、記事一覧に必要な分(タイトルやURL、要約文やアイキャッチ画像等)だけの静的なjsonファイルを投稿記事全体について出力することなのですが、記事数が多くなると、一括出力も大変になっていきます。
それに、この方法では本文の検索機能が追加できません。
さらに、その一括出力を実行するフックを何にするかが、また悩ましいです。
記事を保存するたびに、複数のファイルを保存するのは、やや過剰のような気もします。
管理画面のメニューボタンを設定し、「記事一覧jsonをビルド」というボタンを作ってみても良いのかもしれません。
しかし、記事更新のたびにビルドしなおさないといけないようなら、AstroなどのSSGで十分ということになってしまいます。
いくつか方法を試してみて、ちょうど良い方法が見つかったら、追記したいと思います。