prefix . 'ld_enrollment_logs'; } /** * Run install/migrations when the stored version differs. * * @return void */ public static function maybe_upgrade() { if ( get_option( self::VERSION_OPTION ) !== EVLFL_DB_VERSION ) { try { self::install(); } catch ( \Throwable $e ) { if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( 'EVL migration failed: ' . $e->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log } } } } /** * Create/update the table (idempotent) and run version migrations. * * @return void */ public static function install() { global $wpdb; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; $from = (string) get_option( self::VERSION_OPTION, '0' ); $table = self::table_name(); $collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, user_id bigint(20) unsigned NOT NULL, course_id bigint(20) unsigned NOT NULL, action varchar(20) NOT NULL, source varchar(50) NOT NULL DEFAULT 'unknown', event_trigger varchar(100) NOT NULL DEFAULT 'unknown', actor_type varchar(30) NOT NULL DEFAULT 'unknown', actor_id bigint(20) unsigned DEFAULT NULL, status varchar(20) NOT NULL DEFAULT 'unknown', verification_reason text DEFAULT NULL, confidence varchar(20) NOT NULL DEFAULT 'unknown', event_time datetime NOT NULL, related_event_id bigint(20) unsigned DEFAULT NULL, metadata longtext DEFAULT NULL, created_at datetime NOT NULL, PRIMARY KEY (id), KEY event_time (event_time), KEY user_event (user_id,event_time), KEY course_event (course_id,event_time), KEY action_event (action,event_time), KEY source_event (source,event_time), KEY status_event (status,event_time) ) {$collate};"; dbDelta( $sql ); // Future schema changes: add version => callable entries; they run once, in order. $migrations = array(); foreach ( $migrations as $version => $callback ) { if ( version_compare( $from, (string) $version, '<' ) && is_callable( $callback ) ) { call_user_func( $callback ); } } // Settings are a small array read on every request, so they are autoloaded; creating the // option up front also avoids a "missing option" database lookup on each page load. add_option( EVLFL_Settings::OPTION, array(), '', true ); update_option( self::VERSION_OPTION, EVLFL_DB_VERSION, false ); } }