robotstxt-mediaaudit/includes/Core/Database.php
2026-06-03 06:25:27 +00:00

158 lines
4.8 KiB
PHP

<?php
/**
* Database schema creation and versioned migrations.
*
* @package MediaRightsAudit\Core
*/
namespace MediaRightsAudit\Core;
/**
* Manages all custom table creation and schema migrations.
*
* Migration methods are idempotent: they rely on dbDelta's ALTER TABLE
* diffing and can be run multiple times without side effects.
*/
class Database {
/**
* Creates all tables. Called on plugin activation.
*
* @return void
*/
public static function create_tables(): void {
self::migration_100();
self::migration_101();
}
/**
* Applies any pending migrations based on the currently installed schema version.
*
* Called on admin_init by Plugin::check_db_version().
*
* @return void
*/
public static function run_migrations(): void {
$raw = get_option( 'robotstxt_mediaaudit_db_version', '0.0.0' );
$current = is_string( $raw ) ? $raw : '0.0.0';
if ( version_compare( $current, '1.0.0', '<' ) ) {
self::migration_100();
}
if ( version_compare( $current, '1.0.1', '<' ) ) {
self::migration_101();
}
}
/**
* Drops all plugin tables. Used by uninstall.php.
*
* @return void
*/
public static function drop_tables(): void {
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->prefix}mra_external_results`" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->prefix}mra_media_usage`" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
$wpdb->query( "DROP TABLE IF EXISTS `{$wpdb->prefix}mra_media_index`" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
}
/**
* Truncates all plugin tables, removing all data while preserving the table structure.
*
* Used by the Tools page "Reset all data" operation.
*
* @return void
*/
public static function truncate_all(): void {
global $wpdb;
// phpcs:disable WordPress.DB.DirectDatabaseQuery
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_external_results`" );
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_media_usage`" );
$wpdb->query( "TRUNCATE TABLE `{$wpdb->prefix}mra_media_index`" );
// phpcs:enable WordPress.DB.DirectDatabaseQuery
}
// -------------------------------------------------------------------------
// Migrations
// -------------------------------------------------------------------------
/**
* Migration 1.0.0 — creates the initial three tables.
*
* @return void
*/
private static function migration_100(): void {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta(
"CREATE TABLE {$wpdb->prefix}mra_media_index (
attachment_id bigint(20) unsigned NOT NULL,
file_url text NOT NULL,
file_name varchar(255) NOT NULL DEFAULT '',
mime_type varchar(100) NOT NULL DEFAULT '',
file_size bigint(20) unsigned NOT NULL DEFAULT 0,
internal_scanned_at datetime DEFAULT NULL,
external_status enum('pending','queued','scanned','matches','error') NOT NULL DEFAULT 'pending',
external_scanned_at datetime DEFAULT NULL,
created_at datetime NOT NULL,
PRIMARY KEY (attachment_id),
KEY external_status (external_status)
) {$charset_collate};"
);
dbDelta(
"CREATE TABLE {$wpdb->prefix}mra_media_usage (
usage_id bigint(20) unsigned NOT NULL auto_increment,
attachment_id bigint(20) unsigned NOT NULL,
post_id bigint(20) unsigned NOT NULL,
post_type varchar(20) NOT NULL DEFAULT '',
context enum('featured','content','meta') NOT NULL DEFAULT 'featured',
meta_key varchar(255) DEFAULT NULL,
created_at datetime NOT NULL,
PRIMARY KEY (usage_id),
KEY attachment_id (attachment_id),
KEY post_id (post_id)
) {$charset_collate};"
);
dbDelta(
"CREATE TABLE {$wpdb->prefix}mra_external_results (
result_id bigint(20) unsigned NOT NULL auto_increment,
attachment_id bigint(20) unsigned NOT NULL,
provider enum('google_vision','tineye') NOT NULL,
raw_response json DEFAULT NULL,
match_count int(10) unsigned NOT NULL DEFAULT 0,
top_domains json DEFAULT NULL,
created_at datetime NOT NULL,
PRIMARY KEY (result_id),
UNIQUE KEY attachment_provider (attachment_id,provider)
) {$charset_collate};"
);
}
/**
* Migration 1.0.1 — adds 'queued' to the external_status ENUM.
*
* Uses ALTER TABLE directly because dbDelta cannot modify ENUM column definitions.
* Safe to re-run: MariaDB/MySQL treats an identical MODIFY as a no-op.
*
* @return void
*/
private static function migration_101(): void {
global $wpdb;
// phpcs:disable WordPress.DB.DirectDatabaseQuery,WordPress.DB.DirectDatabaseQuery.SchemaChange
$wpdb->query(
"ALTER TABLE `{$wpdb->prefix}mra_media_index`
MODIFY COLUMN external_status
enum('pending','queued','scanned','matches','error') NOT NULL DEFAULT 'pending'"
);
// phpcs:enable WordPress.DB.DirectDatabaseQuery,WordPress.DB.DirectDatabaseQuery.SchemaChange
}
}