ration is already in progress.
*/
private function initiate_regeneration_from_tools_page() {
$this->verify_tool_execution_nonce();
//phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_REQUEST['regenerate_product_attribute_lookup_data_product_id'] ) ) {
$product_id = (int) $_REQUEST['regenerate_product_attribute_lookup_data_product_id'];
$this->check_can_do_lookup_table_regeneration( $product_id );
$this->data_store->create_data_for_product( $product_id );
} else {
$this->check_can_do_lookup_table_regeneration();
$this->initiate_regeneration();
}
//phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Enable or disable the actual lookup table usage.
*
* @param bool $enable True to enable, false to disable.
* @throws \Exception A lookup table regeneration is currently in progress.
*/
private function enable_or_disable_lookup_table_usage( $enable ) {
if ( $this->data_store->regeneration_is_in_progress() ) {
throw new \Exception( "Can't enable or disable the attributes lookup table usage while it's regenerating." );
}
update_option( 'woocommerce_attribute_lookup_enabled', $enable ? 'yes' : 'no' );
}
/**
* Check if everything is good to go to perform a complete or per product lookup table data regeneration
* and throw an exception if not.
*
* @param mixed $product_id The product id to check the regeneration viability for, or null to check if a complete regeneration is possible.
* @throws \Exception Something prevents the regeneration from starting.
*/
private function check_can_do_lookup_table_regeneration( $product_id = null ) {
if ( $product_id && ! $this->data_store->check_lookup_table_exists() ) {
throw new \Exception( "Can't do product attribute lookup data regeneration: lookup table doesn't exist" );
}
if ( $this->data_store->regeneration_is_in_progress() ) {
throw new \Exception( "Can't do product attribute lookup data regeneration: regeneration is already in progress" );
}
if ( $product_id && ! wc_get_product( $product_id ) ) {
throw new \Exception( "Can't do product attribute lookup data regeneration: product doesn't exist" );
}
}
/**
* Callback to abort the regeneration process from the Status - Tools page.
*
* @throws \Exception The lookup table doesn't exist, or there's no regeneration process in progress to abort.
*/
private function abort_regeneration_from_tools_page() {
$this->verify_tool_execution_nonce();
if ( ! $this->data_store->check_lookup_table_exists() ) {
throw new \Exception( "Can't abort the product attribute lookup data regeneration process: lookup table doesn't exist" );
}
if ( ! $this->data_store->regeneration_is_in_progress() ) {
throw new \Exception( "Can't abort the product attribute lookup data regeneration process since it's not currently in progress" );
}
$queue = WC()->get_instance_of( \WC_Queue::class );
$queue->cancel_all( 'woocommerce_run_product_attribute_lookup_regeneration_callback' );
$this->data_store->unset_regeneration_in_progress_flag();
$this->data_store->set_regeneration_aborted_flag();
$this->enable_or_disable_lookup_table_usage( false );
// Note that we are NOT deleting the options that track the regeneration progress (processed count, last product id to process).
// This is on purpose so that the regeneration can be resumed where it stopped.
}
/**
* Callback to resume the regeneration process from the Status - Tools page.
*
* @throws \Exception The lookup table doesn't exist, or a regeneration process is already in place.
*/
private function resume_regeneration_from_tools_page() {
$this->verify_tool_execution_nonce();
if ( ! $this->data_store->check_lookup_table_exists() ) {
throw new \Exception( "Can't resume the product attribute lookup data regeneration process: lookup table doesn't exist" );
}
if ( $this->data_store->regeneration_is_in_progress() ) {
throw new \Exception( "Can't resume the product attribute lookup data regeneration process: regeneration is already in progress" );
}
$this->data_store->unset_regeneration_aborted_flag();
$this->data_store->set_regeneration_in_progress_flag();
$this->enqueue_regeneration_step_run();
}
/**
* Verify the validity of the nonce received when executing a tool from the Status - Tools page.
*
* @throws \Exception Missing or invalid nonce received.
*/
private function verify_tool_execution_nonce() {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
if ( ! isset( $_REQUEST['_wpnonce'] ) || false === wp_verify_nonce( $_REQUEST['_wpnonce'], 'debug_action' ) ) {
throw new \Exception( 'Invalid nonce' );
}
}
/**
* Get the name of the product attributes lookup table.
*
* @return string
*/
public function get_lookup_table_name() {
return $this->lookup_table_name;
}
/**
* Get the SQL statement that creates the product attributes lookup table, including the indices.
*
* @return string
*/
public function get_table_creation_sql() {
global $wpdb;
$collate = $wpdb->has_cap( 'collation' ) ? $wpdb->get_charset_collate() : '';
return "CREATE TABLE {$this->lookup_table_name} (
product_id bigint(20) NOT NULL,
product_or_parent_id bigint(20) NOT NULL,
taxonomy varchar(32) NOT NULL,
term_id bigint(20) NOT NULL,
is_variation_attribute tinyint(1) NOT NULL,
in_stock tinyint(1) NOT NULL,
INDEX is_variation_attribute_term_id (is_variation_attribute, term_id),
PRIMARY KEY ( `product_or_parent_id`, `term_id`, `product_id`, `taxonomy` )
) $collate;";
}
/**
* Create the primary key for the table if it doesn't exist already.
* It also deletes the product_or_parent_id_term_id index if it exists, since it's now redundant.
*
* @return void
*/
public function create_table_primary_index() {
$database_util = wc_get_container()->get( DatabaseUtil::class );
$database_util->create_primary_key( $this->lookup_table_name, array( 'product_or_parent_id', 'term_id', 'product_id', 'taxonomy' ) );
$database_util->drop_table_index( $this->lookup_table_name, 'product_or_parent_id_term_id' );
if ( empty( $database_util->get_index_columns( $this->lookup_table_name ) ) ) {
wc_get_logger()->error( "The creation of the primary key for the {$this->lookup_table_name} table failed" );
}
if ( ! empty( $database_util->get_index_columns( $this->lookup_table_name, 'product_or_parent_id_term_id' ) ) ) {
wc_get_logger()->error( "Dropping the product_or_parent_id_term_id index from the {$this->lookup_table_name} table failed" );
}
}
/**
* Run additional setup needed after a WooCommerce install or update finishes.
*/
private function run_woocommerce_installed_callback() {
// The table must exist at this point (created via dbDelta), but we check just in case.
if ( ! $this->data_store->check_lookup_table_exists() ) {
return;
}
// If a table regeneration is in progress, leave it alone.
if ( $this->data_store->regeneration_is_in_progress() ) {
return;
}
// If the lookup table has data, or if it's empty because there are no products yet, we're good.
// Otherwise (lookup table is empty but products exist) we need to initiate a regeneration if one isn't already in progress.
if ( $this->data_store->lookup_table_has_data() || ! $this->get_last_existing_product_id() ) {
$this->finalize_regeneration( true );
} else {
$this->initiate_regeneration();
}
}
}