Merge pull request #31811 from woocommerce/mvp-cot/intial-table-strucuture

Add DB table structure for custom order tables.
This commit is contained in:
Néstor Soriano 2022-02-15 11:58:08 +01:00 committed by GitHub
commit 44a2c99268
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 316 additions and 141 deletions

View File

@ -7,6 +7,7 @@
*/
use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper as WCConnectionHelper;
defined( 'ABSPATH' ) || exit;
@ -341,21 +342,16 @@ class WC_Install {
* @param bool $modify_notice Whether to modify notice based on if all tables are present.
* @param bool $execute Whether to execute get_schema queries as well.
*
* @return array List of querues.
* @return array List of queries.
*/
public static function verify_base_tables( $modify_notice = true, $execute = false ) {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
if ( $execute ) {
self::create_tables();
}
$queries = dbDelta( self::get_schema(), false );
$missing_tables = array();
foreach ( $queries as $table_name => $result ) {
if ( "Created table $table_name" === $result ) {
$missing_tables[] = $table_name;
}
}
$missing_tables = wc_get_container()
->get( DatabaseUtil::class )
->get_missing_tables( self::get_schema() );
if ( 0 < count( $missing_tables ) ) {
if ( $modify_notice ) {

View File

@ -12,6 +12,7 @@ use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\Orders
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\ProductAttributesLookupServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\ProxiesServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\RestockRefundedItemsAdjusterServiceProvider;
use Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders\UtilsClassesServiceProvider;
/**
* PSR11 compliant dependency injection container for WooCommerce.
@ -43,6 +44,7 @@ final class Container implements \Psr\Container\ContainerInterface {
ProductAttributesLookupServiceProvider::class,
ProxiesServiceProvider::class,
RestockRefundedItemsAdjusterServiceProvider::class,
UtilsClassesServiceProvider::class,
);
/**

View File

@ -8,7 +8,7 @@ namespace Automattic\WooCommerce\Internal\DataStores\Orders;
defined( 'ABSPATH' ) || exit;
/**
* This is the main class that controls the custom orders table feature. Its responsibilities are:
* This is the main class that controls the custom orders tables feature. Its responsibilities are:
*
* - Allowing to enable and disable the feature while it's in development (show_feature method)
* - Displaying UI components (entries in the tools page and in settings)
@ -19,7 +19,7 @@ defined( 'ABSPATH' ) || exit;
class CustomOrdersTableController {
/**
* The name of the option for enabling the usage of the custom orders table
* The name of the option for enabling the usage of the custom orders tables
*/
const CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION = 'woocommerce_custom_orders_table_enabled';
@ -146,7 +146,7 @@ class CustomOrdersTableController {
* @return WC_Object_Data_Store_Interface|string The actual data store to use.
*/
private function get_data_store_instance( $default_data_store ) {
if ( $this->is_feature_visible() && $this->custom_orders_table_usage_is_enabled() && ! $this->data_synchronizer->data_regeneration_is_in_progress() ) {
if ( $this->is_feature_visible() && $this->custom_orders_table_usage_is_enabled() ) {
return $this->data_store;
} else {
return $default_data_store;
@ -160,66 +160,37 @@ class CustomOrdersTableController {
* @param array $tools_array The array of tools to add the tool to.
* @return array The updated array of tools-
*/
private function add_initiate_regeneration_entry_to_tools_array(array $tools_array ): array {
private function add_initiate_regeneration_entry_to_tools_array( array $tools_array ): array {
if ( ! $this->is_feature_visible() ) {
return $tools_array;
}
$orders_table_exists = $this->data_synchronizer->check_orders_table_exists();
$generation_is_in_progress = $this->data_synchronizer->data_regeneration_is_in_progress();
if ( $orders_table_exists ) {
$generate_item_name = __( 'Regenerate the custom orders table', 'woocommerce' );
$generate_item_desc = __( 'This tool will regenerate the custom orders table data from existing orders data from the posts table. This process may take a while.', 'woocommerce' );
$generate_item_return = __( 'Custom orders table is being regenerated', 'woocommerce' );
$generate_item_button = __( 'Regenerate', 'woocommerce' );
} else {
$generate_item_name = __( 'Create and fill custom orders table', 'woocommerce' );
$generate_item_desc = __( 'This tool will create the custom orders table and fill it with existing orders data from the posts table. This process may take a while.', 'woocommerce' );
$generate_item_return = __( 'Custom orders table is being filled', 'woocommerce' );
$generate_item_button = __( 'Create', 'woocommerce' );
}
$entry = array(
'name' => $generate_item_name,
'desc' => $generate_item_desc,
'requires_refresh' => true,
'callback' => function() use ( $generate_item_return ) {
$this->initiate_regeneration_from_tools_page();
return $generate_item_return;
},
);
if ( $generation_is_in_progress ) {
$entry['button'] = sprintf(
/* translators: %d: How many orders have been processed so far. */
__( 'Filling in progress (%d)', 'woocommerce' ),
$this->data_synchronizer->get_regeneration_processed_orders_count()
);
$entry['disabled'] = true;
} else {
$entry['button'] = $generate_item_button;
}
$tools_array['regenerate_custom_orders_table'] = $entry;
if ( $orders_table_exists ) {
// Delete the table.
if ( $this->data_synchronizer->check_orders_table_exists() ) {
$tools_array['delete_custom_orders_table'] = array(
'name' => __( 'Delete the custom orders table', 'woocommerce' ),
'name' => __( 'Delete the custom orders tables', 'woocommerce' ),
'desc' => sprintf(
'<strong class="red">%1$s</strong> %2$s',
__( 'Note:', 'woocommerce' ),
__( 'This will delete the custom orders table. You can create it again with the "Create and fill custom orders table" tool.', 'woocommerce' )
__( 'This will delete the custom orders tables. The tables can be deleted only if they are not not in use (via Settings > Advanced > Custom data stores). You can create them again at any time with the "Create the custom orders tables" tool.', 'woocommerce' )
),
'button' => __( 'Delete', 'woocommerce' ),
'requires_refresh' => true,
'callback' => function () {
$this->delete_custom_orders_table();
return __( 'Custom orders table has been deleted.', 'woocommerce' );
$this->delete_custom_orders_tables();
return __( 'Custom orders tables have been deleted.', 'woocommerce' );
},
'button' => __( 'Delete', 'woocommerce' ),
'disabled' => $this->custom_orders_table_usage_is_enabled(),
);
} else {
$tools_array['create_custom_orders_table'] = array(
'name' => __( 'Create the custom orders tables', 'woocommerce' ),
'desc' => __( 'This tool will create the custom orders tables. Once created you can go to WooCommerce > Settings > Advanced > Custom data stores and configure the usage of the tables.', 'woocommerce' ),
'requires_refresh' => true,
'callback' => function() {
$this->create_custom_orders_tables();
return __( 'Custom orders tables have been created. You can now go to WooCommerce > Settings > Advanced > Custom data stores.', 'woocommerce' );
},
'button' => __( 'Create', 'woocommerce' ),
);
}
@ -227,41 +198,35 @@ class CustomOrdersTableController {
}
/**
* Initiate the custom orders table (re)generation in response to the user pressing the tool button.
* Create the custom orders tables in response to the user pressing the tool button.
*
* @throws \Exception Can't initiate regeneration.
* @throws \Exception Can't create the tables.
*/
private function initiate_regeneration_from_tools_page() {
private function create_custom_orders_tables() {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
if ( ! isset( $_REQUEST['_wpnonce'] ) || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'debug_action' ) ) {
throw new \Exception( 'Invalid nonce' );
}
$this->check_can_do_table_regeneration();
$this->data_synchronizer->initiate_regeneration();
}
/**
* Can the custom orders table regeneration be started?
*
* @throws \Exception The table regeneration can't be started.
*/
private function check_can_do_table_regeneration() {
if ( ! $this->is_feature_visible() ) {
throw new \Exception( "Can't do custom orders table regeneration: the feature isn't enabled" );
throw new \Exception( "Can't create the custom orders tables: the feature isn't enabled" );
}
if ( $this->data_synchronizer->data_regeneration_is_in_progress() ) {
throw new \Exception( "Can't do custom orders table regeneration: regeneration is already in progress" );
}
$this->data_synchronizer->create_database_tables();
}
/**
* Delete the custom orders table and any related options and data.
* Delete the custom orders tables and any related options and data in response to the user pressing the tool button.
*
* @throws \Exception Can't delete the tables.
*/
private function delete_custom_orders_table() {
private function delete_custom_orders_tables() {
if ( $this->custom_orders_table_usage_is_enabled() ) {
throw new \Exception( "Can't delete the custom orders tables: they are currently in use (via Settings > Advanced > Custom data stores)." );
}
delete_option( self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION );
$this->data_synchronizer->delete_custom_orders_table();
$this->data_synchronizer->delete_database_tables();
}
/**
@ -271,7 +236,7 @@ class CustomOrdersTableController {
* @return array The updated settings sections array.
*/
private function get_settings_sections( array $sections ): array {
if ( ! $this->is_feature_visible() || ! $this->data_synchronizer->check_orders_table_exists() ) {
if ( ! $this->is_feature_visible() ) {
return $sections;
}
@ -282,39 +247,47 @@ class CustomOrdersTableController {
/**
* Get the settings for the "Custom data stores" section in the "Advanced" tab,
* with entries for managing the custom orders table if appropriate.
* with entries for managing the custom orders tables if appropriate.
*
* @param array $settings The original settings array.
* @param string $section_id The settings section to get the settings for.
* @return array The updated settings array.
*/
private function get_settings( array $settings, string $section_id ): array {
if ( ! $this->is_feature_visible() || 'custom_data_stores' !== $section_id || ! $this->data_synchronizer->check_orders_table_exists() ) {
if ( ! $this->is_feature_visible() || 'custom_data_stores' !== $section_id ) {
return $settings;
}
$title_item = array(
'title' => __( 'Custom orders table', 'woocommerce' ),
'title' => __( 'Custom orders tables', 'woocommerce' ),
'type' => 'title',
'desc' => sprintf(
/* translators: %1$s = <strong> tag, %2$s = </strong> tag. */
__( '%1$sWARNING:%2$s This feature is currently under development and may cause database instability. For contributors only.', 'woocommerce' ),
'<strong>',
'</strong>'
),
);
$regeneration_is_in_progress = $this->data_synchronizer->data_regeneration_is_in_progress();
if ( $this->data_synchronizer->check_orders_table_exists() ) {
$settings[] = $title_item;
if ( $regeneration_is_in_progress ) {
$title_item['desc'] = __( 'These settings are not available while the orders table regeneration is in progress.', 'woocommerce' );
}
$settings[] = $title_item;
if ( ! $regeneration_is_in_progress ) {
$settings[] = array(
'title' => __( 'Enable table usage', 'woocommerce' ),
'desc' => __( 'Use the custom orders table as the main orders data store.', 'woocommerce' ),
'title' => __( 'Enable tables usage', 'woocommerce' ),
'desc' => __( 'Use the custom orders tables as the main orders data store.', 'woocommerce' ),
'id' => self::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION,
'default' => 'no',
'type' => 'checkbox',
'checkboxgroup' => 'start',
);
} else {
$title_item['desc'] = sprintf(
/* translators: %1$s = <em> tag, %2$s = </em> tag. */
__( 'Create the tables first by going to %1$sWooCommerce > Status > Tools%2$s and running %1$sCreate the custom orders tables%2$s.', 'woocommerce' ),
'<em>',
'</em>'
);
$settings[] = $title_item;
}
$settings[] = array( 'type' => 'sectionend' );

View File

@ -5,19 +5,18 @@
namespace Automattic\WooCommerce\Internal\DataStores\Orders;
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
defined( 'ABSPATH' ) || exit;
/**
* This class handles the data migration/synchronization for the custom orders table. Its responsibilites are:
* This class handles the database structure creation and the data synchronization for the custom orders tables. Its responsibilites are:
*
* - Performing the initial table creation and filling (triggered by initiate_regeneration)
* - Synchronizing changes between the custom orders table and the posts table whenever changes in orders happen.
* - Providing entry points for creating and deleting the required database tables.
* - Synchronizing changes between the custom orders tables and the posts table whenever changes in orders happen.
*/
class DataSynchronizer {
const CUSTOM_ORDERS_TABLE_DATA_REGENERATION_IN_PROGRESS = 'woocommerce_custom_orders_table_data_regeneration_in_progress';
const CUSTOM_ORDERS_TABLE_DATA_REGENERATION_DONE_COUNT = 'woocommerce_custom_orders_table_data_regeneration_done_count';
/**
* The data store object to use.
*
@ -25,6 +24,13 @@ class DataSynchronizer {
*/
private $data_store;
/**
* The database util object to use.
*
* @var DatabaseUtil
*/
private $database_util;
// TODO: Add a constructor to handle hooks as appropriate.
/**
@ -32,58 +38,41 @@ class DataSynchronizer {
*
* @internal
* @param OrdersTableDataStore $data_store The data store to use.
* @param DatabaseUtil $database_util The database util class to use.
*/
final public function init( OrdersTableDataStore $data_store ) {
$this->data_store = $data_store;
final public function init( OrdersTableDataStore $data_store, DatabaseUtil $database_util ) {
$this->data_store = $data_store;
$this->database_util = $database_util;
}
/**
* Does the custom orders table exist in the database?
* Does the custom orders tables exist in the database?
*
* @return bool True if the custom orders table exist in the database.
* @return bool True if the custom orders tables exist in the database.
*/
public function check_orders_table_exists(): bool {
global $wpdb;
$missing_tables = $this->database_util->get_missing_tables( $this->data_store->get_database_schema() );
$table_name = $this->data_store->get_orders_table_name();
$query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $this->$table_name ) );
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
return $table_name === $wpdb->get_var( $query );
return count( $missing_tables ) === 0;
}
/**
* Is a table regeneration in progress?
*
* @return bool True if a table regeneration in currently progress
* Create the custom orders database tables.
*/
public function data_regeneration_is_in_progress(): bool {
return 'yes' === get_option( self::CUSTOM_ORDERS_TABLE_DATA_REGENERATION_IN_PROGRESS );
public function create_database_tables() {
$this->database_util->dbdelta( $this->data_store->get_database_schema() );
}
/**
* Initiate a table regeneration process.
* Delete the custom orders database tables.
*/
public function initiate_regeneration() {
update_option( self::CUSTOM_ORDERS_TABLE_DATA_REGENERATION_IN_PROGRESS, 'yes' );
update_option( self::CUSTOM_ORDERS_TABLE_DATA_REGENERATION_DONE_COUNT, 0 );
public function delete_database_tables() {
$table_names = $this->data_store->get_all_table_names();
// TODO: Create the tables as appropriate, schedule the table filling in batches with Action Scheduler.
foreach ( $table_names as $table_name ) {
$this->database_util->drop_database_table( $table_name );
}
}
/**
* How many orders have been processed as part of the custom orders table regeneration?
*
* @return int Number of orders already processed, 0 if no regeneration is in progress.
*/
public function get_regeneration_processed_orders_count(): int {
return (int)get_option( self::CUSTOM_ORDERS_TABLE_DATA_REGENERATION_DONE_COUNT, 0 );
}
/**
* Delete the custom orders table and the associated information.
*/
public function delete_custom_orders_table() {
// TODO: Delete the tables and any associated data (e.g. options).
}
}

View File

@ -19,13 +19,45 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
*/
public function get_orders_table_name() {
global $wpdb;
return $wpdb->prefix . 'wc_orders';
}
/**
* Get the order addresses table name.
*
* @return string The order addresses table name.
*/
public function get_addresses_table_name() {
global $wpdb;
return $wpdb->prefix . 'wc_order_addresses';
}
/**
* Get the orders operational data table name.
*
* @return string The orders operational data table name.
*/
public function get_operational_data_table_name() {
global $wpdb;
return $wpdb->prefix . 'wc_order_operational_data';
}
/**
* Get the names of all the tables involved in the custom orders table feature.
*
* @return string[]
*/
public function get_all_table_names() {
return array(
$this->get_orders_table_name(),
$this->get_addresses_table_name(),
$this->get_operational_data_table_name(),
);
}
// TODO: Add methods for other table names as appropriate.
//phpcs:disable Squiz.Commenting.FunctionComment.Missing
//phpcs:disable Squiz.Commenting, Generic.Commenting
public function get_total_refunded( $order ) {
// TODO: Implement get_total_refunded() method.
@ -99,6 +131,9 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
return 'shop_order';
}
/**
* @param \WC_Order $order
*/
public function create( &$order ) {
throw new \Exception( 'Unimplemented' );
}
@ -139,5 +174,80 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements
return 'line_item';
}
//phpcs:enable Squiz.Commenting.FunctionComment.Missing
//phpcs:enable Squiz.Commenting, Generic.Commenting
/**
* Get the SQL needed to create all the tables needed for the custom orders table feature.
*
* @return string
*/
public function get_database_schema() {
$orders_table_name = $this->get_orders_table_name();
$addresses_table_name = $this->get_addresses_table_name();
$operational_data_table_name = $this->get_operational_data_table_name();
$sql = "
CREATE TABLE $orders_table_name (
id bigint(20) unsigned auto_increment,
post_id bigint(20) unsigned null,
status varchar(20) null,
currency varchar(10) null,
tax_amount decimal(26,8) null,
total_amount decimal(26,8) null,
customer_id bigint(20) unsigned null,
billing_email varchar(320) null,
date_created_gmt datetime null,
date_updated_gmt datetime null,
parent_order_id bigint(20) unsigned null,
payment_method varchar(100) null,
payment_method_title text null,
transaction_id varchar(100) null,
ip_address varchar(100) null,
user_agent text null,
PRIMARY KEY (id),
KEY post_id (post_id),
KEY status (status),
KEY date_created (date_created_gmt),
KEY customer_id_billing_email (customer_id, billing_email)
);
CREATE TABLE $addresses_table_name (
id bigint(20) unsigned auto_increment primary key,
order_id bigint(20) unsigned NOT NULL,
address_type varchar(20) null,
first_name text null,
last_name text null,
company text null,
address_1 text null,
address_2 text null,
city text null,
state text null,
postcode text null,
country text null,
email varchar(320) null,
phone varchar(100) null,
KEY order_id (order_id)
);
CREATE TABLE $operational_data_table_name (
id bigint(20) unsigned auto_increment primary key,
order_id bigint(20) unsigned NULL,
created_via varchar(100) NULL,
woocommerce_version varchar(20) NULL,
prices_include_tax tinyint(1) NULL,
coupon_usages_are_counted tinyint(1) NULL,
download_permissionis_granted tinyint(1) NULL,
cart_hash varchar(100) NULL,
new_order_email_sent tinyint(1) NULL,
order_key varchar(100) NULL,
order_stock_reduced tinyint(1) NULL,
date_paid_gmt datetime NULL,
date_completed_gmt datetime NULL,
shipping_tax_amount decimal(26, 8) NULL,
shopping_total_amount decimal(26, 8) NULL,
discount_tax_amount decimal(26, 8) NULL,
discount_total_amount decimal(26, 8) NULL,
KEY order_id (order_id),
KEY order_key (order_key)
);";
return $sql;
}
}

View File

@ -1,6 +1,6 @@
<?php
/**
* ProductAttributesLookupServiceProvider class file.
* OrdersDataStoreServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
@ -9,9 +9,10 @@ use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
/**
* Service provider for the ProductAttributesLookupServiceProvider namespace.
* Service provider for the classes in the Internal\DataStores\Orders namespace.
*/
class OrdersDataStoreServiceProvider extends AbstractServiceProvider {
@ -30,7 +31,7 @@ class OrdersDataStoreServiceProvider extends AbstractServiceProvider {
* Register the classes.
*/
public function register() {
$this->share( DataSynchronizer::class )->addArgument( OrdersTableDataStore::class );
$this->share( DataSynchronizer::class )->addArguments( array( OrdersTableDataStore::class, DatabaseUtil::class ) );
$this->share( CustomOrdersTableController::class )->addArguments( array( OrdersTableDataStore::class, DataSynchronizer::class ) );
$this->share( OrdersTableDataStore::class );
}

View File

@ -0,0 +1,31 @@
<?php
/**
* UtilsClassesServiceProvider class file.
*/
namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders;
use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider;
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
/**
* Service provider for the non-static utils classes in the Automattic\WooCommerce\src namespace.
*/
class UtilsClassesServiceProvider extends AbstractServiceProvider {
/**
* The classes/interfaces that are serviced by this service provider.
*
* @var array
*/
protected $provides = array(
DatabaseUtil::class,
);
/**
* Register the classes.
*/
public function register() {
$this->share( DatabaseUtil::class );
}
}

View File

@ -0,0 +1,73 @@
<?php
/**
* DatabaseUtil class file.
*/
namespace Automattic\WooCommerce\Internal\Utilities;
/**
* A class of utilities for dealing with the database.
*/
class DatabaseUtil {
/**
* Wrapper for the WordPress dbDelta function, allows to execute a series of SQL queries.
*
* @param string $queries The SQL queries to execute.
* @param bool $execute Ture to actually execute the queries, false to only simulate the execution.
* @return array The result of the execution (or simulation) from dbDelta.
*/
public function dbdelta( string $queries = '', bool $execute = true ): array {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
return dbDelta( $queries, $execute );
}
/**
* Given a set of table creation SQL statements, check which of the tables are currently missing in the database.
*
* @param string $creation_queries The SQL queries to execute ("CREATE TABLE" statements, same format as for dbDelta).
* @return array An array containing the names of the tables that currently don't exist in the database.
*/
public function get_missing_tables( string $creation_queries ): array {
$dbdelta_output = $this->dbdelta( $creation_queries, false );
$parsed_output = $this->parse_dbdelta_output( $dbdelta_output );
return $parsed_output['created_tables'];
}
/**
* Parses the output given by dbdelta and returns information about it.
*
* @param array $dbdelta_output The output from the execution of dbdelta.
* @return array[] An array containing a 'created_tables' key whose value is an array with the names of the tables that have been (or would have been) created.
*/
public function parse_dbdelta_output( array $dbdelta_output ): array {
$created_tables = array();
foreach ( $dbdelta_output as $table_name => $result ) {
if ( "Created table $table_name" === $result ) {
$created_tables[] = $table_name;
}
}
return array( 'created_tables' => $created_tables );
}
/**
* Drops a database table.
*
* @param string $table_name The name of the table to drop.
* @param bool $add_prefix True if the table name passed needs to be prefixed with $wpdb->prefix before processing.
* @return bool True on success, false on error.
*/
public function drop_database_table( string $table_name, bool $add_prefix = false ) {
global $wpdb;
if ( $add_prefix ) {
$table_name = $wpdb->prefix . $table_name;
}
//phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
return $wpdb->query( "DROP TABLE IF EXISTS `{$table_name}`" );
}
}