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 ) {
$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 );

View File

@ -14,6 +14,15 @@ defined( 'ABSPATH' ) || exit;
*/
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.
*
@ -95,13 +104,13 @@ AND table_name = %s;',
}
/**
* Get the name of the lookup table.
*
* @return string
*/
public function get_lookup_table_name() {
return $this->lookup_table_name;
}
* Get the name of the lookup table.
*
* @return string
*/
public function get_lookup_table_name() {
return $this->lookup_table_name;
}
/**
* Insert/update the appropriate lookup table entries for a new or modified product or variation.
@ -120,20 +129,82 @@ AND table_name = %s;',
$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 ) ) {
$this->delete_lookup_table_entries_for( $product->get_id() );
if ( $this->is_variation( $product ) ) {
$this->insert_data_for_variation( $product );
// No changeset at all means that the product is new.
return self::ACTION_INSERT;
}
$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 {
$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;
}
$this->delete_lookup_table_entries_for( $product_id );
$this->delete_data_for( $product_id );
}
/**
* Insert the lookup data for a given product or variation.
* If a variable product is passed the information is created for all of its variations.
* Create the lookup data for a given product, if a variable product is passed
* 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.
* @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 ) ) {
$product = WC()->call_function( 'wc_get_product', $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 {
$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.
*/
private function delete_lookup_table_entries_for( int $product_id ) {
private function delete_data_for( int $product_id ) {
global $wpdb;
// 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.
*/
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 );
$has_stock = $product->is_in_stock();
$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.
*/
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 );
$variation_attributes_data = array_filter(
$product_attributes_data,
@ -266,7 +348,7 @@ AND table_name = %s;',
*
* @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() );
$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 {
public $passed_products = array();
public function insert_data_for_product( $product ) {
public function create_data_for_product( $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();
$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]
* [false]
@ -90,7 +90,7 @@ class LookupDataStoreTest extends \WC_Unit_Test_Case {
$expected_in_stock = 0;
}
$this->sut->insert_data_for_product( $product );
$this->sut->create_data_for_product( $product );
$expected = 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() {
$products = array();
@ -239,7 +239,7 @@ class LookupDataStoreTest extends \WC_Unit_Test_Case {
$products[1001] = $variation_1;
$products[1002] = $variation_2;
$this->sut->insert_data_for_product( $product );
$this->sut->create_data_for_product( $product );
$expected = array(
// Main product: one entry for each of the regular attribute values,