0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WordPressを静的ページビルダーにしたい(未完成)

Last updated at Posted at 2024-08-08

以下の内容は、WordPressの正しい使い方ではないので、その程度のものとしてお読みください。
また、単に静的サイトを作るなら、Simply Staticというプラグインが使いやすいようです。

やりたいこと

  • WordPressで静的サイトを管理したい
  • ブロックエディタで記事を書きたい
  • 外部からのサイト閲覧者には、wp-admin/ ディレクトリ等にアクセスしてほしくない
  • 一括生成(SSG)のような、時間のかかることはやりたくない

基本的なアイディア

  • save_postフックを利用して、記事が保存される度にfile_put_contents()する

現時点で静的化できていないこと

以下のページには、動的処理が必要。

  • archiveページ(ブログ投稿の一覧ページ)
  • sitemap.xml
  • 記事検索
  • メールフォーム等

不安要素

WordPressの本来の使い方ではないので、将来的なバージョンアップによって、htmlの構造やcssが変化した場合、追従できないかもしれません。

作業手順1:サブドメインを3つ用意する

サブドメインを3つ用意します。

  1. www.example.com -> 外部からアクセスされるドメイン
  2. img.example.com -> アップロードしたメディアファイル用のドメイン
  3. 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で十分ということになってしまいます。

いくつか方法を試してみて、ちょうど良い方法が見つかったら、追記したいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?