get_charset_collate(); // テーブル作成クエリ // v2.17.0棚卸し: 実使用は aagl_main_keywords / aagl_sub_keywords の2テーブルのみ。 // 書き込みコードが存在しなかった将来用テーブル(aagl_templates / aagl_keywords / // aagl_generated_articles / aagl_schedule_settings / aagl_ai_subkeyword_history / // aagl_generation_history)の作成を停止(既存サイトのテーブルはDROPしない)。 // テンプレート・スケジュールは wp_options、生成カウントは postmeta で管理。 $sql_queries = array(); // 1. メインキーワードテーブル // Note: varchar(191) = MySQL 5.6 + utf8mb4 UNIQUE KEY上限(767bytes / 4bytes = 191) $table_name = $wpdb->prefix . 'aagl_main_keywords'; $sql_queries[] = "CREATE TABLE $table_name ( id int(11) NOT NULL AUTO_INCREMENT, keyword varchar(191) NOT NULL, category varchar(100) DEFAULT NULL, search_volume int(11) DEFAULT 0, difficulty int(11) DEFAULT 50, priority tinyint(4) NOT NULL DEFAULT 1, status enum('active','inactive','completed') DEFAULT 'active', notes text DEFAULT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY unique_keyword (keyword), KEY idx_status (status), KEY idx_priority (priority), KEY idx_created_at (created_at), KEY idx_status_priority (status, priority) ) $charset_collate;"; // 2. サブキーワードテーブル // Note: varchar(187) = 複合UNIQUE KEY上限(767 - 4(int) = 763bytes / 4bytes = 190、余裕見て187) $table_name = $wpdb->prefix . 'aagl_sub_keywords'; $sql_queries[] = "CREATE TABLE $table_name ( id int(11) NOT NULL AUTO_INCREMENT, main_keyword_id int(11) NOT NULL, sub_keyword varchar(187) NOT NULL, search_volume int(11) DEFAULT 0, status enum('active','inactive','used') DEFAULT 'active', source enum('manual','ai_generated','imported') DEFAULT 'manual', created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY fk_main_keyword (main_keyword_id), KEY idx_status (status), KEY idx_source (source), KEY idx_main_keyword_status (main_keyword_id, status), UNIQUE KEY unique_sub_keyword_per_main (main_keyword_id, sub_keyword) ) $charset_collate;"; // テーブル作成実行 require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); foreach ($sql_queries as $sql) { dbDelta($sql); } // デフォルトデータ挿入 self::insert_default_data(); // エラーチェック if ($wpdb->last_error) { AAGL_Utilities::log('DB Error: ' . $wpdb->last_error); } // 作成確認ログ AAGL_Utilities::log('DB: テーブル作成処理完了'); } /** * デフォルトデータ挿入 * 注: テンプレートはwp_options (aagl_article_templates) で管理。 * 旧DBテーブル (aagl_templates, aagl_keywords, aagl_schedule_settings) へのサンプルデータ挿入は不要。 */ private static function insert_default_data() { // 現在のシステムではwp_options APIベースのため、DBテーブルへのサンプルデータ挿入は行わない // テンプレート初期化: wordpress-native-functions.php init_default_templates() // 生成の記録・設定は wp_options で管理する } /** * テーブル削除(アンインストール時) */ public static function drop_tables() { global $wpdb; // 外部キー制約を考慮した順序で削除(子テーブルから先に削除) $tables = array( 'aagl_generated_articles', 'aagl_generation_history', 'aagl_ai_subkeyword_history', 'aagl_sub_keywords', 'aagl_main_keywords', 'aagl_keywords', 'aagl_templates', 'aagl_schedule_settings', ); foreach ($tables as $table) { $table_name = $wpdb->prefix . $table; // テーブル名はプレフィックス+固定値のみで構成されるため、直接埋め込みで安全 $wpdb->query("DROP TABLE IF EXISTS `{$table_name}`"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared } } /** * テーブル存在チェック(実使用テーブルのみ) */ public static function tables_exist() { global $wpdb; $required_tables = array( 'aagl_main_keywords', 'aagl_sub_keywords', ); foreach ($required_tables as $table) { $table_name = $wpdb->prefix . $table; if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) !== $table_name) { AAGL_Utilities::log('DB: 必要なテーブルが存在しません: ' . $table_name); return false; } } return true; } /** * データベース整合性チェック */ public static function check_integrity() { global $wpdb; $issues = array(); // 孤立したサブキーワードチェック // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Multi-table JOIN with dynamic prefix, not user input $orphaned_subs = $wpdb->get_var(" SELECT COUNT(*) FROM {$wpdb->prefix}aagl_sub_keywords s LEFT JOIN {$wpdb->prefix}aagl_main_keywords m ON s.main_keyword_id = m.id WHERE m.id IS NULL "); if ($orphaned_subs > 0) { $issues[] = "孤立したサブキーワードが {$orphaned_subs} 件存在します"; } return $issues; } /** * データベースクリーンアップ */ public static function cleanup_orphaned_data() { global $wpdb; // 孤立したサブキーワード削除 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Multi-table DELETE with dynamic prefix, not user input $wpdb->query(" DELETE s FROM {$wpdb->prefix}aagl_sub_keywords s LEFT JOIN {$wpdb->prefix}aagl_main_keywords m ON s.main_keyword_id = m.id WHERE m.id IS NULL "); return true; } }