WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 外部 API の呼び出し)

アクションフックを使った例を用いて、WordPress プログラミングを紐解くシリーズ。引き続きまして「WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 外部 API の呼び出し)」です。

今回は LWWS お天気APIを呼び出して、記事公開時に当日のお天気情報をカスタムフィールドに入れるところまで進めてみます。


コンストラクタを使って地域IDを指定する

前回まででできあがった骨組みソースは以下のようなものでした。

プログラムがクラス化され、記事の公開を表すアクションフック「publish_post」に Otenki クラスのメソッド「prepare」を定義し、記事公開時に処理が呼び出されるところまでできています。また、テーマ用のお天気出力テンプレートタグ the_otenki も用意しました。

class Otenki {

    /**
     * カスタムフィールド・メタキー.
     */
    const META_KEY = 'otenki';

    /**
     * APIバージョン情報(V1/JSON).
     */
    const LWWS_V1_JSON = 'LWWS_V1_JSON';

    /**
     * 記事公開フックで天気情報を取得をする.
     *
     * @param $post_ID
     * @param $post
     * @return none
     */
    function prepare($post_ID, $post) {
        // お天気 API を呼び出し
        // Otenki::META_KEY, Otenki::LWWS_V1_JSON を用いて
        // カスタムフィールドに登録
    }

   /**
    * カスタムフィールドに取得したお天気アイコンを出力.
    *
    * @param none
    * @return none
    */
    function output() {
        // Otenki::META_KEY, Otenki::LWWS_V1_JSON を用いて
        // カスタムフィールドを取得しアイコン HTML を出力
    }
}

$otenki = new Otenki();
add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);

/**
 * お天気情報を出力するテンプレートタグ.
 */
function the_otenki() {
    global $otenki;
 
    $otenki->output();
}

まず、今回利用する外部API(LWWS)の仕様です。

LWWS のお天気APIは、指定された地域IDで RESTのリクエストをなげると、JSON 形式で当日以降のお天気情報が返却されるという動作が行われます。

(例)「福岡県・久留米の天気」を取得する場合
下記URLにアクセスしてJSONデータを取得します。
基本URL + 久留米のID(400040)
http://weather.livedoor.com/forecast/webservice/json/v1?city=400040

ここで指定するお天気を取得する地域ID は使う人によって変わりますので、何かしらの方法で指定する実装が必要です。

いくつかやり方が考えられますが、ここではオブジェクトのインスタンス生成時に地域IDを指定し、コンストラクタからメンバ変数に値を格納することにします。合わせて、後続でつかいそうなお天気情報出力時の CSS クラス名も設定できるようにしました。

class Otenki {
    /* 略 */

    /**
     * LWWS URL 引数(地域別値).
     *
     * @see 
     */
    var $city;

    /**
     * 画像出力時の CSS クラス.
     */
    var $css_class;

    /**
     * コンストラクタ・プラグインデフォルト値設定.
     */
    function __construct(
        $city = '016010', $css_class = 'otenki') {
        // LWWS URL 引数(地域別値)
        $this->city = $city;
        // 画像出力時の CSS クラス
        $this->css_class = $css_class;
    }

    /* 略 */
}

$otenki = new Otenki();
add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);

function __cunstruct はインスタンス生成時に PHP によって呼び出される、コンストラクタと呼ばれる特殊なメソッドです。

次のようにクラスが new されたとき、__cunstruct メソッドは自動的にコールされ処理をそこに移します。

$otenki = new Otenki();

コンストラクタの引数には、オブジェクトの new をするときに指定した引数値が渡されます。

上記の new Otenki() の場合は引数の指定がないため、コンストラクタ(__construct)のデフォルト引き数値である、’016010′ と ‘otenki’ がメンバ変数 $city(地域ID) と $css_class (CSSクラス名)に格納され保持されます。

/**
 * コンストラクタ・プラグインデフォルト値設定.
 */
function __construct(
    $city = '016010', $css_class = 'otenki') {
    // LWWS URL 引数(地域別値)
    $this->city = $city;
    // 画像出力時の CSS クラス
    $this->css_class = $css_class;
}

たとえば、久留米を地域IDに指定したい場合は、LWWS オブジェクトの生成を次のように書き換えます。

$otenki = new Otenki('400040');

さて、ここから少し余談です。オブジェクト指向、いかがですか。

このようなクラスとインスタンスの性質を利用すると、複数の地域の処理も同一のクラス定義で実行できます。

$sapporo = new Otenki();
$kurume = new Otenki('400040');
add_action(
    'publish_post', array($sapporo, 'prepare'), 10, 2);
add_action(
    'publish_post', array($kurume, 'prepare'), 10, 2);

/**
 * お天気情報を出力するテンプレートタグ.
 */
function the_otenki() {
    global $sapporo;
    global $kurume;
 
    $sapporo->output();
    $kurume->output();
}

処理シーケンスのイメージ的には次のようになります。 初期化 otenki.php からの <<new>> 部分で 2つのインスタンスが生成されていること注目してください。

otenki_seq2

単純な上位部への処理追加でいくつでも対象地域を増やせるようになっています。 🙂

ただし実装上、これからまだ考えるべきことが残っていることもシーケンスから見てとれるかもしれません。

DB へのデータ保存時のキー設計(カスタムフィールドを地域別にする?)や、ソースを修正することなく設定で動的に地域の増減を行いたい場合は、生成したインスタンスを管理する実装などが該当します。

これらの処置はそれほど難しくないものの、今回のサンプルソースとしては若干意味がぼやけてしまいそうなので、つくり込みは省略し他で解説することにしましょう。

オブジェクト指向の基本的な使い方のひとつを紹介したく、若干脱線してみました。普段オブジェクト指向が分からないと感じている方は、まずはシーケンスの確認を。異なる値を持った対象が new によってつくりだされ、それぞれを区別して処理を呼べる感覚が分かれば出来たも同然です。

ではここからは単純に、コンストラクタで地域IDが指定できるようになったことに注目していきます。 コンストラクタを用いることで処理に必要な変数の初期化のタイミングを得ることができます。

以上で地域IDの可変化を行う準備が整いました。まだ、ソースコードファイルを修正しないとユーザが地域IDが設定できませんが、これは後の管理画面までのお楽しみとしておきましょう。 🙂

外部 API からのお天気情報取得

外部 API を呼び出しお天気情報を取得します。

コンストラクタで設定した、地域ID($city) とアクションフックから渡された投稿記事情報 $post_ID, $post を元に、記事の公開時に WordPress によって呼び出される prepare メソッドを実装します。

/**
 * 記事公開フックで天気情報を取得をする.
 *
 * @param $post_ID
 * @param $post
 * @return none
 */
function prepare($post_ID, $post) {
    // "今日"のポストの時だけ天気を取得
    if(date("Y/m/d", strtotime($post->post_date))
        != date("Y/m/d")) return;
    // LWWS(V1/JSON)より天気情報を取得
    $json = wp_remote_get(
        'http://weather.livedoor.com/forecast/webservice/json/v1?'
        . http_build_query(array('city' => $this->city)));
    // 取得できなかった場合は次回の公開に期待
    if(is_wp_error($json)) return;
    // リクエストから JSON データ取得し PHP 連想配列に変換
    $otenki = json_decode($json['body'], true);
    // 正常な JSON 形式ではなければ次回の公開に期待
    if(!$this->isOtenki($otenki)) return;
    // カスタムフィールドにバージョン情報付きで格納
    update_post_meta(
        $post_ID
        , Otenki::META_KEY
        , array(Otenki::LWWS_V1_JSON => $otenki));
}

ソースを上から順に追っていきます。

まず最初に、公開された記事が当日かを判定します。これはお天気APIが”本日”の情報を返却するためです。過去日の記事の公開に対して取得を行うと”本日”の情報で上書きされてしまうため、これを抑止します。

// "今日"のポストの時だけ天気を取得
if(date("Y/m/d", strtotime($post->post_date))
    != date("Y/m/d")) return;

公開対象記事の情報を知るにはアクションフックから渡されてきた $post の情報を用いています。(var_dump($post) すると分かりやすいでしょう)

次にいよいよお天気 API に対して REST リクエストをなげます。 WordPress にはこのような http での外部情報取得のために wp_remote_get 関数が用意されてます。

実はこの処理に、古いソースでは PHP 標準関数の file_get_content を忘れて使ってしまっていたのでした。(まがりんさんご指摘ありがとうございます)

WordPress に準備された wp_remote_get 関数は、条件により適切な PHP の通信関数を選択がされ、詳細な通信ステータスの取得ができる機能を持っています。また、内部処理にフックがあちこちに仕掛けられており外部からの制御も可能ですので、WordPress で外部通信を行う場合はこちらを用いましょう。

使い方は簡単で、引数に API などのエンドポイント URL を渡し、is_wp_error で通信エラーハンドリングします。

URL に、コンストラクタで設定したメンバ変数($this->city)から地域IDを引数として付与していることにも注目してください。

// LWWS(V1/JSON)より天気情報を取得
$json = wp_remote_get(
    'http://weather.livedoor.com/forecast/webservice/json/v1?'
    . http_build_query(array('city' => $this->city)));
// 取得できなかった場合は次回の公開に期待
if(is_wp_error($json)) return;

wp_remote_get で取得したデータ本体は、$json[‘body’] に格納されます。

取得した JSON 形式を PHP の連想配列に変換し、正しいデータ形式かの正当性確認(バリデーション)を行います。

// リクエストから JSON データ取得し PHP 連想配列に変換
$otenki = json_decode($json['body'], true);
// 正常な JSON 形式ではなければ次回の公開に期待
if(!$this->isOtenki($otenki)) return;

外部 API から取得したデータのバリデーションチェックは非常に重要です。期待値が返ってくればよいものの、異常データが戻ってきた場合、特に連想配列で処理している局面で PHP が警告を発砲し不具合になる場合があります。

また、お天気の HTML の出力ではここで登録されたカスタムフィールド値の取得処理を行いますが、こちらも同様に、ユーザが手で不正な値を登録されたり変更されると不具合になる可能性がありますので、念のためバリデーションしておいたほうがよいでしょう。

このように2つのメソッドで使われる同じバリデーションになりますので、ここでは isOtenki メソッドとして外だしにしています。

連想配列を、LWWS の仕様を元に”本日のお天気”部までツリーをたどり、存在するかを確認しています。

/**
 * お天気情報バリデーション.
 *
 * @param unknown $otenki
 * @return boolean
 */
function isOtenki($otenki) {
    if(!(is_array($otenki)
        && isset($otenki['forecasts'])
        && is_array($otenki['forecasts'])
        && isset($otenki['forecasts'][0]['image']))) {
        return false;
    }
    return true;
}

PHP の配列仕様に、string 値に添え字をつけるとその位置の1文字が返ってくるというちょっと悲しい下位互換機能があるため、少ししつこい判定になっています。

バリデーションを抜け正しいお天気情報だと確認できたら、いよいよカスタムフィールドに登録です。

アクションフックからもらえる記事ID($post_ID)と、カスタムフィールド用のキーをもって、カスタムフィールドに連想配列を全て登録します。API のバージョンアップを鑑みて API バージョン情報も独自に付与しています。

// カスタムフィールドにバージョン情報付きで格納
update_post_meta(
    $post_ID
    , Otenki::META_KEY
    , array(Otenki::LWWS_V1_JSON => $otenki));

格納する情報は、お天気出力処理に必要な”今日のお天気”だけでよいのですが、予報の文言なども面白いためそのまま全部入れています。(この辺は趣味です)

というわけで、ようやくAPI の呼び出しと、カスタムフィールドへの登録まで完成しました。 🙂

今回のポイントは、

  • クラス化した処理の、メンバ変数の初期化や設定はコンストラクタで行える。
  • 複数のインスタンスを使い処理を簡略化するオブジェクト指向の技がある。
  • 外部 API への http リクエストには wp_remote_get を使う。
  • 外部 API からの返却値はバリデーションをしっかり行う。

でした。

WordPress と PHP プログラミングテクニックの組み合わせの実例ということで、それぞれ単独でみるよりも面白いんじゃないかと思って書いてみましたが、いかがでしょうか。。

では、また次回。 🙂


目次

本連載はファイルがない状態からソースコードを実装していく方式を採っています。プログラムの構成法も含めて解説していますので、最初から読んでいくとより理解しやすいでしょう。

アクションフックのプラグインをつくる編
最初に、WordPress の拡張において重要なフックの考え方を、サンプルの動作のひとつである「記事公開時の処理追加」の実装を元に解説します。フックはこの後の画面出力、処理の非同期化や管理画面の付与でも使われていきます。
処理のクラス化
WordPress 拡張におけるモジュール設計として、PHP のクラスを用いた方法を紹介します。分かりやすく、他のモジュールとの競合を最小限に抑えられる手法です。
外部 API の呼び出し(この記事)
LWWS などの REST API に対して WordPress からリクエストを送る解説です。また、ユーザによって設定が異なる値がある場合のモジュール構成も合わせて紹介します。
外部 API 呼び出しの非同期化
外的要因などで処理スピードの低下が懸念される場合の処理方法です。WP-Cron を用いたスケジュール化手法を解説しています。
テンプレートファイルからの情報出力
テンプレートファイルに処理を記述し出力する場合に、拡張モジュールと疎結合にする実装法を紹介します。アクションフックの活用例としても有効です。
管理画面をつける
管理画面に設定欄を設けユーザが拡張の機能をコントロールできるようにする手法の解説です。また、本連載中のソースコードに最終的なリファクタリングをかけ、他の開発者にこの拡張の意味を表現するプログラミングテクニックも解説します。
まとめ
本連載の概要とまとめ、そして目次です。

コメントを残す