応用編
💾 データベース操作
WordPressの$wpdbクラスを使ったカスタムテーブルの作成・CRUD操作・マイグレーション方法を学びます。メタデータAPIやトランザクションなど、プロが使う技術も解説します。
💾 $wpdb クラスとは
$wpdbはWordPressがDB操作のために提供するグローバルオブジェクトです。すべてのWordPressのDB操作はこれを通して行います。
標準テーブルが向くケース
- 記事・ページのコンテンツ → wp_posts
- 設定値・キーバリュー → wp_options
- 記事に紐づく追加情報 → wp_postmeta
- ユーザーの追加情報 → wp_usermeta
カスタムテーブルが向くケース
- 複雑なリレーションを持つデータ
- 数万件以上の大量レコード
- ログ・アクセス履歴などの時系列データ
- 複数カラムで検索・集計が必要なデータ
🗄️ カスタムテーブルの作成
dbDelta() はテーブルが存在しなければ作成し、あれば差分のみ更新します。
function mfp_create_tables() {
global $wpdb;
$table = $wpdb->prefix . 'mfp_logs';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned NOT NULL DEFAULT 0,
post_id bigint(20) unsigned NOT NULL DEFAULT 0,
action varchar(100) NOT NULL DEFAULT '',
ip_address varchar(45) NOT NULL DEFAULT '',
created_at datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (id),
KEY user_id (user_id),
KEY created_at (created_at)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
update_option( 'mfp_db_version', '1.0' );
}
register_activation_hook( __FILE__, 'mfp_create_tables' );
⚠️ dbDelta() の書式ルール
- 各カラム定義の前にスペース2つのインデントが必要
PRIMARY KEYの後ろにスペース2つが必要(スペース1つでは動かない)- 文字コードは必ず
$wpdb->get_charset_collate()で取得 - インデックス名を必ず指定する
🔧 CRUD 操作
データの挿入・更新・削除
global $wpdb;
$table = $wpdb->prefix . 'mfp_logs';
// ─ 挿入 ────────────────────────────────────────────────────
$result = $wpdb->insert(
$table,
array(
'user_id' => get_current_user_id(),
'post_id' => get_the_ID(),
'action' => 'view',
'ip_address' => sanitize_text_field( $_SERVER['REMOTE_ADDR'] ?? '' ),
'created_at' => current_time( 'mysql' ),
),
array( '%d', '%d', '%s', '%s', '%s' ) // 各値のデータ型
);
$new_id = $wpdb->insert_id; // 挿入されたID
// ─ 更新 ────────────────────────────────────────────────────
$wpdb->update(
$table,
array( 'action' => 'updated' ), // 更新内容
array( 'id' => 5 ), // WHERE条件
array( '%s' ), // 更新内容の型
array( '%d' ) // WHERE条件の型
);
// ─ 削除 ────────────────────────────────────────────────────
$wpdb->delete( $table, array( 'user_id' => 10 ), array( '%d' ) );
データの取得
// 複数行取得 → オブジェクトの配列
$logs = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $table WHERE user_id = %d ORDER BY created_at DESC LIMIT %d",
get_current_user_id(), 20
)
);
foreach ( $logs as $log ) {
echo esc_html( $log->action );
}
// 1行取得
$log = $wpdb->get_row(
$wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id )
);
// 単一の値(COUNT等)
$count = (int) $wpdb->get_var(
$wpdb->prepare( "SELECT COUNT(*) FROM $table WHERE user_id = %d", $user_id )
);
// エラー確認
if ( $wpdb->last_error ) {
error_log( 'DB Error: ' . $wpdb->last_error );
}
📊 メタデータAPI
記事・ユーザー・タームに追加データを紐付けるには、カスタムテーブルを作らず標準のメタデータAPIを活用できます。
// ─ 投稿メタデータ ────────────────────────────────────────
update_post_meta( $post_id, 'mfp_view_count', 100 ); // 保存
$count = (int) get_post_meta( $post_id, 'mfp_view_count', true ); // 取得
delete_post_meta( $post_id, 'mfp_view_count' ); // 削除
// ─ ユーザーメタデータ ────────────────────────────────────
update_user_meta( $user_id, 'mfp_preferences', array( 'theme' => 'dark' ) );
$prefs = get_user_meta( $user_id, 'mfp_preferences', true );
// ─ タームメタデータ(カテゴリー・タグ等)────────────────
update_term_meta( $term_id, 'mfp_icon', 'star' );
$icon = get_term_meta( $term_id, 'mfp_icon', true );
💡 メタデータAPIとカスタムテーブルの使い分け
数百件以下のデータや、投稿・ユーザーに直接紐づく情報はメタデータAPIが便利です。数万件以上で複数カラムをまたいだ検索・集計が必要な場合はカスタムテーブルを選択してください。
🔄 DBマイグレーション(バージョン管理)
add_action( 'plugins_loaded', 'mfp_check_db_upgrade' );
function mfp_check_db_upgrade() {
$installed = get_option( 'mfp_db_version', '0' );
// v1.0 → v2.0: カラムを追加
if ( version_compare( $installed, '2.0', '<' ) ) {
global $wpdb;
$table = $wpdb->prefix . 'mfp_logs';
$columns = $wpdb->get_col( "SHOW COLUMNS FROM $table LIKE 'referer'" );
if ( empty( $columns ) ) {
$wpdb->query( "ALTER TABLE $table ADD COLUMN referer varchar(500) NOT NULL DEFAULT ''" );
}
update_option( 'mfp_db_version', '2.0' );
}
// v2.0 → v3.0: 新テーブルを追加
if ( version_compare( $installed, '3.0', '<' ) ) {
mfp_create_tables(); // dbDelta()は冪等なので再実行OK
update_option( 'mfp_db_version', '3.0' );
}
}
⚠️ ALTER TABLE は dbDelta() では実行できない
既存テーブルへのカラム追加・変更は $wpdb->query() で直接実行します。実行前に対象カラムの有無を必ず確認してください。
🔒 トランザクション処理
複数のDB処理をアトミック(全成功か全失敗)にしたい場合はトランザクションを使います。
global $wpdb;
$wpdb->query( 'START TRANSACTION' );
try {
// 処理1: ポイントを消費
$r1 = $wpdb->query( $wpdb->prepare(
"UPDATE {$wpdb->prefix}mfp_points SET points = points - %d WHERE user_id = %d AND points >= %d",
100, $user_id, 100
) );
if ( ! $r1 ) throw new Exception( 'ポイント不足またはDB エラー' );
// 処理2: 購入履歴を記録
$r2 = $wpdb->insert( $wpdb->prefix . 'mfp_purchases',
array( 'user_id' => $user_id, 'item_id' => $item_id, 'cost' => 100, 'created_at' => current_time('mysql') )
);
if ( ! $r2 ) throw new Exception( '購入履歴の記録に失敗' );
$wpdb->query( 'COMMIT' );
} catch ( Exception $e ) {
$wpdb->query( 'ROLLBACK' );
error_log( 'Transaction failed: ' . $e->getMessage() );
}
✏️ 演習:アクセスログ機能を実装しよう
📝 課題
- カスタムテーブル(カラム: id, post_id, user_id, ip_address, accessed_at)を作成する
- 投稿ページが閲覧されるたびにレコードを挿入する
- 管理画面に「直近50件のアクセスログ」を表形式で表示する
- (発展)v2.0として user_agent カラムを追加するマイグレーションを実装する
✅ この章のチェックリスト
- dbDelta()でカスタムテーブルを正しい書式で作成できた
- insert/update/delete/get_resultsを使えた
- prepare()でSQLインジェクション対策ができた
- post/user/termのメタデータAPIを使えた
- バージョン管理を使ったマイグレーションを実装できた
- トランザクション処理の仕組みを理解した
🔗 関連ページ・次のステップ
📘 前提知識
- STEP 5: フロントエンド機能(基本的なDB操作)
- セキュリティ対策($wpdb->prepare())
🚀 あわせて学ぶ
- パフォーマンス最適化(クエリのキャッシュ)
- REST API拡張(DBデータをAPIで公開)
- WP-CLIコマンド開発(DBのエクスポート)
📚 補足
- 実例集(アクセスログの実装例)
- トラブルシューティング