<?php
/**
* Extends WooCommerce product with additional fields.
*
* @package tripe\StripeTaxForWooCommerce\WooCommerce
*/
namespace Stripe\StripeTaxForWooCommerce\WooCommerce;
// Exit if script started not from WordPress.
use Stripe\StripeTaxForWooCommerce\Stripe\StripeTaxPluginHelper;
use Stripe\StripeTaxForWooCommerce\Stripe\Validate;
use Stripe\StripeTaxForWooCommerce\WordPress\Options;
use WC_Data;
defined( 'ABSPATH' ) || exit;
/**
* WooCommerce Product class replacement.
*/
class ExtendedProduct {
/**
* Database table name for storing additional WooCommerce product fields
*
* @var string
*/
public const TABLE_NAME = STRIPE_TAX_FOR_WOOCOMMERCE_DB_PREFIX . 'products';
/**
* WooCommerce Product id.
*
* @var int
*/
protected $product_id;
/**
* Constructor for ExtendedProduct.
*
* @param int $product_id Product id.
*/
public function __construct( int $product_id = 0 ) {
$this->product_id = $product_id;
}
/**
* Gets WooCommerce Product id.
*/
protected function get_product_id() {
return $this->product_id;
}
/**
* Gets additional WooCommerce Product fields from WordPress object cache to reduce database requests.
*
* @param int|null $id Product id, null by default.
*
* @return array
*/
protected function get_from_object_cache( $id = null ): array {
$product_id = $id ?? (int) $this->get_product_id();
$products = wp_cache_get( 'products', 'stripe-tax-for-woocommerce' );
if ( ! is_array( $products ) || ! array_key_exists( $product_id, $products ) ) {
return array();
}
return $products[ $product_id ];
}
/**
* Sets additional WooCommerce Product fields into WordPress object cache.
*
* @param array $product Array of additional WooCommerce Product fields to store.
*/
protected function set_into_object_cache( array $product ) {
$products = wp_cache_get( 'products', 'stripe-tax-for-woocommerce' );
if ( ! is_array( $products ) ) {
$products = array();
}
$products [ $this->product_id ] = array(
'tax_code' => $product['tax_code'] ?? '',
);
wp_cache_set( 'products', $products, 'stripe-tax-for-woocommerce', 2 * HOUR_IN_SECONDS );
}
/**
* Populate tax code from submitted form.
*
* @param WC_Data $wc_data WC_Data object. Usually here will be WooCommerce Product object.
*
* @return string
* @throws \Exception In case of invalid tax code entered.
*/
public function get_on_save_post_parameter_tax_code( WC_Data $wc_data ): string {
$tax_code = '';
$nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
wp_verify_nonce( $nonce );
if ( isset( $_POST['_stripe_tax_for_woocommerce_tax_code'] ) ) {
$tax_code = sanitize_text_field( wp_unslash( $_POST['_stripe_tax_for_woocommerce_tax_code'] ) );
/* @phpstan-ignore-next-line */
} elseif ( $wc_data->get_parent_id() ) {
/* @phpstan-ignore-next-line */
$tax_code = $this->get_extended_product( $wc_data->get_parent_id() )['tax_code'];
} else {
$product = $this->get_extended_product();
if ( ! empty( $product ) && ! empty( $product['tax_code'] ) ) {
$tax_code = sanitize_text_field( wp_unslash( $product['tax_code'] ) );
}
}
if ( '' !== $tax_code && 'stfwc_inherit' !== $tax_code ) { // Empty and "stfwc_inherit" "tax_code" values are valid and in this situation means "using global Stripe Tax Settings parameter".
Validate::validate_tax_code( $tax_code, Options::get_live_mode_key() );
}
return $tax_code;
}
/**
* Gets additional WooCommerce Product fields from database.
*
* @param int|null $id Product id, null by default.
*
* @return array
*/
protected function get_from_db( $id = null ) {
global $wpdb;
$product_id = $id ?? $this->product_id;
if ( 'product_variation' === (string) get_post_type( $product_id ) ) {
$variation_tax_class = (string) get_post_meta( $product_id, '_tax_class', true );
if ( '' === $variation_tax_class || 'parent' === $variation_tax_class ) {
$product_id = (int) wp_get_post_parent_id( $product_id );
}
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
$result = $wpdb->get_row(
$wpdb->prepare(
'SELECT %i FROM %i WHERE %i = %d',
array(
'tax_code',
static::TABLE_NAME,
'product_id',
$product_id,
)
),
ARRAY_A
);
return is_array( $result ) ? $result : array();
}
/**
* Store additional WooCommerce Product fields to database.
*
* @param array $product WooCommerce Product.
*/
public function set_into_db( array $product ) {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query(
$wpdb->prepare(
'INSERT INTO %i ( %i, %i ) VALUES ( %d, %s ) ON DUPLICATE KEY UPDATE %i = %s',
array(
static::TABLE_NAME,
'product_id',
'tax_code',
(int) $this->get_product_id(),
$product['tax_code'] ?? '',
'tax_code',
$product['tax_code'] ?? '',
)
)
);
}
/**
* Load additional WooCommerce Product fields from WordPress object cache or database.
*
* @param int|null $id Product id, null by default.
*
* @param bool $skip_object_cache Skip object cache.
*/
public function get_extended_product( $id = null, $skip_object_cache = false ): array {
if ( ! $skip_object_cache ) {
$product = $this->get_from_object_cache( $id );
if ( $product ) {
return $product;
}
}
$product = $this->get_from_db( $id );
if ( $product ) {
$this->set_into_object_cache( $product );
return $product;
}
return array(
'tax_code' => '',
);
}
/**
* Save additional WooCommerce Product fields into WordPress object cache and database.
*
* @param array $product WooCommerce Product.
*/
public function save_extended_product( $product ) {
$new_product = array(
'tax_code' => $product['tax_code'] ?? '',
);
if ( 'stfwc_inherit' === $new_product['tax_code'] ) {
$new_product['tax_code'] = '';
}
$this->set_into_db( $new_product );
$this->set_into_object_cache( $new_product );
}
}