> */ public static function list_versions( string $key ): array { if ( ! PRAutoBlogger_Prompt_Registry::is_available() ) { return array(); } global $wpdb; $table = PRAutoBlogger_Prompt_Registry::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE prompt_key = %s ORDER BY version DESC", $key ), ARRAY_A ); return is_array( $rows ) ? $rows : array(); } /** * Create a NEW immutable version of a key and (optionally) activate it. * Never mutates an existing row's body — that is the whole point. * * @param string $key Registry key. * @param string $body Template body. * @param string|null $model Optional model hint. * @param array|null $params Optional params snapshot. * @param string $author Who created it (seed tag, login, agent). * @param bool $activate Flip active to this version (default true). * @return int New version number, or 0 on failure / table unavailable. */ public static function create_version( string $key, string $body, ?string $model, ?array $params, string $author, bool $activate = true ): int { if ( ! PRAutoBlogger_Prompt_Registry::is_available() ) { return 0; } global $wpdb; $table = PRAutoBlogger_Prompt_Registry::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $max = (int) $wpdb->get_var( $wpdb->prepare( "SELECT MAX(version) FROM {$table} WHERE prompt_key = %s", $key ) ); $next = $max + 1; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $ok = $wpdb->insert( $table, array( 'prompt_key' => $key, 'version' => $next, 'body' => $body, 'model' => $model, 'params_json' => null !== $params ? wp_json_encode( $params ) : null, 'author' => $author, 'created_at' => current_time( 'mysql' ), 'active' => 0, ) ); if ( false === $ok ) { return 0; } if ( $activate ) { self::activate( $key, $next ); } return $next; } /** * Make one version the single active version of its key. * * @param string $key Registry key. * @param int $version Version to activate. * @return bool True when the version is now active. */ public static function activate( string $key, int $version ): bool { if ( ! PRAutoBlogger_Prompt_Registry::is_available() ) { return false; } global $wpdb; $table = PRAutoBlogger_Prompt_Registry::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( $wpdb->prepare( "UPDATE {$table} SET active = 0 WHERE prompt_key = %s", $key ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $updated = $wpdb->query( $wpdb->prepare( "UPDATE {$table} SET active = 1 WHERE prompt_key = %s AND version = %d", $key, $version ) ); PRAutoBlogger_Prompt_Registry::flush_cache(); return 1 === (int) $updated; } /** * Seed every registry key with its canonical v1 body. Idempotent: keys * that already have rows are left untouched (their history is theirs). * * Side effects: up to one INSERT + two UPDATEs per missing key. * * @param string $author Audit tag for the seed rows (e.g. 'seed:v0.18.0'). * @return int Number of keys seeded. */ public static function seed_v1( string $author = 'seed:v0.18.0' ): int { if ( ! PRAutoBlogger_Prompt_Registry::is_available() ) { return 0; } $seeded = 0; foreach ( PRAutoBlogger_Prompt_Registry::defs() as $key => $def ) { if ( ! empty( self::list_versions( $key ) ) ) { continue; } $model = null !== $def['model_option'] ? (string) get_option( $def['model_option'], '' ) : null; $version = self::create_version( $key, $def['body'], '' !== (string) $model ? $model : null, $def['params'], $author, true ); if ( $version > 0 ) { ++$seeded; } } PRAutoBlogger_Prompt_Registry::flush_cache(); return $seeded; } }