Implement handling of changesets in the LookupDataStore class.

This commit is contained in:
Nestor Soriano 2021-06-11 11:44:57 +02:00
parent bbddcbcc15
commit 7c9137698b
No known key found for this signature in database
GPG Key ID: 08110F3518C12CAD
4 changed files with 119 additions and 37 deletions

View File

@ -233,7 +233,7 @@ CREATE TABLE ' . $this->lookup_table_name . '(
} }
foreach ( $product_ids as $id ) { foreach ( $product_ids as $id ) {
$this->data_store->insert_data_for_product( $id ); $this->data_store->create_data_for_product( $id );
} }
update_option( 'woocommerce_attribute_lookup__last_products_page_processed', $current_products_page ); update_option( 'woocommerce_attribute_lookup__last_products_page_processed', $current_products_page );

View File

@ -14,6 +14,15 @@ defined( 'ABSPATH' ) || exit;
*/ */
class LookupDataStore { class LookupDataStore {
/**
* Types of updates to perform depending on the current changest
*/
const ACTION_NONE = 0;
const ACTION_INSERT = 1;
const ACTION_UPDATE_STOCK = 2;
const ACTION_DELETE = 3;
/** /**
* The lookup table name. * The lookup table name.
* *
@ -120,20 +129,82 @@ AND table_name = %s;',
$product = WC()->call_function( 'wc_get_product', $product ); $product = WC()->call_function( 'wc_get_product', $product );
} }
// No changeset available: the product/variation is new, so just insert its data. $update_type = $this->get_update_type( $changeset );
switch ( $update_type ) {
case self::ACTION_INSERT:
$this->delete_data_for( $product->get_id() );
$this->create_data_for( $product );
break;
case self::ACTION_UPDATE_STOCK:
$this->update_stock_status_for( $product );
break;
case self::ACTION_DELETE:
$this->delete_data_for( $product->get_id() );
break;
}
}
/**
* Determine the type of action to perform depending on the received changeset.
*
* @param array|null $changeset The changeset received by on_product_changed.
* @return int One of the ACTION_ constants.
*/
private function get_update_type( $changeset ) {
if ( is_null( $changeset ) ) { if ( is_null( $changeset ) ) {
$this->delete_lookup_table_entries_for( $product->get_id() ); // No changeset at all means that the product is new.
if ( $this->is_variation( $product ) ) { return self::ACTION_INSERT;
$this->insert_data_for_variation( $product ); }
$keys = array_keys( $changeset );
// Order matters:
// - The change with the most precedence is a change in catalog visibility
// (which will result in all data being regenerated or deleted).
// - Then a change in attributes (all data will be regenerated).
// - And finally a change in stock status (existing data will be updated).
// Thus these conditions must be checked in that same order.
if ( in_array( 'catalog_visibility', $keys, true ) ) {
$new_visibility = $changeset['catalog_visibility'];
if ( 'visible' === $new_visibility || 'catalog' === $new_visibility ) {
return self::ACTION_INSERT;
} else { } else {
$this->insert_data_for_product( $product ); return self::ACTION_DELETE;
} }
} }
// Changeset available: process it. if ( in_array( 'attributes', $keys, true ) ) {
return self::ACTION_INSERT;
}
// TODO: Implement changeset processing. if ( array_intersect( $keys, array( 'stock_quantity', 'stock_status', 'manage_stock' ) ) ) {
return self::ACTION_UPDATE_STOCK;
}
return self::ACTION_NONE;
}
/**
* Update the stock status of the lookup table entries for a given product.
*
* @param \WC_Product $product The product to update the entries for.
*/
private function update_stock_status_for( \WC_Product $product ) {
global $wpdb;
$in_stock = $product->is_in_stock();
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
$wpdb->prepare(
'UPDATE ' . $this->lookup_table_name . ' SET in_stock = %d WHERE product_id = %d',
$in_stock ? 1 : 0,
$product->get_id()
)
);
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
} }
/** /**
@ -154,31 +225,42 @@ AND table_name = %s;',
$product_id = $product; $product_id = $product;
} }
$this->delete_lookup_table_entries_for( $product_id ); $this->delete_data_for( $product_id );
} }
/** /**
* Insert the lookup data for a given product or variation. * Create the lookup data for a given product, if a variable product is passed
* If a variable product is passed the information is created for all of its variations. * the information is created for all of its variations.
* This method is intended to be called from the data regenerator.
* *
* @param int|WC_Product $product Product object or id. * @param int|WC_Product $product Product object or id.
* @throws \Exception A variation object is passed. * @throws \Exception A variation object is passed.
*/ */
public function insert_data_for_product( $product ) { public function create_data_for_product( $product ) {
if ( ! is_a( $product, \WC_Product::class ) ) { if ( ! is_a( $product, \WC_Product::class ) ) {
$product = WC()->call_function( 'wc_get_product', $product ); $product = WC()->call_function( 'wc_get_product', $product );
} }
if ( $this->is_variation( $product ) ) { if ( $this->is_variation( $product ) ) {
throw new \Exception( "LookupDataStore::insert_data_for_product can't be called for variations." ); throw new \Exception( "LookupDataStore::create_data_for_product can't be called for variations." );
} }
$this->delete_lookup_table_entries_for( $product->get_id() ); $this->delete_data_for( $product->get_id() );
$this->create_data_for( $product );
}
if ( $this->is_variable_product( $product ) ) { /**
$this->create_lookup_table_entries_for_variable_product( $product ); * Create lookup table data for a given product.
*
* @param \WC_Product $product The product to create the data for.
*/
private function create_data_for( \WC_Product $product ) {
if ( $this->is_variation( $product ) ) {
$this->create_data_for_variation( $product );
} elseif ( $this->is_variable_product( $product ) ) {
$this->create_data_for_variable_product( $product );
} else { } else {
$this->create_lookup_table_entries_for_simple_product( $product ); $this->create_data_for_simple_product( $product );
} }
} }
@ -188,7 +270,7 @@ AND table_name = %s;',
* *
* @param int $product_id Simple product id, or main/parent product id for variable products. * @param int $product_id Simple product id, or main/parent product id for variable products.
*/ */
private function delete_lookup_table_entries_for( int $product_id ) { private function delete_data_for( int $product_id ) {
global $wpdb; global $wpdb;
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
@ -208,7 +290,7 @@ AND table_name = %s;',
* *
* @param \WC_Product $product The product to create the entries for. * @param \WC_Product $product The product to create the entries for.
*/ */
private function create_lookup_table_entries_for_simple_product( \WC_Product $product ) { private function create_data_for_simple_product( \WC_Product $product ) {
$product_attributes_data = $this->get_attribute_taxonomies( $product ); $product_attributes_data = $this->get_attribute_taxonomies( $product );
$has_stock = $product->is_in_stock(); $has_stock = $product->is_in_stock();
$product_id = $product->get_id(); $product_id = $product->get_id();
@ -226,7 +308,7 @@ AND table_name = %s;',
* *
* @param \WC_Product_Variable $product The product to create the entries for. * @param \WC_Product_Variable $product The product to create the entries for.
*/ */
private function create_lookup_table_entries_for_variable_product( \WC_Product_Variable $product ) { private function create_data_for_variable_product( \WC_Product_Variable $product ) {
$product_attributes_data = $this->get_attribute_taxonomies( $product ); $product_attributes_data = $this->get_attribute_taxonomies( $product );
$variation_attributes_data = array_filter( $variation_attributes_data = array_filter(
$product_attributes_data, $product_attributes_data,
@ -266,7 +348,7 @@ AND table_name = %s;',
* *
* @param \WC_Product_Variation $variation The variation to create entries for. * @param \WC_Product_Variation $variation The variation to create entries for.
*/ */
private function insert_data_for_variation( \WC_Product_Variation $variation ) { private function create_data_for_variation( \WC_Product_Variation $variation ) {
$main_product = wc_get_product( $variation->get_parent_id() ); $main_product = wc_get_product( $variation->get_parent_id() );
$product_attributes_data = $this->get_attribute_taxonomies( $main_product ); $product_attributes_data = $this->get_attribute_taxonomies( $main_product );

View File

@ -51,7 +51,7 @@ class DataRegeneratorTest extends \WC_Unit_Test_Case {
$this->lookup_data_store = new class() extends LookupDataStore { $this->lookup_data_store = new class() extends LookupDataStore {
public $passed_products = array(); public $passed_products = array();
public function insert_data_for_product( $product ) { public function create_data_for_product( $product ) {
$this->passed_products[] = $product; $this->passed_products[] = $product;
} }
}; };

View File

@ -42,19 +42,19 @@ class LookupDataStoreTest extends \WC_Unit_Test_Case {
} }
/** /**
* @testdox `insert_data_for_product` throws an exception if a variation is passed. * @testdox `create_data_for_product` throws an exception if a variation is passed.
*/ */
public function test_insert_data_for_product_throws_if_variation_is_passed() { public function test_create_data_for_product_throws_if_variation_is_passed() {
$product = new \WC_Product_Variation(); $product = new \WC_Product_Variation();
$this->expectException( \Exception::class ); $this->expectException( \Exception::class );
$this->expectExceptionMessage( "LookupDataStore::insert_data_for_product can't be called for variations." ); $this->expectExceptionMessage( "LookupDataStore::create_data_for_product can't be called for variations." );
$this->sut->insert_data_for_product( $product ); $this->sut->create_data_for_product( $product );
} }
/** /**
* @testdox `insert_data_for_product` creates the appropriate entries for simple products, skipping custom product attributes. * @testdox `create_data_for_product` creates the appropriate entries for simple products, skipping custom product attributes.
* *
* @testWith [true] * @testWith [true]
* [false] * [false]
@ -90,7 +90,7 @@ class LookupDataStoreTest extends \WC_Unit_Test_Case {
$expected_in_stock = 0; $expected_in_stock = 0;
} }
$this->sut->insert_data_for_product( $product ); $this->sut->create_data_for_product( $product );
$expected = array( $expected = array(
array( array(
@ -133,7 +133,7 @@ class LookupDataStoreTest extends \WC_Unit_Test_Case {
} }
/** /**
* @testdox `insert_data_for_product` creates the appropriate entries for variable products. * @testdox `create_data_for_product` creates the appropriate entries for variable products.
*/ */
public function test_update_data_for_variable_product() { public function test_update_data_for_variable_product() {
$products = array(); $products = array();
@ -239,7 +239,7 @@ class LookupDataStoreTest extends \WC_Unit_Test_Case {
$products[1001] = $variation_1; $products[1001] = $variation_1;
$products[1002] = $variation_2; $products[1002] = $variation_2;
$this->sut->insert_data_for_product( $product ); $this->sut->create_data_for_product( $product );
$expected = array( $expected = array(
// Main product: one entry for each of the regular attribute values, // Main product: one entry for each of the regular attribute values,