2026年10月5日

2026年10月5日

WordPressのFacadeパターンで複雑なAPIをシンプルに使う方法

外部サービスとの通信や他のプラグインの関数を、テーマやプラグインのあちこちから直接呼んでいると、仕様が変わったときに修正箇所が散らばり、エラー処理も場所ごとにばらつきます。Facade(ファサード)パターンは、その複雑な手順を1つのクラスにまとめ、使う側には「何がしたいか」だけを表す短いメソッドを見せる設計です。この記事では、WordPress の HTTP API とキャッシュをまとめる例と、他のプラグインへの依存をまとめる例を通して、Facade の作り方と、作りすぎないための判断基準を説明します。

Facade パターンとは

Facade は「正面の窓口」という意味です。裏側では複数の関数やクラスが連携していても、使う側は窓口の1メソッドを呼ぶだけで済むようにします。

たとえば外部の顧客管理サービスから顧客情報を取ってくる処理は、実際には次の手順の組み合わせです。

  1. 認証ヘッダーを付けてリクエストを送る
  2. 通信エラーと HTTP ステータスを確認する
  3. JSON を解析し、想定どおりの形かを確かめる
  4. 必要な項目だけを取り出して整える
  5. 何度も問い合わせないようにキャッシュする

これを呼び出し元ごとに書くと、どこかで手順が抜けます。Facade にまとめれば、呼び出し側は $crm->get_contact( 42 ) と書くだけになり、手順の抜けや重複がなくなります。

Laravel の「Facade」とは別物

PHP では、Laravel の「Facade」(Cache::get() のように静的メソッドで呼ぶ仕組み)を思い浮かべる方も多いかもしれません。あれは裏にあるサービスへ静的呼び出しを中継する仕組みで、デザインパターンとしての Facade とは目的が異なります。この記事で扱うのは後者、「複雑な手順を単純な窓口にまとめる」という意味の Facade です。WordPress で Laravel 風の静的中継を自作すると、依存が見えにくくなりテストもしにくくなるため、まずは普通のクラスとして作ることをおすすめします。

例1:HTTP API とキャッシュをまとめる

WordPress には wp_remote_get() などの HTTP API と、一時的なデータを保存する Transients API があります。どちらも便利ですが、エラー処理を含めると呼び出し側のコードが長くなります。これを1つのクラスにまとめます。

コードは子テーマの functions.php ではなく、専用のプラグインか mu-plugin に置いてください。テーマを変えても動き続けるようにするためです。

<?php
namespace MyPlugin;

final class CrmClient {
	private const CACHE_TTL = 10 * MINUTE_IN_SECONDS;

	private string $base_url;
	private string $api_key;

	public function __construct( string $base_url, string $api_key ) {
		$this->base_url = untrailingslashit( $base_url );
		$this->api_key  = $api_key;
	}

	/**
	 * 顧客情報を取得する。
	 *
	 * @return array{id:int,name:string,email:string}|\WP_Error
	 */
	public function get_contact( int $id ) {
		$cache_key = 'myplugin_contact_' . $id;
		$cached    = get_transient( $cache_key );
		if ( is_array( $cached ) ) {
			return $cached;
		}

		$response = wp_remote_get(
			$this->base_url . '/contacts/' . $id,
			array(
				'timeout' => 10,
				'headers' => array(
					'Authorization' => 'Bearer ' . $this->api_key,
					'Accept'        => 'application/json',
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			return $response; // 通信そのものの失敗
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		if ( 200 !== $code ) {
			return new \WP_Error( 'myplugin_crm_http', 'CRM API error', array( 'status' => $code ) );
		}

		$data = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( ! is_array( $data ) || ! isset( $data['id'] ) ) {
			return new \WP_Error( 'myplugin_crm_format', 'Unexpected CRM response' );
		}

		$contact = array(
			'id'    => (int) $data['id'],
			'name'  => sanitize_text_field( $data['name'] ?? '' ),
			'email' => sanitize_email( $data['email'] ?? '' ),
		);

		set_transient( $cache_key, $contact, self::CACHE_TTL );
		return $contact;
	}
}

設計のポイントは3つあります。

  • 戻り値の形を決める:成功なら決まった形の配列、失敗なら WP_Error を返します。外部 API がどんな JSON を返しても、呼び出し側が受け取る形は変わりません。外部サービスの仕様変更は、このクラスの中だけを直せば吸収できます
  • 入ってきたデータを整える:外部から来た値は信頼せず、sanitize_text_field() などで整えてから返します。表示するときには、さらに出力先に合わせてエスケープします
  • 失敗はキャッシュしない:エラー時に Transient を保存すると、障害が直ったあとも一定時間エラー状態が続いてしまいます

API キーはコードに直接書かず、wp-config.php に定数として定義するか、環境変数から読み込みます。定数名や値は例であり、実際のキーをリポジトリに含めないようにしてください。

<?php
// wp-config.php 側: define( 'MYPLUGIN_CRM_API_KEY', 'your-api-key' );

add_action( 'plugins_loaded', function () {
	if ( ! defined( 'MYPLUGIN_CRM_API_KEY' ) ) {
		return; // 設定がなければ機能ごと無効にする
	}
	$crm = new MyPlugin\CrmClient( 'https://api.example.com/v1', MYPLUGIN_CRM_API_KEY );

	add_shortcode( 'crm_contact_name', function ( $atts ) use ( $crm ) {
		$atts    = shortcode_atts( array( 'id' => 0 ), $atts );
		$contact = $crm->get_contact( absint( $atts['id'] ) );
		if ( is_wp_error( $contact ) ) {
			return ''; // 訪問者にはエラー内容を見せない
		}
		return esc_html( $contact['name'] );
	} );
} );

呼び出し側は「ID を渡して名前を表示する」ことだけを書いており、通信やキャッシュの事情を知りません。これが Facade の効果です。

例2:他のプラグインへの依存をまとめる

テーマが ACF(Advanced Custom Fields)などのプラグインの関数を直接呼んでいると、そのプラグインを停止しただけで、関数が存在しないという致命的エラーが出ます。プラグインの関数をテンプレートのあちこちで呼ぶのではなく、窓口を1つ用意して、存在確認をそこに集めます。

<?php
namespace MyTheme;

final class Fields {
	public static function text( string $name, $post_id = false ): string {
		if ( ! function_exists( 'get_field' ) ) {
			return ''; // プラグインが無効でもサイトを止めない
		}
		$value = get_field( $name, $post_id );
		return is_string( $value ) ? $value : '';
	}
}

// テンプレート側
// echo esc_html( \MyTheme\Fields::text( 'subtitle' ) );

将来、別のカスタムフィールドの仕組みに移行することになっても、このクラスの中身を書き換えるだけで済みます。ここでは使い勝手を優先して静的メソッドにしていますが、単体テストで差し替えたい場合は、例1のようにインスタンスを渡す形にするとよいでしょう。

Facade を作りすぎないための判断基準

Facade は便利ですが、何にでも被せると、薄いだけのクラスが増えて読みにくくなります。次のどれかに当てはまるときに作ると効果があります。

  • 同じ手順(認証、エラー処理、キャッシュなど)を3か所以上で書いている
  • 外部サービスや他のプラグインなど、自分で管理できない仕様に依存している
  • 呼び出し側に知らせたくない複雑さ(再試行、形式の変換など)がある

逆に、get_option() を1回呼ぶだけの処理を包むような、手順が単純なものには不要です。また、Facade が多くの機能を抱え込んで巨大になってきたら、「顧客」「注文」のように関心ごとに分けるサインです。

Facade は「依存をまとめる窓口」で、依存を「どう渡すか」は別の問題です。例1では Facade のインスタンスを起動処理で作ってショートコードに渡しています。依存の渡し方については、Service Locator と DI を比較した別記事で扱っています。

導入時の確認

既存のコードを Facade に置き換えるときは、バックアップを取り、ステージング環境で次の点を確かめてから本番に反映します。

  • 外部 API が止まった・遅いときに、ページ全体が止まらず空表示などで済むか(timeout の値も確認する)
  • 依存先のプラグインを停止しても致命的エラーにならないか
  • キャッシュの有効期限が、データの更新頻度に合っているか

なお、WordPress の HTTP API や Transients API は長く使われている安定した仕組みですが、外部サービス側の認証方式や API のバージョンは変わります(2026年10月現在)。仕様変更の告知は、利用しているサービスの開発者向けドキュメントで確認してください。

まとめ

  • Facade は複雑な手順を1つのクラスにまとめ、使う側には目的を表す短いメソッドだけを見せる設計
  • Laravel の静的な Facade とは別物。WordPress ではまず普通のクラスとして作る
  • HTTP API の例では、戻り値の形を固定し、外部データを整え、失敗はキャッシュしない
  • 他のプラグインの関数は窓口に集めて function_exists() で確認し、停止時もサイトを止めない
  • 同じ手順の繰り返しや管理できない依存があるときに作り、単純な処理には被せない

お気軽にご相談ください

お見積りへ お問い合わせへ
▲