cuerda blog

cuerda の開発者が考えていることを発信

連載 ChatGPTとCodexで進めるUbuntu開発環境移行

【第13回・最終回】テーマ専用テンプレートによるRSS本文生成を実装する

投稿日:

代替テンプレート

みなさま、こんにちは。クエルダ開発担当の渡部公也です。

前回は、Cuerda-Feed-Totalの内部ライセンス判定とHTTP経由のRSSレスポンスが一致しなかった原因を調査しました。

WP-CLIと通常のHTTPリクエストでは、PHPへ渡されるホスト情報が異なっており、同じ条件で比較できていなかったことが原因でした。

実行条件を揃えることで、データベース、キャッシュ、テーマ、RSS生成コードを変更せず、内部判定とHTTPレスポンスが一致することを確認できました。

Ubuntuへの開発環境移行に必要な基盤と確認方法が整ったため、今回は実際の機能改善をUbuntu上で完了させます。

実装するのは、WordPressの投稿本文だけでなく、使用中のテーマに用意したRSS専用テンプレートから本文を生成する機能です。

テーマ側で投稿、カスタムフィールド、画像などを組み合わせた後、Cuerda-Feed-Totalの既存処理へ渡し、各配信先の仕様に合わせて変換できるようにします。

この機能の実装と動作確認をもって、「ChatGPTとCodexで進めるUbuntu開発環境移行」の連載は最終回となります。

今回実装する機能

Cuerda-Feed-Totalでは、WordPressの投稿本文を取得し、配信先ごとの仕様に合わせて加工したうえでRSSへ出力しています。

一般的な記事であれば、投稿画面へ入力した本文をそのまま利用する現在の方法で対応できます。

一方、Webサイト上では投稿本文以外の情報を組み合わせて記事を構成している場合があります。

  • 投稿本文の前に独自のリード文を追加する
  • 複数のカスタムフィールドを決められた順番で表示する
  • 投稿に紐づく画像や補足情報を本文へ挿入する
  • 投稿タイプごとに異なる構成を使用する
  • 通常の画面では別の場所に表示される情報をRSS本文へ含める

このようなサイト固有の本文構成を、Cuerda-Feed-Total本体へ追加していくと、プラグインが案件ごとの仕様を抱えることになります。

そこで、サイト固有の本文構成はテーマが担当し、配信先固有の変換はCuerda-Feed-Totalが担当する構成としました。

テーマにRSS専用テンプレートを配置する

今回実装した機能では、使用中のテーマへ次のファイルを配置します。

/wp-content/themes/お使いのテーマディレクトリ/cuerda-feed/content.php

テーマディレクトリの中にcuerda-feedディレクトリを作成し、その中へcontent.phpを置きます。

お使いのテーマディレクトリ/
└── cuerda-feed/
    └── content.php

任意のPHPファイルを管理画面から指定する方式にはせず、テンプレートの場所とファイル名を固定しました。

ファイルの探索範囲を限定することで、意図しないPHPファイルが実行されることを防ぎ、利用者にとっても設置場所が分かりやすくなります。

子テーマを優先する

WordPressでは、親テーマを直接変更せず、子テーマを利用してサイト固有のカスタマイズを行うことがあります。

RSS専用テンプレートも、子テーマが有効な場合は子テーマ側を優先します。

テンプレートを探す順番は次のとおりです。

  1. 子テーマを確認する

    子テーマのcuerda-feed/content.phpが存在し、読み取り可能であれば使用します。

  2. 親テーマを確認する

    子テーマにテンプレートがない場合は、親テーマのcuerda-feed/content.phpを確認します。

  3. 標準の投稿本文を使用する

    子テーマと親テーマのどちらにもテンプレートがない場合は、従来どおりWordPressの投稿本文を使用します。

親テーマの更新によって独自のテンプレートが失われないよう、サイト固有の実装は子テーマへ配置できます。

管理画面で本文の生成方法を選択する

テーマ専用テンプレートを追加しても、既存サイトのRSS本文が自動的に変わることはありません。

管理画面の「本文の生成方法」で、テーマのRSS専用テンプレートを使用する設定を選択した場合だけ、新しい機能が有効になります。

RSS本文の生成方法
生成方法 内容
WordPressの投稿本文を使用 従来どおり、投稿画面へ保存された本文を使用する
テーマのRSS専用テンプレートを使用 テーマ内のcuerda-feed/content.phpを実行して本文を生成する

管理画面には、テンプレートを設置するための説明と、次のパスも表示するようにしました。

/wp-content/themes/your-theme-directory/cuerda-feed/content.php

子テーマと親テーマのどちらが優先されるかについても説明を加えました。

また、現在のテーマからテンプレートを検出できているかを、設定画面で確認できます。

既存のカスタムフィールド追加機能はそのまま利用する

Cuerda-Feed-Totalには、投稿本文の末尾へ指定したカスタムフィールドを追加する機能があります。

テーマ専用テンプレートを使用する場合も、この既存機能は引き続き有効です。

処理の順番は次のようになります。

テーマ専用テンプレートで本文を生成
        ↓
指定されたカスタムフィールドを末尾へ追加
        ↓
Cuerda-Feed-Totalの既存本文処理
        ↓
配信先の仕様に合わせて変換
        ↓
RSSの本文要素へ出力

これまでカスタムフィールドを本文末尾へ追加していたサイトでも、その設定を残したまま、本文の構成だけをテーマ側へ移せます。

RSS本文だけに適用する

今回のテーマ専用テンプレートは、RSSの本文に相当するcontentへだけ適用します。

記事の概要として使用するdescriptionには適用しません。

テーマ専用テンプレートの適用範囲
RSS要素 テーマテンプレート
content 適用する
description 適用しない

本文と概要の生成方法を同時に変更すると、既存のRSS出力へ広い影響が及ぶ可能性があります。

今回は目的をRSS本文の生成へ限定し、概要の生成方法はこれまでの動作を維持しました。

専用クラスでテンプレートを管理する

テーマテンプレートの探索と実行は、専用のクラスへ分離しました。

テンプレートの固定パスは、次のように定義されています。

cuerda-feed/content.php

専用クラスは、主に次の処理を担当します。

  • 子テーマからテンプレートを探す
  • 子テーマにない場合は親テーマを探す
  • ファイルが存在し、読み取り可能であることを確認する
  • 対象となる投稿をテンプレートへ設定する
  • テンプレートが出力したHTMLを文字列として取得する
  • 実行後にWordPressの投稿状態を元へ戻す

テンプレートの場所を探す処理と、本文を生成する処理を分けることで、管理画面の検出表示とRSS生成の両方から同じ判定を利用できます。

出力バッファでHTMLを取得する

WordPressテーマのテンプレートでは、HTMLを戻り値として返すのではなく、テンプレートタグやechoによって直接出力する方法が一般的です。

今回の機能では、テーマテンプレートの内容をそのままブラウザへ表示するのではなく、Cuerda-Feed-Totalの既存本文処理へ渡す必要があります。

そこで、PHPの出力バッファを利用します。

ob_start();

include $template_file;

$content = ob_get_clean();

ob_start()以降にテンプレートが出力した内容を一時的に保存し、ob_get_clean()で文字列として取得します。

取得したHTMLは、そのままRSSへ出力せず、既存の本文変換処理へ渡します。

WordPressの投稿状態を一時的に切り替える

テーマのテンプレートでは、the_title()the_content()get_the_ID()などのテンプレートタグを使用します。

これらを正しく動作させるには、現在処理している投稿をWordPressの投稿状態へ設定する必要があります。

テンプレートを実行する前に対象の投稿を設定し、setup_postdata()を使用してテンプレートタグを利用できる状態にします。

一方、RSSでは複数の投稿を順番に処理するため、一つの投稿で変更した状態を次の投稿へ残してはいけません。

そこで、テンプレート実行前のグローバル状態を保存し、実行後は必ず元へ戻すようにしました。

主に、次のような投稿関連の状態を保護します。

  • 現在の投稿
  • 投稿ID
  • 投稿者情報
  • ページ分割の状態
  • 現在の日付に関係する状態

テンプレート実行中に問題が発生した場合も、後処理で元の状態へ戻します。

テンプレート内で利用できる情報

content.phpでは、通常のWordPressテーマと同じように投稿情報を取得できます。

例えば、投稿本文と独自のカスタムフィールドを組み合わせる場合は、次のように記述できます。

<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}
?>

実際のテンプレートでは、サイトで使用しているカスタムフィールドや投稿構造に合わせてHTMLを組み立てます。

また、現在処理している配信先を判断できるよう、テンプレート内では配信先を表す変数も利用できます。

ただし、配信先ごとのHTML制限や変換処理をテーマへ重複して実装するのではなく、基本的な本文構成の調整に利用します。

テンプレートのHTMLを既存処理へ渡す

テーマテンプレートで生成したHTMLを、無加工のままRSSへ出力する設計にはしていません。

テーマ側では、Webサイトの表示に使用しているHTML要素、クラス、属性などが含まれる可能性があります。

一方、外部の配信先では、利用できるHTML要素や属性が制限されています。

Cuerda-Feed-Totalの既存本文処理では、配信先に応じて次のような処理を行います。

  • 利用できないHTML要素の除去
  • 不要な属性の削除
  • リンクや画像URLの調整
  • 相対URLから絶対URLへの変換
  • 画像要素の変換
  • 空の要素や不要な改行の整理

テーマが生成した内容も、投稿本文と同じ既存処理へ渡します。

そのため、テーマはサイト固有の本文構成に集中し、配信先の仕様は引き続きCuerda-Feed-Total側で管理できます。

テンプレートがない場合も既存動作を維持する

テーマ専用テンプレートを使用する設定であっても、テーマの変更やファイルの削除によってcontent.phpが見つからなくなる可能性があります。

テンプレートが存在しない場合は、従来のWordPress投稿本文を使用します。

これにより、テンプレートがないことを理由に、RSS本文が突然空になることを避けられます。

また、ファイルが見つからないというPHPの警告をRSSへ直接出力しないため、XMLの構造も壊しません。

管理画面ではテンプレートの検出状態を確認できるため、利用者はRSSを取得する前に設置状況を確認できます。

テンプレート実行中のエラーへ備える

content.phpはテーマ側で作成するPHPファイルなので、構文エラーや実行時エラーが発生する可能性があります。

実行中に補足可能なエラーが発生した場合は、処理中の出力を破棄し、空の本文として既存処理へ渡します。

プラグイン独自のエラーログ出力は追加していません。

WordPressやPHPが備える標準的なデバッグ方法を使用して、テンプレート側の問題を確認します。

また、エラーが発生した場合も、出力バッファとWordPressの投稿状態を確実に元へ戻すようにしました。

既存のキャッシュ処理は変更しない

Cuerda-Feed-Totalには、RSS生成時の負荷を軽減するためのキャッシュ処理があります。

テーマ専用テンプレートは、RSS本文を準備する部分だけを変更する機能です。

テンプレートで生成したHTMLは、既存の変換処理とRSS生成処理を通り、これまでと同じキャッシュの仕組みで扱われます。

新しいテンプレート専用のキャッシュや、独自のキャッシュ削除処理は追加していません。

処理経路を大きく変えず、本文の入力元だけを切り替えることで、既存のキャッシュや配信先別処理への影響を抑えました。

Ubuntu上でCodexと実装を進める

今回の実装も、これまでの連載で整えたAI伴走開発の流れに沿って進めました。

  1. ChatGPTで仕様を整理する

    テンプレートの固定パス、子テーマの優先、既存機能との関係、エラー時の動作、確認項目を整理しました。

  2. Codexが現在の処理を調査する

    投稿本文の取得、カスタムフィールドの追加、配信先別の変換、管理画面、キャッシュの処理経路を確認しました。

  3. 変更範囲を限定して実装する

    本文の入力元を切り替える処理と、テーマテンプレートを安全に実行する専用クラスを追加しました。

  4. Git差分を確認する

    既存のRSS変換処理や概要の生成処理へ、意図しない変更がないことを確認しました。

  5. WordPress上で動作を確認する

    テーマにテンプレートを配置し、実際のRSS本文へ内容が反映されることを確認しました。

  6. 管理画面とドキュメントを整える

    テンプレートの設置方法、固定パス、子テーマの優先関係を管理画面と技術資料へ追加しました。

環境構築だけでなく、実際の機能設計、実装、確認、公開準備までをUbuntu上で完了できました。

実装後の動作を確認する

実装後は、テーマのcuerda-feed/content.phpからRSS本文を生成できることを確認しました。

最初のテンプレートではPHPの記述に誤りがありましたが、構文を修正した後は想定どおり動作しました。

最終的には、次の内容を確認しています。

  • 標準設定では従来の投稿本文が使用されること
  • テーマテンプレートを選択するとcontent.phpが実行されること
  • 子テーマのテンプレートが親テーマより優先されること
  • テンプレート内で投稿本文とカスタムフィールドを取得できること
  • 既存のカスタムフィールド追加設定を併用できること
  • 生成されたHTMLが既存の本文変換処理を通ること
  • テーマテンプレートがdescriptionへ影響しないこと
  • テンプレートがない場合は標準の投稿本文が使用されること
  • 管理画面でテンプレートの設置方法を確認できること

これにより、既存サイトの動作を維持しながら、必要なサイトだけがテーマ専用テンプレートを利用できるようになりました。

Cuerda-Feed-Total 4.0.35として完成する

テーマ専用テンプレート機能と管理画面の説明を整え、Cuerda-Feed-Totalのバージョンを4.0.35へ更新しました。

管理画面には、テーマ内へcuerda-feedディレクトリを作成し、content.phpを配置する手順を追加しています。

プラグイン本体のバージョン、readme.txtのStable tag、変更履歴、翻訳用のPOTファイル、技術資料についても、4.0.35の内容へ合わせました。

4.0.35で追加した主な内容
項目 内容
RSS本文生成 テーマのcuerda-feed/content.phpから本文を生成できる
テーマ探索 子テーマを優先し、次に親テーマを確認する
既存機能 カスタムフィールドの本文末尾追加を引き続き利用できる
互換性 設定を変更しない既存サイトは従来の投稿本文を使用する
管理画面 テンプレートの設置方法、固定パス、優先順位を表示する
適用範囲 contentだけへ適用し、descriptionは従来の動作を維持する

これで、Ubuntuへ移行した開発環境上で、既存プラグインの新機能を設計し、実装し、公開できるところまで進めることができました。

今回の到達点

今回は、Cuerda-Feed-Totalへ、テーマ内の専用テンプレートファイルからRSS本文を生成する機能を実装しました。

テンプレートの場所はcuerda-feed/content.phpへ固定し、子テーマを親テーマより優先して探します。

テンプレートの出力は、WordPressの投稿状態を一時的に設定したうえで出力バッファへ取得し、既存のカスタムフィールド追加処理と本文変換処理へ渡します。

配信先ごとのHTML変換や除去は、これまでどおりCuerda-Feed-Totalが担当します。

また、テーマテンプレートはRSSのcontentだけへ適用し、descriptionには影響を与えません。

テンプレートが存在しない場合は従来の投稿本文へ戻るため、既存サイトの出力も維持できます。

管理画面にはテンプレートの設置方法と固定パスを追加し、Cuerda-Feed-Total 4.0.35として機能を完成させました。

連載を終えて

第1回では、WindowsとWSLを中心にしていた開発環境を、クリーンなUbuntuへ段階的に移行する目的と方針を整理しました。

その後、Git、GitHub CLI、Visual Studio Code、Codex、PHP、Composer、Apache、MariaDB、WordPress、Dockerなどを導入し、日常の開発環境と独立した検証環境を構築しました。

GitHubから実案件を取得し、シンボリックリンクでWordPressと接続し、Codexによる調査と変更、Git差分、構文検査、コーディング規約、WordPress上の動作確認までをUbuntu上で行えるようにしました。

さらに、長期間運用しているCuerda-Feed-Totalを移行し、ライセンス確認のような環境依存の問題を安全に調査したうえで、今回のテーマ専用テンプレート機能を実装しました。

これにより、Ubuntuは単にソフトウェアをインストールした検証用のパソコンではなく、ChatGPTとCodexを活用して実際の開発を完了できる環境になりました。

今回で「ChatGPTとCodexで進めるUbuntu開発環境移行」の連載は終了します。

今後は連載の続きとして環境構築を追うのではなく、プラグインやWebシステムで発生した個々の改善を一つのテーマとして取り上げ、設計、AIへの依頼、実装、検証の過程をblogへ書いて行きたいと思います。

最後までお読みいただき、ありがとうございました。