WordPress のプラグイン作成 — $wpdb 編 その1 –

posted at 2014/01/15 by denpoya@コワーキングスペース町田

今回は、WordPressにデータベースのテーブルを作成してもらうことが主なテーマです。その時使う$wpdbやWordPressの関数dbDelta()update_option()get_option()の使い方にも触れておきます。

さらに、テーブルの数が複数になった時にforeach文で回すための工夫もしておくことにしましょう。バージョンが上がって、テーブルを増やさなければならなくなった時でも簡単に対処できるようになるでしょう。

WordPressにテーブルを作ってもらう

WordPressにテーブルを作ってもらうのはいつどの場面でしょうか?プラグインを「有効化」した時の1回だけでよいですか?

プラグインの新しいバージョンを作成する際、別のテーブル構造が必要になったらアップグレード用関数を作成することになるでしょう。フィールド属性を変更するだけでなく、新しいテーブルを追加することもあるかもしれません。そのようなことを念頭に置いてコーディングしていきます。

WordPressが通常使っているテーブルではなく、私たちのプラグイン専用のテーブルを「有効化」のタイミングで作成することにします。本当に専用のテーブルが必要かどうかは検討すべきでしょう。このプラグインには必要である、という設定でお話を進めていきます。

WordPressをインストールするときにテーブル名のプレフィックス(接頭辞)を指定できました。私たちのテーブル名にも同じプレフィックスをつける必要があるでしょう。デフォルトは「wp_」ですがインストールする人によって異なるプレフィックスになっているかもしれません。

まず、第1段階として

  1. 「テーブル名に付けられているプレフィックスを私たちのテーブルにも付ける」
  2. 「私たちのテーブルが既に存在しているかを確認しておく」

この2つからコーディングしていきます。必要なものは$wpdbです。

WordPressのデータベース操作用のクラス関数 wpdb

WordPressにはデータベース操作用のクラス関数が用意されています。

しかしそれを直接使うのではなく、WordPressにはデータベースと対話するために設定されたクラスをインスタンス化した$wpdbというグローバル変数が用意されているのでこのグローバル変数$wpdbを使うのが良いようです。

そのためにはグローバル宣言をします。

global $wpdb;

$wpdbの持つメソッドのうちよく使うのが、get_var('query',column_offset,row_offset)get_results('query', output_type)そしてquery('query')といったSQL文を発行するメソッド、その時にSQLインジェクション攻撃からクエリを保護するために使うprepare( 'query'[, value_parameter, value_parameter ... ] )メソッドとSQLエスケープのためのescape($user_entered_data_string)ではないでしょうか。説明は関数を使う時にしましょう。

テーブル名にプレフィックスをつける

テーブル名に付けられているプレフィックスは、

$wpdb->prefix;

という変数に格納されています。例えば、「example_table」という名前にプレフィックスをつけてテーブル名にしたいならば、

$table_name = $wpdb->prefix . 'example_table';

のようにします。

テーブルが既に存在しているかどうかを確認する

テーブルが既に存在しているかどうかを確認するのには、

$is_db_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name));

のように「SHOW TABLES LIKE テーブル名」クエリを発行します。prepareメソッドによって、「%s」には第2引き数の文字列がその箇所に挿入されます(2つ目の「%s」があったら第3引き数の値が挿入されます)。

get_varメソッドはデータベースから変数を一つ返します。変数は一つしか返ってきませんが、クエリの結果はあとから使うことができます。結果にマッチするものがない場合、NULL が返されます(関数リファレンス/wpdb Classを参照、以下同様)。

テーブルがないときには作成するようにします。

CDPluginクラスのactivate()メソッドを実装

前回作成したCDPluginクラスのactivate()メソッドを編集します。既に述べたことから下のようになるでしょう。

  • CDPlugin.php
<?php
class CDPlugin {
    function __construct() {
    }

    function activate() {
        $table_name = $wpdb->prefix . 'main_master';
        $is_db_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name));

        if ($is_db_exists == $table_name) {
            return;
        }

        // ここでSQLクエリの実行する

    }
}
SQLクエリの実行

直接SQLクエリを実行($wpdb->query($sql))してもよいのですが、wp-admin/include/upgrade.phpで定義されているdbDelta関数を使う方がよいようです。dbDelta関数現在のテーブル構造を走査し、作成予定のテーブルと比較、必要に応じてテーブルを追加・修正します。更新にはとても便利な関数です(Creating Tables with Plugins(日本語版)を参照)。ただし、使用するには少しばかり癖があるようです。

参考にさせていただいた上記リンクとその最新版であるCreating Tables with Plugins(最新英語版)のページを見ると日本語版よりも癖がもう一つ増えて4つ項目が並んでいます。

  1. 1行につき1つのフィールドを定義してください。 〔訳注:一つの行に複数のフィールド定義を書くことは出来ません。さもなくば ALTER TABLEが正しく実行されず、プラグインのバージョンアップに失敗します。 〕
  2. PRIMARY KEYと主キーの定義の間には二つのスペースが必要です。〔訳注:原文 "You have to have two spaces between the words PRIMARY KEY and the definition of your primary key."〕
  3. INDEXという言葉ではなく、KEYという言葉を使う必要があります。
  4. フィールド名をアポストロフィやバッククォートで囲ってはいけません。〔原文:"You must not use any apostrophes or backticks around field names. "〕

このことに気を付けてSQL文を次のように作ります。dbDelta関数を使うには、wp-admin/include/upgrade.phpをインクルードする必要があります。

発行するSQL文を次のようにし、dbDelta関数を実行させます。

$sql = "CREATE TABLE " . $table_name . " (
     id mediumint(9) NOT NULL AUTO_INCREMENT,
     name tinytext NOT NULL,
     url VARCHAR(55) NOT NULL,
     created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '作成日',
     modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '修正日',
     UNIQUE KEY id (id)
     );";

require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
バージョン管理

合わせて私たちのテーブルのバージョン管理の仕組みも用意します。プラグインを一度「停止」してもらい、再び「有効化」することで今編集しているactivate()が実行されるのでデータベースに変更があった時だけdbDelta関数を呼び出しテーブルが追加更新されるようにするためです。

私たちのテーブルのバージョン番号は、WordPressの持つ設定を保存するテーブルに登録させてもらうことにしましょう。

独立した、名前の付いたデータ (「設定」) を WordPress データベースに保存し利用することができます。設定値の取り得る形式は、文字列 (string)、配列 (array)、もしくは PHP オブジェクトです (オブジェクトは、保管時は「シリアライズ」もしくは文字列に変換され、読出時にオブジェクトに戻されます)。オプション名は文字列で、他にない唯一のものでなければなりません。なぜなら、WordPress や他のプラグインと衝突しないためです(プラグインの作成を参照)。

使う関数は下の2つ、add_option()を利用する必要はありません。

update_option( $option, $new_value )

optionsデータベーステーブルから、指定したオプションの設定値を更新または作成します。オプションの設定値はインサートされる前に$wpdb->escapeでエスケープされます。また、指定された
オプション名が存在していないときにはadd_option( $option, $new_value )が実行されるので、add_option( $option, $new_value )の代りに使うことができます。

$option_name
    (文字列) (必須) 更新したい設定の名前。有効なデフォルトオプション値は、Option Referenceに一覧があります。

        初期値: なし

$newvalue

    (混合) (必須) 設定の新しい値。この値には、整数、文字列、配列やオブジェクトを取ることができます。

        初期値: なし
get_option( $show, $default )

optionsデータベーステーブルから、指定したオプションの値を取得する安全な方法です。希望するオプションが存在しない場合は、値が関連付けされず、FALSE が返されます。

 $show
    (文字列) (必須) 取得するオプションの名前。有効なデフォルトオプション値は、Option Referenceに一覧があります。

        初期値: なし

$default
    (混合) (オプション) 値が返されない(データベースにオプションが存在しない)場合のデフォルト値。

        初期値: false
完成したメソッド

SQL文は、別メソッドからの戻り値となるようにしました。

<?php
class CDPlugin {
    // 現在のテーブルのバージョン。
    // 数値を上げるとテーブルを更新する。
    // (テーブルが存在していて)上げなければ何もしない。
    private $version = 0.1;

    function __construct() {
    }

    public function activate() {
        $db_version = get_option('cdq_db_version', 0);

        $table_name = $wpdb->prefix . 'main_master';
        $is_db_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name));

        if ($is_db_exists == $table_name && $db_version >= $this->version) {
            return;
        }

        require_once ABSPATH . "wp-admin/includes/upgrade.php";
        dbDelta($this->$getSql($table_name));

        update_option("cdp_db_version", $this->version);
    }

    private function getSql($table_name) {
        return <<<EOS
CREATE TABLE  $table_name (
     id mediumint(9) NOT NULL AUTO_INCREMENT,
     name tinytext NOT NULL,
     url VARCHAR(55) NOT NULL,
     created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '作成日',
     modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '修正日',
     UNIQUE KEY id (id)
);
EOS;
    }

}

複数のテーブルに対応

テーブルが1つだけなら良いのですが、複数のテーブルを使うプラグインでは、先のactivate()がテーブルの数だけ必要になります。そんな時はforeachで回したくなります。そのための工夫をしてみましょう。

テーブル名の識別とSQL文

先ず、テーブルを識別できるようにします。新しいテーブルを追加するときにここに追加記入します。

private $tables = array(
    "master_1",
    "detail_1",
    "master_2",
    "detail_2",
);

そしてもう一つ新しいテーブルを追加するときに記述しなければならないのはSQL文です。この二つを記述するだけで新しいテーブルを追加できるようにします。

それには、SQL文を返すメソッド名に規則を作ります。すなわちメソッド名に、上で記入したテーブルの識別を使って、例えば、こんな感じにします。

private function master_1Sql($table_name) {return "CREATE TABLE $table_name ・・・"}
private function detail_1Sql($table_name) {return "CREATE TABLE $table_name ・・・"}
private function master_2Sql($table_name) {return "CREATE TABLE $table_name ・・・"}
private function detail_2Sql($table_name) {return "CREATE TABLE $table_name ・・・"}

"テーブルの識別" . "Sql"がメソッド名になっています。新しいテーブルを追加するときに新たに記述するのはこれだけでそれ以外に修正する必要はありません。

プレフィックスをつける

実際にCREATEされるテーブル名(プレフィックスがつく)を作ります。

private function getTableNames() {
    global $wpdb;
    foreach ($this->tables as $name) {
        $this->table_names [$name] = $wpdb->prefix . $name;
    }
    return $table_names;
}
クエリを実行

そして、各テーブルごとにSQL文を発行し、クエリを実行するよう以下のコードをactivate()メソッドに埋め込みます。

require_once ABSPATH . "wp-admin/includes/upgrade.php";

$table_names = $this->getTableNames();
foreach ($table_names as $name => $table_name) {
     $sqlfunc = $name . "Sql";
    dbDelta($this->$sqlfunc ($table_name));
}

今回は、ここまで。

次回は、「$wpdb 編 その2」として、$wpdbの使い方、エラー処理(といっても、エラーメッセージを受け取るわけですが)などに触れたいと思います。

One comment on “WordPressのプラグイン作成 第二回

  1. Pingback: WordPressのプラグインを開発したい人向けのおすすめ記事30選 | NESTonline Blog

コメントを残す