💾 $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() ); }

✏️ 演習:アクセスログ機能を実装しよう

📝 課題

  1. カスタムテーブル(カラム: id, post_id, user_id, ip_address, accessed_at)を作成する
  2. 投稿ページが閲覧されるたびにレコードを挿入する
  3. 管理画面に「直近50件のアクセスログ」を表形式で表示する
  4. (発展)v2.0として user_agent カラムを追加するマイグレーションを実装する

✅ この章のチェックリスト

  • dbDelta()でカスタムテーブルを正しい書式で作成できた
  • insert/update/delete/get_resultsを使えた
  • prepare()でSQLインジェクション対策ができた
  • post/user/termのメタデータAPIを使えた
  • バージョン管理を使ったマイグレーションを実装できた
  • トランザクション処理の仕組みを理解した

🔗 関連ページ・次のステップ

📘 前提知識

🚀 あわせて学ぶ

📚 補足