WordPressのデータベースに新しいテーブルを!カスタムテーブルについて

WordPressでEC機能や予約機能と言った複雑な仕組みを新規で構築する場合、標準の構造では中々難しい事があるでしょう。データを保存する表(テーブル)を別に用意する事でその問題が少し解決しやすくなるケースは多いです。この記事ではそんな新規にテーブルを作る仕組みや基礎について解説します。

WordPressの標準テーブル構造を見てみよう

テーブル一覧 – WordPress Codex
ルミェール

上のテーブル一覧を見ながら実際のデータベースを開いて確認してみよう。
wp_postsテーブルが一番メインで使われているって感じがするよね。

投稿や固定ページの記事データ以外にメニューやカスタマイズ情報の一部とかを記録している汎用的なテーブルみたいな使われ方をしてる印象を持つかも?
wp_optionsテーブルも情報を保持してるけど、プラグインとかサイトの全体的な情報しか記録していないような気もするよ。

ルミェール(アイコン01)
テネーブル

そうですね。カスタム投稿タイプを追加した場合は、通常であれば標準のテーブルであるwp_postsテーブルのpost_typeカラムにカスタム投稿のslug名で保存されます。また、リビジョンや下書きと言った公開されていないデータもwp_postsテーブルに保存されますので、wp_postsテーブルは非常に大きくデータを保持しますね。

テネーブル(アイコン01)

新規でテーブルを作る意味など

ルミェール

データベースを見て思ったかもしれないけど、複雑なシステムを作る場合ってそう言った多くのデータに影響を与えちゃいそうだよね。
場合によっては、運営しているWebサイトに深刻な不具合を作っちゃう原因にもなっちゃいそうって感じなかったかな…?

ルミェール(アイコン04)
テネーブル

その問題を解決する為にも、新規にテーブルを用意すると言うのが大きな理由の1つですね。標準のテーブルとは別にデータを読み書きする事で、標準のデータを守る、またサイトのパフォーマンスを大きく低下させない事が可能です。
それに、新しく追加したい機能の用途に合わせてテーブル設計を行う事で、それに合わせたシステムを構築しやすくするのも大きな理由でしょう。

テネーブル(アイコン01)

新規にテーブルを用意するのはプラグインで

テネーブル

カスタムテーブルは、標準で存在するテーブルと違ってプラグインで新規に生成するのが基本です。場合によってはテーマで用意される場合もありますが、推奨はされていないようですね。

テネーブル(アイコン03)

テーブルを生成するタイミング、削除するタイミング

ルミェール

プラグインで新規にテーブルを作るから、プラグインが有効になった時にテーブルを生成するのが基本だよ。
プラグインが削除された時にはテーブルは使わなくなるから削除する感じだね!

ルミェール(アイコン03)
テネーブル

メンテナンス時やアップデートなどの場合によって、プラグインを停止する事があります。なので、停止時にはテーブルは削除してはいけません。そのままの状態にしておく必要があります。
良く間違って停止時にも削除するように実装されているケースがありますが、それが理由でアップデートするとデータが消えたとか言う問題に繋がっています。

テネーブル(アイコン01)

有効時と削除時の仕組みについて

ルミェール

プラグインを有効にした時に処理を行うようにするにはどうすれば良いのかな?

ルミェール(アイコン05)
テネーブル

プラグインが有効化された際に実行する関数を登録する為の仕組みとしてregister_activation_hook関数が存在しています。register_activation_hook関数で、テーブルを新規生成する処理を実行させる関数を設定し、プラグインが有効化された際に実行させる形になります。

テネーブル(アイコン01)
関数リファレンス/register activation hook
テネーブル

プラグインが削除(アンインストール)される際に実行する関数を登録する為の仕組みとしてregister_uninstall_hook関数が存在しています。register_uninstall_hook関数で、テーブルを削除する処理を実行させる関数を設定し、プラグインが削除される際に実行させる形になります。

テネーブル(アイコン01)

データベースを操作する wpdbクラス

テネーブル

WordPressでデータベース操作を行う為に用意された仕組みとしてwpdbクラスが存在します。

テネーブル(アイコン01)
関数リファレンス/wpdb Class
ルミェール

カスタムテーブルもデータベースの中のテーブルの1つなので、wpdbクラスで操作するって事だね!

ルミェール(アイコン01)

テーブルを追加してみよう

ルミェール

追加するテーブルの構造は、wp_optionsテーブルと同じ構造のwp_customtable_sampleと言うテーブル名で例にしたよ。

wp_の部分は、WordPressをインストールする際に設定したデータベースのプレフィックスとなるので、インストール設定によってwp_の部分は異なる場合もあるよ。その場合は置き換えて確認してね!

ルミェール(アイコン01)
テネーブル

動作する全体のサンプルコードファイルは、記事の最後からダウンロードできるようにしています。
動作確認の場合は、そちらをどうぞ。

テネーブル(アイコン01)
// プラグインが有効化された場合に_activation関数を実行するように登録
register_activation_hook( __FILE__, '_activation' );

// _activation関数
function _activation() {
	// プラグインの現在のバージョンを設定
	$_this_version = '0.1.0';
	// プラグインのインストールされているバージョンを取得
	$_installed_version = get_option( 'customtable_sample_version', '' );
	// インストールされているバージョンと現在のバージョンを比較し、異なっている場合に処理を行う
	if ( $_installed_version !== $_this_version ) {
		// グローバル変数 wpdbクラスを宣言
		global $wpdb;
		// テーブル名の設定
		$_table_name = $wpdb->prefix . 'customtable_sample';
		// 文字コードの設定
		$_collate = '';
		if ( $wpdb->has_cap( 'collation' ) ) {
			$_collate = $wpdb->get_charset_collate();
		}
		// クエリの設定
		$_query = "CREATE TABLE $_table_name (
			option_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
			option_name varchar(191) NOT NULL DEFAULT '',
			option_value longtext NOT NULL,
			PRIMARY KEY (option_id),
			UNIQUE KEY option_name (option_name)
		) $_collate;";
		// dbDelta関数を使用する為のPHPファイルをロード
		require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
		// データベースを操作
		dbDelta( $_query );
		// プラグインのインストールされているバージョン情報の更新
		update_option( 'customtable_sample_version', $_this_version );
	}
}
テネーブル

順番に解説していきます。

まず、register_activation_hook関数により、_activation関数をプラグイン有効時に実行されるように登録しています。

_activation関数では、プラグインの現在インストールされているバージョンと現在のバージョンを最初に比較しています。

テネーブル(アイコン01)
ルミェール

何故、比較を行うんだろうね?考えてみよう!

ルミェール(アイコン05)
テネーブル

まず、比較する大きな理由として停止した後に再度有効した場合の動作対応です。
必ず、違うバージョン時だけに有効化された際の処理が実行されるとは限りません。

停止後に同じバージョンで有効化される場合もあります。しかし、同一のバージョン時であればプラグインに変更は無いのですから、テーブル構造は変更する必要はありません。その為、テーブル操作処理は行わないようにする必要があります。

テネーブル(アイコン03)
ルミェール

データを守る為にも、操作する必要が無い場合は必要ない処理を実行しないようにするのも大事なんだね!

ルミェール(アイコン05)
テネーブル

処理の解説に戻ります。


比較を行った後、データベース操作が必要な場合にはデータベース操作をする為にwpdbクラスを関数内で使用する為にglobalで宣言しています。

テーブル名であるwp_customtable_sampleを設定する際に$wpdb->prefixをテーブル名の先頭に付与してcustomtable_sampleと記述していますが、これは$wpdb->prefixがWordPressをインストールする際に設定したデータベースのプレフィックスとなる為です。

テネーブル(アイコン01)
ルミェール

プレフィックスは、デフォルトでインストールしている場合はwp_の部分。違う場合は、確認の時に置き換えて確認してもらった部分だよ!

なので、wp_customtable_sampleと言うテーブル名からwp_を抜いた部分と$wpdb->prefixで変数を設定してるんだね!

ルミェール(アイコン01)
テネーブル

次に文字コードの設定です。

WordPressではバージョンやインストール時の言語や設定方法によってデータベースの文字コードが異なる場合がありますので、wpdbクラスを用いて設定されている文字コードを取得しています。

テネーブル(アイコン01)
ルミェール

ちょっと前まではutf8だったみたいだけど、現在のWordPress日本語版だと、大体CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;が定義されているのかな?
collateが設定されていない場合は文字コードは省略出来るから、ここは文字コードの設定にはこの書き方をするって覚えておくだけで良いかも。

ルミェール(アイコン01)
テネーブル

次に行うのはクエリの設定ですね。

ここのクエリでは制約されたSQL文を設定します。

テネーブル(アイコン01)
ルミェール

制約されたSQL文?

ルミェール(アイコン05)
テネーブル

はい。動作確認を行う際にクエリ文のコードを1行にまとめたりしてみてください。

テネーブル(アイコン03)
ルミェール

エラーが出るか、正常にテーブル生成が行われないようになっちゃうね。

ルミェール(アイコン04)
テネーブル

はい。実はデータベース操作のクエリ実行を行うdbDelta関数はクエリをそのまま実行する関数ではありません。

テネーブル(アイコン05)

dbDelta関数は現在のテーブル構造を走査し、作成予定のテーブル構造と比較します。そして、必要に応じてテーブルを追加・変更してくれるので、更新にはとても便利な関数です

注意dbDelta関数はやや融通がききません。

WordPress Codex – テーブルの作成または更新
テネーブル

色々な制約が存在しますので、制約については上記のサイトでご確認ください。
テスト時には制約で動作エラーにならないかをテーブルの生成だけではなく構造の変更も正しく試してみる必要があります。

テネーブル(アイコン01)
ルミェール

ちょっと難しいけど、テーブル内のデータを安全に更新する為には必要な事だよね!
プラグインの有効時のテーブル生成やアップデート時のテーブル構造の更新を正しく行ってくれるので、きちんとクエリを書くようにしよう!

ルミェール(アイコン03)
テネーブル

最後の処理では、インストールされたバージョンの値をwp_optionsテーブルにcustomtable_sample_versionと言う名前で保存します。

再度有効化した際は、前述の比較処理時にこの値を基に判定されるようになります!

テネーブル(アイコン01)

テーブルを削除してみよう

テネーブル

削除の際のコード例は下記の通りです。

テネーブル(アイコン01)
// プラグインが削除される場合に_uninstall関数を実行するように登録
register_uninstall_hook( __FILE__, '_uninstall' );

// _uninstall関数
function _uninstall() {
	global $wpdb;
	$_table_name = $wpdb->prefix . 'customtable_sample';
	// SQLクエリの設定
	$_query = sprintf(
		'DROP TABLE IF EXISTS %1$s',
		$_table_name
	);
	// SQLクエリの実行
	$wpdb->query( $_query );
	// バージョン情報の削除
	delete_option( 'customtable_sample_version' );
}
テネーブル

生成処理とほぼ同様ですが、テーブルの生成や更新でない限りは現在のテーブル構造を走査する必要が無いですので、クエリの実行ではdbDelta関数を使用しません。

クエリの実行にはwpdbクラスのquery関数を使用してSQLクエリを実行します。

SQLクエリはDROP文で、テーブルの削除を行うSQLを指定してあげます。

クエリ実行後に、比較をする為のバージョン情報であるcustomtable_sample_versionの値を削除します。

テネーブル(アイコン01)
ルミェール

DROPなどと言ったSQL構文についてはデータベースの勉強が必要なので、ここでは割愛してるよ!

query関数の戻り値などでテーブル削除が失敗した場合にはアンインストールを中断させるとか作るとより安全になるのかな?

それもこの処理を基に作ってみると良いかも!

ルミェール(アイコン01)

サンプルコードのダウンロード

次回予告!

テネーブル

長くなりましたので、データの書き換えについては、次回の記事で説明いたします。

プラグインを有効化してみたり、削除してみたりしてテーブルが正しく生成や削除される事をまずは確認してみてください!

テネーブル(アイコン01)

次回記事を公開しました。

今回の記事を振り返って

記事の難易度について

回答いただき、ありがとうございました!
回答結果は、次回の記事制作の参考にさせていただきます。