Merge pull request #12381 from woocommerce/wc-data-meta

WC_Data Meta Improvements
This commit is contained in:
Justin Shreve 2016-11-22 04:09:42 -08:00 committed by GitHub
commit e1d2a252b7
28 changed files with 641 additions and 506 deletions

View File

@ -66,33 +66,12 @@ abstract class WC_Data {
*/
protected $cache_group = '';
/**
* Meta type. This should match up with
* the types avaiable at https://codex.wordpress.org/Function_Reference/add_metadata.
* WP defines 'post', 'user', 'comment', and 'term'.
*/
protected $meta_type = 'post';
/**
* This only needs set if you are using a custom metadata type (for example payment tokens.
* This should be the name of the field your table uses for associating meta with objects.
* For example, in payment_tokenmeta, this would be payment_token_id.
* @var string
*/
protected $object_id_field_for_meta = '';
/**
* Stores additonal meta data.
* @var array
*/
protected $meta_data = array();
/**
* Internal meta keys we don't want exposed for the object.
* @var array
*/
protected $internal_meta_keys = array();
/**
* Default constructor.
* @param int|object|array $read ID to load from the DB (optional) or already queried data.
@ -203,26 +182,6 @@ abstract class WC_Data {
return array_filter( $this->meta_data, array( $this, 'filter_null_meta' ) );
}
/**
* Internal meta keys we don't want exposed as part of meta_data. This is in
* addition to all data props with _ prefix.
* @since 2.6.0
* @return array
*/
protected function prefix_key( $key ) {
return '_' === substr( $key, 0, 1 ) ? $key : '_' . $key;
}
/**
* Internal meta keys we don't want exposed as part of meta_data. This is in
* addition to all data props with _ prefix.
* @since 2.6.0
* @return array
*/
protected function get_internal_meta_keys() {
return array_merge( array_map( array( $this, 'prefix_key' ), array_keys( $this->data ) ), $this->internal_meta_keys );
}
/**
* Get Meta Data by Key.
* @since 2.6.0
@ -330,7 +289,7 @@ abstract class WC_Data {
* @param int $mid Meta ID
*/
public function delete_meta_data_by_mid( $mid ) {
$array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), $mid );
$array_keys = array_keys( wp_list_pluck( $this->meta_data, 'id' ), $mid );
if ( $array_keys ) {
foreach ( $array_keys as $array_key ) {
$this->meta_data[ $array_key ]->value = null;
@ -338,60 +297,52 @@ abstract class WC_Data {
}
}
/**
* Callback to remove unwanted meta data.
*
* @param object $meta
* @return bool
*/
protected function exclude_internal_meta_keys( $meta ) {
return ! in_array( $meta->meta_key, $this->get_internal_meta_keys() );
}
/**
* Read Meta Data from the database. Ignore any internal properties.
*
* @since 2.6.0
* @param bool $force_read True to force a new DB read (and update cache).
*/
public function read_meta_data() {
$this->meta_data = array();
public function read_meta_data( $force_read = false ) {
$this->meta_data = array();
$cache_loaded = false;
if ( ! $this->get_id() ) {
return;
}
if ( ! empty( $this->cache_group ) ) {
$cache_key = WC_Cache_Helper::get_cache_prefix( $this->cache_group ) . $this->get_id();
$cached_meta = wp_cache_get( $cache_key, $this->cache_group );
if ( ! $this->data_store ) {
return;
}
if ( false !== $cached_meta ) {
$this->meta_data = $cached_meta;
$cache_loaded = true;
if ( ! empty( $this->cache_group ) ) {
$cache_key = WC_Cache_Helper::get_cache_prefix( $this->cache_group ) . 'object_meta_' . $this->get_id();
}
if ( ! $force_read ) {
if ( ! empty( $this->cache_group ) ) {
$cached_meta = wp_cache_get( $cache_key, $this->cache_group );
if ( false !== $cached_meta ) {
$this->meta_data = $cached_meta;
$cache_loaded = true;
}
}
}
if ( ! $cache_loaded ) {
global $wpdb;
$db_info = $this->get_db_info();
$raw_meta_data = $wpdb->get_results( $wpdb->prepare( "
SELECT " . $db_info['meta_id_field'] . ", meta_key, meta_value
FROM " . $db_info['table'] . "
WHERE " . $db_info['object_id_field'] . "=%d AND meta_key NOT LIKE 'wp\_%%' ORDER BY " . $db_info['meta_id_field'] . "
", $this->get_id() ) );
$raw_meta_data = $this->data_store->read_meta( $this );
if ( $raw_meta_data ) {
$raw_meta_data = array_filter( $raw_meta_data, array( $this, 'exclude_internal_meta_keys' ) );
foreach ( $raw_meta_data as $meta ) {
$this->meta_data[] = (object) array(
'id' => (int) $meta->{ $db_info['meta_id_field'] },
'id' => (int) $meta->meta_id,
'key' => $meta->meta_key,
'value' => maybe_unserialize( $meta->meta_value ),
);
}
}
if ( ! empty( $this->cache_group ) ) {
wp_cache_set( $cache_key, $this->meta_data, $this->cache_group );
if ( ! empty( $this->cache_group ) ) {
wp_cache_set( $cache_key, $this->meta_data, $this->cache_group );
}
}
}
}
@ -401,58 +352,27 @@ abstract class WC_Data {
* @since 2.6.0
*/
public function save_meta_data() {
if ( ! $this->data_store ) {
return;
}
foreach ( $this->meta_data as $array_key => $meta ) {
if ( is_null( $meta->value ) ) {
if ( ! empty( $meta->id ) ) {
delete_metadata_by_mid( $this->meta_type, $meta->id );
$this->data_store->delete_meta( $this, $meta );
}
} elseif ( empty( $meta->id ) ) {
$new_meta_id = add_metadata( $this->meta_type, $this->get_id(), $meta->key, $meta->value, false );
$new_meta_id = $this->data_store->add_meta( $this, $meta );
$this->meta_data[ $array_key ]->id = $new_meta_id;
} else {
update_metadata_by_mid( $this->meta_type, $meta->id, $meta->value, $meta->key );
$this->data_store->update_meta( $this, $meta );
}
}
if ( ! empty( $this->cache_group ) ) {
WC_Cache_Helper::incr_cache_prefix( $this->cache_group );
}
$this->read_meta_data();
}
/**
* Table structure is slightly different between meta types, this function will return what we need to know.
* @since 2.6.0
* @return array Array elements: table, object_id_field, meta_id_field
*/
protected function get_db_info() {
global $wpdb;
$meta_id_field = 'meta_id'; // for some reason users calls this umeta_id so we need to track this as well.
$table = $wpdb->prefix;
// If we are dealing with a type of metadata that is not a core type, the table should be prefixed.
if ( ! in_array( $this->meta_type, array( 'post', 'user', 'comment', 'term' ) ) ) {
$table .= 'woocommerce_';
}
$table .= $this->meta_type . 'meta';
$object_id_field = $this->meta_type . '_id';
// Figure out our field names.
if ( 'user' === $this->meta_type ) {
$meta_id_field = 'umeta_id';
}
if ( ! empty( $this->object_id_field_for_meta ) ) {
$object_id_field = $this->object_id_field_for_meta;
}
return array(
'table' => $table,
'object_id_field' => $object_id_field,
'meta_id_field' => $meta_id_field,
);
$this->read_meta_data( true );
}
/**

View File

@ -45,25 +45,6 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
'total_tax' => 0,
);
/**
* Data stored in meta keys, but not considered "meta" for an order.
*
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'_order_currency',
'_cart_discount',
'_cart_discount_tax',
'_order_shipping',
'_order_shipping_tax',
'_order_tax',
'_order_total',
'_order_version',
'_prices_include_tax',
'_payment_tokens',
);
/**
* Order items will be stored here, sometimes before they persist in the DB.
*
@ -86,13 +67,6 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
*/
protected $items_to_delete = array();
/**
* Internal meta type used to store order data.
*
* @var string
*/
protected $meta_type = 'post';
/**
* Stores meta in cache for future reads.
*

View File

@ -32,12 +32,6 @@ if ( ! defined( 'ABSPATH' ) ) {
'type' => '',
);
/**
* Meta type. Payment tokens are a new object type.
* @var string
*/
protected $meta_type = 'payment_token';
/**
* Initialize a payment token.
*

View File

@ -27,6 +27,12 @@ class WC_Product extends WC_Abstract_Legacy_Product {
*/
protected $post_type = 'product';
/**
* Cache group.
* @var string
*/
protected $cache_group = 'products';
/**
* Stores product data.
*
@ -83,46 +89,6 @@ class WC_Product extends WC_Abstract_Legacy_Product {
'review_count' => 0,
);
/**
* Data stored in meta keys, but not considered "meta".
*
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'_visibility',
'_sku',
'_price',
'_regular_price',
'_sale_price',
'_sale_price_dates_from',
'_sale_price_dates_to',
'total_sales',
'_tax_status',
'_tax_class',
'_manage_stock',
'_stock',
'_stock_status',
'_backorders',
'_sold_individually',
'_weight',
'_length',
'_width',
'_height',
'_upsell_ids',
'_crosssell_ids',
'_purchase_note',
'_default_attributes',
'_product_attributes',
'_virtual',
'_downloadable',
'_featured',
'_downloadable_files',
'_wc_rating_count',
'_wc_average_rating',
'_wc_review_count',
);
/**
* Supported features such as 'ajax_add_to_cart'.
*

View File

@ -68,39 +68,10 @@ class WC_Coupon extends WC_Legacy_Coupon {
const WC_COUPON_REMOVED = 201;
/**
* Internal meta type used to store coupon data.
* @since 2.7.0
* Cache group.
* @var string
*/
protected $meta_type = 'post';
/**
* Data stored in meta keys, but not considered "meta" for a coupon.
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'discount_type',
'coupon_amount',
'expiry_date',
'usage_count',
'individual_use',
'product_ids',
'exclude_product_ids',
'usage_limit',
'usage_limit_per_user',
'limit_usage_to_x_items',
'free_shipping',
'product_categories',
'exclude_product_categories',
'exclude_sale_items',
'minimum_amount',
'maximum_amount',
'customer_email',
'_used_by',
'_edit_lock',
'_edit_last',
);
protected $cache_group = 'coupons';
/**
* Coupon constructor. Loads coupon data.

View File

@ -56,60 +56,6 @@ class WC_Customer extends WC_Legacy_Customer {
'is_paying_customer' => false,
);
/**
* Data stored in meta keys, but not considered "meta".
*
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'billing_postcode',
'billing_city',
'billing_address_1',
'billing_address_2',
'billing_state',
'billing_country',
'shipping_postcode',
'shipping_city',
'shipping_address_1',
'shipping_address_2',
'shipping_state',
'shipping_country',
'paying_customer',
'last_update',
'first_name',
'last_name',
'show_admin_bar_front',
'use_ssl',
'admin_color',
'rich_editing',
'comment_shortcuts',
'dismissed_wp_pointers',
'show_welcome_panel',
'_woocommerce_persistent_cart',
'session_tokens',
'nickname',
'description',
'billing_first_name',
'billing_last_name',
'billing_company',
'billing_phone',
'billing_email',
'shipping_first_name',
'shipping_last_name',
'shipping_company',
'default_password_nag',
'primary_blog',
'source_domain',
);
/**
* Internal meta type used to store user data.
*
* @var string
*/
protected $meta_type = 'user';
/**
* Stores a password if this needs to be changed. Write-only and hidden from _data.
*

View File

@ -42,27 +42,6 @@ class WC_Order_Refund extends WC_Abstract_Order {
parent::__construct( $read );
}
/**
* Data stored in meta keys, but not considered "meta" for an order.
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'_order_currency',
'_cart_discount',
'_refund_amount',
'_refunded_by',
'_refund_reason',
'_cart_discount_tax',
'_order_shipping',
'_order_shipping_tax',
'_order_tax',
'_order_total',
'_order_version',
'_prices_include_tax',
'_payment_tokens',
);
/**
* Get internal type (post type.)
* @return string

View File

@ -17,61 +17,6 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
class WC_Order extends WC_Abstract_Order {
/**
* Data stored in meta keys, but not considered "meta" for an order.
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'_customer_user',
'_order_key',
'_order_currency',
'_billing_first_name',
'_billing_last_name',
'_billing_company',
'_billing_address_1',
'_billing_address_2',
'_billing_city',
'_billing_state',
'_billing_postcode',
'_billing_country',
'_billing_email',
'_billing_phone',
'_shipping_first_name',
'_shipping_last_name',
'_shipping_company',
'_shipping_address_1',
'_shipping_address_2',
'_shipping_city',
'_shipping_state',
'_shipping_postcode',
'_shipping_country',
'_completed_date',
'_paid_date',
'_edit_lock',
'_edit_last',
'_cart_discount',
'_cart_discount_tax',
'_order_shipping',
'_order_shipping_tax',
'_order_tax',
'_order_total',
'_payment_method',
'_payment_method_title',
'_transaction_id',
'_customer_ip_address',
'_customer_user_agent',
'_created_via',
'_order_version',
'_prices_include_tax',
'_customer_note',
'_date_completed',
'_date_paid',
'_payment_tokens',
'_billing_address_index',
'_shipping_address_index'
);
/**
* Stores data about status changes so relevant hooks can be fired.
* @var bool|array

View File

@ -43,7 +43,6 @@ class WC_Product_Variation extends WC_Product_Simple {
* @param mixed $product
*/
public function __construct( $product = 0 ) {
$this->internal_meta_keys[] = '_variation_description';
parent::__construct( $product );
}
@ -285,16 +284,6 @@ class WC_Product_Variation extends WC_Product_Simple {
|--------------------------------------------------------------------------
*/
/**
* Callback to remove unwanted meta data.
*
* @param object $meta
* @return bool false if excluded.
*/
protected function exclude_internal_meta_keys( $meta ) {
return ! in_array( $meta->meta_key, $this->get_internal_meta_keys() ) && 0 !== stripos( $meta->meta_key, 'attribute_' );
}
/**
* Set the parent data array for this variation.
*

View File

@ -10,7 +10,34 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooThemes
*/
abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_Data_Store, WC_Abstract_Order_Data_Store_Interface {
abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Data_Store, WC_Abstract_Order_Data_Store_Interface {
/**
* Internal meta type used to store order data.
*
* @var string
*/
protected $meta_type = 'post';
/**
* Data stored in meta keys, but not considered "meta" for an order.
*
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'_order_currency',
'_cart_discount',
'_cart_discount_tax',
'_order_shipping',
'_order_shipping_tax',
'_order_tax',
'_order_total',
'_order_version',
'_prices_include_tax',
'_payment_tokens',
);
/*
|--------------------------------------------------------------------------
| CRUD Methods

View File

@ -10,7 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooCommerce
*/
abstract class Abstract_WC_Order_Item_Type_Data_Store implements WC_Object_Data_Store {
abstract class Abstract_WC_Order_Item_Type_Data_Store extends WC_Data_Store_WP implements WC_Object_Data_Store {
/**
* Create a new order item in the database.
*

View File

@ -10,7 +10,42 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooThemes
*/
class WC_Coupon_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Coupon_Data_Store, WC_Object_Data_Store {
class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Data_Store, WC_Object_Data_Store {
/**
* Internal meta type used to store coupon data.
* @since 2.7.0
* @var string
*/
protected $meta_type = 'post';
/**
* Data stored in meta keys, but not considered "meta" for a coupon.
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'discount_type',
'coupon_amount',
'expiry_date',
'usage_count',
'individual_use',
'product_ids',
'exclude_product_ids',
'usage_limit',
'usage_limit_per_user',
'limit_usage_to_x_items',
'free_shipping',
'product_categories',
'exclude_product_categories',
'exclude_sale_items',
'minimum_amount',
'maximum_amount',
'customer_email',
'_used_by',
'_edit_lock',
'_edit_last',
);
/**
* Method to create a new coupon in the database.

View File

@ -10,7 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooThemes
*/
class WC_Customer_Data_Store_Session implements WC_Customer_Data_Store_Interface, WC_Object_Data_Store {
class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Customer_Data_Store_Interface, WC_Object_Data_Store {
/**
* Keys which are also stored in a session (so we can make sure they get updated...)

View File

@ -10,7 +10,58 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooThemes
*/
class WC_Customer_Data_Store implements WC_Customer_Data_Store_Interface, WC_Object_Data_Store {
class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Data_Store_Interface, WC_Object_Data_Store {
/**
* Data stored in meta keys, but not considered "meta".
*
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'billing_postcode',
'billing_city',
'billing_address_1',
'billing_address_2',
'billing_state',
'billing_country',
'shipping_postcode',
'shipping_city',
'shipping_address_1',
'shipping_address_2',
'shipping_state',
'shipping_country',
'paying_customer',
'last_update',
'first_name',
'last_name',
'show_admin_bar_front',
'use_ssl',
'admin_color',
'rich_editing',
'comment_shortcuts',
'dismissed_wp_pointers',
'show_welcome_panel',
'_woocommerce_persistent_cart',
'session_tokens',
'nickname',
'description',
'billing_first_name',
'billing_last_name',
'billing_company',
'billing_phone',
'billing_email',
'shipping_first_name',
'wptests_capabilities',
'wptests_user_level',
);
/**
* Internal meta type used to store user data.
*
* @var string
*/
protected $meta_type = 'user';
/**
* Method to create a new customer in the database.

View File

@ -1,31 +0,0 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Shared logic for post/CPT data stores.
*
* @version 2.7.0
* @category Class
* @author WooThemes
*/
class WC_Data_Store_CPT {
/**
* Get and store terms from a taxonomy.
*
* @since 2.7.0
* @param WC_Product
* @param string $taxonomy Taxonomy name e.g. product_cat
* @return array of terms
*/
protected function get_term_ids( $product, $taxonomy ) {
$terms = get_the_terms( $product->get_id(), $taxonomy );
if ( false === $terms || is_wp_error( $terms ) ) {
return array();
}
return wp_list_pluck( $terms, 'term_id' );
}
}

View File

@ -0,0 +1,167 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Shared logic for WP based data.
* Contains functions like meta handling for all default data stores.
* Your own data store doesn't need to use WC_Data_Store_WP -- you can write
* your own meta handling functions.
*
* @version 2.7.0
* @category Class
* @author WooThemes
*/
class WC_Data_Store_WP {
/**
* Meta type. This should match up with
* the types avaiable at https://codex.wordpress.org/Function_Reference/add_metadata.
* WP defines 'post', 'user', 'comment', and 'term'.
*/
protected $meta_type = 'post';
/**
* This only needs set if you are using a custom metadata type (for example payment tokens.
* This should be the name of the field your table uses for associating meta with objects.
* For example, in payment_tokenmeta, this would be payment_token_id.
* @var string
*/
protected $object_id_field_for_meta = '';
/**
* Data stored in meta keys, but not considered "meta" for an object.
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array();
/**
* Get and store terms from a taxonomy.
*
* @since 2.7.0
* @param WC_Data
* @param string $taxonomy Taxonomy name e.g. product_cat
* @return array of terms
*/
protected function get_term_ids( $object, $taxonomy ) {
$terms = get_the_terms( $object->get_id(), $taxonomy );
if ( false === $terms || is_wp_error( $terms ) ) {
return array();
}
return wp_list_pluck( $terms, 'term_id' );
}
/**
* Returns an array of meta for an object.
*
* @since 2.7.0
* @param WC_Data
* @return array
*/
public function read_meta( &$object ) {
global $wpdb;
$db_info = $this->get_db_info();
$raw_meta_data = $wpdb->get_results( $wpdb->prepare( "
SELECT " . $db_info['meta_id_field'] . " as meta_id, meta_key, meta_value
FROM " . $db_info['table'] . "
WHERE " . $db_info['object_id_field'] . "=%d AND meta_key NOT LIKE 'wp\_%%' ORDER BY " . $db_info['meta_id_field'] . "
", $object->get_id() ) );
$this->internal_meta_keys = array_merge( array_map( array( $this, 'prefix_key' ), $object->get_data_keys() ), $this->internal_meta_keys );
return array_filter( $raw_meta_data, array( $this, 'exclude_internal_meta_keys' ) );
}
/**
* Deletes meta based on meta ID.
*
* @since 2.7.0
* @param WC_Data
* @param stdClass (containing at least ->id)
* @return array
*/
public function delete_meta( &$object, $meta ) {
delete_metadata_by_mid( $this->meta_type, $meta->id );
}
/**
* Add new piece of meta.
*
* @since 2.7.0
* @param WC_Data
* @param stdClass (containing ->key and ->value)
* @return meta ID
*/
public function add_meta( &$object, $meta ) {
return add_metadata( $this->meta_type, $object->get_id(), $meta->key, $meta->value, false );
}
/**
* Update meta.
*
* @since 2.7.0
* @param WC_Data
* @param stdClass (containing ->id, ->key and ->value)
*/
public function update_meta( &$object, $meta ) {
update_metadata_by_mid( $this->meta_type, $meta->id, $meta->value, $meta->key );
}
/**
* Table structure is slightly different between meta types, this function will return what we need to know.
*
* @since 2.7.0
* @return array Array elements: table, object_id_field, meta_id_field
*/
protected function get_db_info() {
global $wpdb;
$meta_id_field = 'meta_id'; // for some reason users calls this umeta_id so we need to track this as well.
$table = $wpdb->prefix;
// If we are dealing with a type of metadata that is not a core type, the table should be prefixed.
if ( ! in_array( $this->meta_type, array( 'post', 'user', 'comment', 'term' ) ) ) {
$table .= 'woocommerce_';
}
$table .= $this->meta_type . 'meta';
$object_id_field = $this->meta_type . '_id';
// Figure out our field names.
if ( 'user' === $this->meta_type ) {
$meta_id_field = 'umeta_id';
}
if ( ! empty( $this->object_id_field_for_meta ) ) {
$object_id_field = $this->object_id_field_for_meta;
}
return array(
'table' => $table,
'object_id_field' => $object_id_field,
'meta_id_field' => $meta_id_field,
);
}
/**
* Internal meta keys we don't want exposed as part of meta_data. This is in
* addition to all data props with _ prefix.
* @since 2.6.0
* @return array
*/
protected function prefix_key( $key ) {
return '_' === substr( $key, 0, 1 ) ? $key : '_' . $key;
}
/**
* Callback to remove unwanted meta data.
*
* @param object $meta
* @return bool
*/
protected function exclude_internal_meta_keys( $meta ) {
return ! in_array( $meta->meta_key, $this->internal_meta_keys );
}
}

View File

@ -12,6 +12,61 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implements WC_Object_Data_Store, WC_Order_Data_Store_Interface {
/**
* Data stored in meta keys, but not considered "meta" for an order.
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'_customer_user',
'_order_key',
'_order_currency',
'_billing_first_name',
'_billing_last_name',
'_billing_company',
'_billing_address_1',
'_billing_address_2',
'_billing_city',
'_billing_state',
'_billing_postcode',
'_billing_country',
'_billing_email',
'_billing_phone',
'_shipping_first_name',
'_shipping_last_name',
'_shipping_company',
'_shipping_address_1',
'_shipping_address_2',
'_shipping_city',
'_shipping_state',
'_shipping_postcode',
'_shipping_country',
'_completed_date',
'_paid_date',
'_edit_lock',
'_edit_last',
'_cart_discount',
'_cart_discount_tax',
'_order_shipping',
'_order_shipping_tax',
'_order_tax',
'_order_total',
'_payment_method',
'_payment_method_title',
'_transaction_id',
'_customer_ip_address',
'_customer_user_agent',
'_created_via',
'_order_version',
'_prices_include_tax',
'_customer_note',
'_date_completed',
'_date_paid',
'_payment_tokens',
'_billing_address_index',
'_shipping_address_index'
);
/**
* Method to create a new order in the database.
* @param WC_Order $order

View File

@ -12,6 +12,27 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
class WC_Order_Refund_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implements WC_Object_Data_Store, WC_Order_Refund_Data_Store_Interface {
/**
* Data stored in meta keys, but not considered "meta" for an order.
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'_order_currency',
'_cart_discount',
'_refund_amount',
'_refunded_by',
'_refund_reason',
'_cart_discount_tax',
'_order_shipping',
'_order_shipping_tax',
'_order_tax',
'_order_total',
'_order_version',
'_prices_include_tax',
'_payment_tokens',
);
/**
* Read refund data. Can be overridden by child classes to load other props.
*

View File

@ -10,7 +10,13 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooThemes
*/
class WC_Payment_Token_Data_Store implements WC_Payment_Token_Data_Store_Interface, WC_Object_Data_Store {
class WC_Payment_Token_Data_Store extends WC_Data_Store_WP implements WC_Payment_Token_Data_Store_Interface, WC_Object_Data_Store {
/**
* Meta type. Payment tokens are a new object type.
* @var string
*/
protected $meta_type = 'payment_token';
/**
* Create a new payment token in the database.

View File

@ -10,7 +10,48 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooThemes
*/
class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_Data_Store, WC_Product_Data_Store_Interface {
class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Data_Store, WC_Product_Data_Store_Interface {
/**
* Data stored in meta keys, but not considered "meta".
*
* @since 2.7.0
* @var array
*/
protected $internal_meta_keys = array(
'_visibility',
'_sku',
'_price',
'_regular_price',
'_sale_price',
'_sale_price_dates_from',
'_sale_price_dates_to',
'total_sales',
'_tax_status',
'_tax_class',
'_manage_stock',
'_stock',
'_stock_status',
'_backorders',
'_sold_individually',
'_weight',
'_length',
'_width',
'_height',
'_upsell_ids',
'_crosssell_ids',
'_purchase_note',
'_default_attributes',
'_product_attributes',
'_virtual',
'_downloadable',
'_featured',
'_downloadable_files',
'_wc_rating_count',
'_wc_average_rating',
'_wc_review_count',
'_variation_description',
);
/**
* If we have already saved our extra data, don't do automatic / default handling.

View File

@ -11,6 +11,17 @@ if ( ! defined( 'ABSPATH' ) ) {
* @author WooThemes
*/
class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT implements WC_Object_Data_Store {
/**
* Callback to remove unwanted meta data.
*
* @param object $meta
* @return bool false if excluded.
*/
protected function exclude_internal_meta_keys( $meta ) {
return ! in_array( $meta->meta_key, $this->internal_meta_keys ) && 0 !== stripos( $meta->meta_key, 'attribute_' );
}
/*
|--------------------------------------------------------------------------
| CRUD Methods

View File

@ -10,7 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooCommerce
*/
class WC_Shipping_Zone_Data_Store implements WC_Shipping_Zone_Data_Store_Interface, WC_Object_Data_Store {
class WC_Shipping_Zone_Data_Store extends WC_Data_Store_WP implements WC_Shipping_Zone_Data_Store_Interface, WC_Object_Data_Store {
/**
* Method to create a new shipping zone.

View File

@ -11,7 +11,6 @@ if ( ! defined( 'ABSPATH' ) ) {
* @author WooThemes
*/
interface WC_Object_Data_Store {
/**
* Method to create a new record of a WC_Data based object.
* @param WC_Data
@ -37,4 +36,34 @@ interface WC_Object_Data_Store {
* @return bool result
*/
public function delete( &$data, $args = array() );
/**
* Returns an array of meta for an object.
* @param WC_Data
* @return array
*/
public function read_meta( &$data );
/**
* Deletes meta based on meta ID.
* @param WC_Data
* @param stdClass (containing at least ->id)
* @return array
*/
public function delete_meta( &$data, $meta );
/**
* Add new piece of meta.
* @param WC_Data
* @param stdClass (containing ->key and ->value)
* @return meta ID
*/
public function add_meta( &$data, $meta );
/**
* Update meta.
* @param WC_Data
* @param stdClass (containing ->id, ->key and ->value)
*/
public function update_meta( &$data, $meta );
}

View File

@ -17,6 +17,10 @@ class WC_Dummy_Data_Store_CPT implements WC_Object_Data_Store {
public function read( &$data ) { }
public function update( &$data ) { }
public function delete( &$data, $args = array() ) { }
public function read_meta( &$data ) { }
public function delete_meta( &$data, $meta ) { }
public function add_meta( &$data, $meta ) { }
public function update_meta( &$data, $meta ) { }
}
/**
@ -33,4 +37,8 @@ class WC_Dummy_Data_Store_Custom_Table implements WC_Object_Data_Store {
public function read( &$data ) { }
public function update( &$data ) { }
public function delete( &$data, $args = array() ) { }
public function read_meta( &$data ) { }
public function delete_meta( &$data, $meta ) { }
public function add_meta( &$data, $meta ) { }
public function update_meta( &$data, $meta ) { }
}

View File

@ -1,26 +1,13 @@
<?php
/**
* Used for exposing and testing the various Abstract WC_Data methods.
*/
class WC_Mock_WC_Data extends WC_Data {
class WC_Mock_WC_Data_Store extends WC_Data_Store_WP implements WC_Object_Data_Store {
/**
* Data array
*/
protected $data = array(
'content' => '',
'bool_value' => false,
);
// see WC_Data
protected $cache_group = '';
protected $meta_type = 'post';
protected $object_id_field_for_meta = '';
protected $internal_meta_keys = array();
/*
|--------------------------------------------------------------------------
| Setters for internal WC_Data properties.
| Setters for internal properties.
|--------------------------------------------------------------------------
| Normally we wouldn't want to be able to change this once the class is defined,
| but to make testing different types of meta/storage, we should be able to
@ -44,6 +31,88 @@ class WC_Mock_WC_Data extends WC_Data {
$this->object_id_field_for_meta = $object_id_field;
}
public function create( &$object ) {
if ( 'user' === $this->meta_type ) {
$content_id = wc_create_new_customer( $object->get_content(), 'username-' . time(), 'hunter2' );
} else {
$content_id = wp_insert_post( array( 'post_title' => $object->get_content() ) );
}
if ( $content_id ) {
$object->set_id( $content_id );
}
$object->apply_changes();
}
/**
* Simple read.
*/
public function read( &$object ) {
$object->set_defaults();
$id = $object->get_id();
if ( 'user' === $this->meta_type ) {
if ( empty( $id ) || ! ( $user_object = get_userdata( $id ) ) ) {
return;
}
$object->set_content( $user_object->user_email );
} else {
if ( empty( $id ) || ! ( $post_object = get_post( $id ) ) ) {
return;
}
$object->set_content( $post_object->post_title );
}
$object->read_meta_data();
$object->set_object_read( true );
}
/**
* Simple update.
*/
public function update( &$object ) {
global $wpdb;
$content_id = $object->get_id();
if ( 'user' === $this->meta_type ) {
wp_update_user( array( 'ID' => $customer_id, 'user_email' => $object->get_content() ) );
} else {
wp_update_post( array( 'ID' => $content_id, 'post_title' => $object->get_content() ) );
}
}
/**
* Simple delete.
*/
public function delete( &$object, $args = array() ) {
if ( 'user' === $this->meta_type ) {
wp_delete_user( $object->get_id() );
} else {
wp_delete_post( $object->get_id() );
}
$object->set_id( 0 );
}
}
/**
* Used for exposing and testing the various Abstract WC_Data methods.
*/
class WC_Mock_WC_Data extends WC_Data {
/**
* Data array
*/
protected $data = array(
'content' => '',
'bool_value' => false,
);
// see WC_Data
protected $cache_group = '';
public $data_store;
/*
|--------------------------------------------------------------------------
| Abstract methods.
@ -58,43 +127,57 @@ class WC_Mock_WC_Data extends WC_Data {
public function __construct( $id = '' ) {
parent::__construct();
if ( ! empty( $id ) ) {
$this->read( $id );
$this->set_id( $id );
} else {
$this->set_object_read( true );
}
$this->data_store = new WC_Mock_WC_Data_Store;
if ( $this->get_id() > 0 ) {
$this->data_store->read( $this );
}
}
/**
* Simple get content.
*
* @param string $context
* @return string
*/
public function get_content() {
return $this->data['content'];
public function get_content( $context = 'view' ) {
return $this->get_prop( 'content', $context );
}
/**
* Simple set content.
*
* @param string $content
*/
public function set_content( $content ) {
$this->data['content'] = $content;
$this->set_prop('content', $content );
}
/**
* Simple get bool value.
*
* @param string $context
* @return bool
*/
public function get_bool_value() {
return $this->data['bool_value'];
public function get_bool_value( $context = 'view' ) {
return $this->get_prop( 'bool_value', $context );
}
/**
* Simple set bool value.
*
* @return bool
*/
public function set_bool_value( $value ) {
if ( ! is_bool( $value ) ) {
$this->error( 'invalid_bool_value', 'O noes' );
}
$this->data['bool_value'] = $value;
$this->set_prop( 'bool_value', $value );
}
/**
@ -110,77 +193,18 @@ class WC_Mock_WC_Data extends WC_Data {
);
}
/**
* Simple create.
*/
public function create() {
if ( 'user' === $this->meta_type ) {
$content_id = wc_create_new_customer( $this->get_content(), 'username-' . time(), 'hunter2' );
} else {
$content_id = wp_insert_post( array( 'post_title' => $this->get_content() ) );
}
if ( $content_id ) {
$this->set_id( $content_id );
}
}
/**
* Simple read.
*/
public function read( $id ) {
$this->set_defaults();
if ( 'user' === $this->meta_type ) {
if ( empty( $id ) || ! ( $user_object = get_userdata( $id ) ) ) {
return;
}
$this->set_id( absint( $user_object->ID ) );
$this->set_content( $user_object->user_email );
} else {
if ( empty( $id ) || ! ( $post_object = get_post( $id ) ) ) {
return;
}
$this->set_id( absint( $post_object->ID ) );
$this->set_content( $post_object->post_title );
}
$this->read_meta_data();
}
/**
* Simple update.
*/
public function update() {
global $wpdb;
$content_id = $this->get_id();
if ( 'user' === $this->meta_type ) {
wp_update_user( array( 'ID' => $customer_id, 'user_email' => $this->get_content() ) );
} else {
wp_update_post( array( 'ID' => $content_id, 'post_title' => $this->get_content() ) );
}
}
/**
* Simple delete.
*/
public function delete( $force_delete = false ) {
if ( 'user' === $this->meta_type ) {
wp_delete_user( $this->get_id() );
} else {
wp_delete_post( $this->get_id() );
}
}
/**
* Simple save.
*/
public function save() {
if ( ! $this->get_id() ) {
$this->create();
} else {
$this->update();
if ( $this->data_store ) {
if ( $this->get_id() ) {
$this->data_store->update( $this );
} else {
$this->data_store->create( $this );
}
}
$this->save_meta_data();
return $this->get_id();
}
}

View File

@ -57,7 +57,8 @@ class WC_Tests_Coupon_Data extends WC_Unit_Test_Case {
$this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, $legacy_keys );
$coupon = WC_Helper_Coupon::create_coupon();
add_post_meta( $coupon->get_id(), 'test_coupon_field', 'testing', true );
$coupon->add_meta_data( 'test_coupon_field', 'testing', true );
$coupon->save_meta_data();
$coupon = new WC_Coupon( $coupon->get_id() );
$this->assertEquals( $coupon->get_id(), $coupon->id );
@ -194,7 +195,9 @@ class WC_Tests_Coupon_Data extends WC_Unit_Test_Case {
$coupon = WC_Helper_Coupon::create_coupon();
$coupon_id = $coupon->get_id();
$meta_value = time() . '-custom-value';
add_post_meta( $coupon_id, 'test_coupon_field', $meta_value, true );
$coupon->add_meta_data( 'test_coupon_field', $meta_value, true );
$coupon->save_meta_data();
$coupon = new WC_Coupon( $coupon_id );
$custom_fields = $coupon->get_meta_data();
$this->assertCount( 1, $custom_fields );

View File

@ -11,6 +11,8 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
*/
public function create_test_post() {
$object = new WC_Mock_WC_Data();
$object->data_store->set_meta_type( 'post' );
$object->data_store->set_object_id_field( '' );
$object->set_content( 'testing' );
$object->save();
return $object;
@ -21,8 +23,8 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
*/
public function create_test_user() {
$object = new WC_Mock_WC_Data();
$object->set_meta_type( 'user' );
$object->set_object_id_field( 'user_id' );
$object->data_store->set_meta_type( 'user' );
$object->data_store->set_object_id_field( 'user_id' );
$object->set_content( 'testing@woo.dev' );
$object->save();
return $object;
@ -77,10 +79,10 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
function test_get_meta_data() {
$object = $this->create_test_post();
$object_id = $object->get_id();
add_metadata( 'post', $object_id, 'test_meta_key', 'val1', true );
add_metadata( 'post', $object_id, 'test_multi_meta_key', 'val2' );
add_metadata( 'post', $object_id, 'test_multi_meta_key', 'val3' );
$object->read( $object_id );
$object->add_meta_data( 'test_meta_key', 'val1', true );
$object->add_meta_data( 'test_multi_meta_key', 'val2' );
$object->add_meta_data( 'test_multi_meta_key', 'val3' );
$object->save_meta_data();
$meta_data = $object->get_meta_data();
$i = 1;
@ -98,10 +100,11 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
function test_get_meta() {
$object = $this->create_test_post();
$object_id = $object->get_id();
add_metadata( 'post', $object_id, 'test_meta_key', 'val1', true );
add_metadata( 'post', $object_id, 'test_multi_meta_key', 'val2' );
add_metadata( 'post', $object_id, 'test_multi_meta_key', 'val3' );
$object->read( $object_id );
$object->add_meta_data( 'test_meta_key', 'val1', true );
$object->add_meta_data( 'test_multi_meta_key', 'val2' );
$object->add_meta_data( 'test_multi_meta_key', 'val3' );
$object->save_meta_data();
$object = new WC_Mock_WC_Data( $object_id );
// test single meta key
$single_meta = $object->get_meta( 'test_meta_key' );
@ -126,7 +129,7 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
$object_id = $object->get_id();
add_metadata( 'post', $object_id, 'test_meta_key', 'val1', true );
add_metadata( 'post', $object_id, 'test_meta_key_2', 'val2', true );
$object->read( $object_id );
$object = new WC_Mock_WC_Data( $object_id );
$metadata = array();
$raw_metadata = $wpdb->get_results( $wpdb->prepare( "
@ -169,7 +172,7 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
$object = $this->create_test_post();
$object_id = $object->get_id();
add_metadata( 'post', $object_id, 'test_meta_key', 'val1', true );
$object->read( $object_id );
$object = new WC_Mock_WC_Data( $object_id );
$this->assertEquals( 'val1', $object->get_meta( 'test_meta_key' ) );
@ -191,7 +194,7 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
$object = $this->create_test_post();
$object_id = $object->get_id();
add_metadata( 'post', $object_id, 'test_meta_key', 'val1', true );
$object->read( $object_id );
$object = new WC_Mock_WC_Data( $object_id );
$this->assertEquals( 'val1', $object->get_meta( 'test_meta_key' ) );
@ -208,9 +211,10 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
global $wpdb;
$object = $this->create_test_post();
$object_id = $object->get_id();
add_metadata( 'post', $object_id, 'test_meta_key', 'val1', true );
add_metadata( 'post', $object_id, 'test_meta_key_2', 'val2', true );
$object->read( $object_id );
$object->add_meta_data( 'test_meta_key', 'val1', true );
$object->add_meta_data( 'test_meta_key_2', 'val2', true );
$object->save_meta_data();
$object = new WC_Mock_WC_Data( $object_id );
$raw_metadata = $wpdb->get_results( $wpdb->prepare( "
SELECT meta_id, meta_key, meta_value
@ -222,7 +226,7 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
$object->update_meta_data( 'test_meta_key_2', 'updated_value', $raw_metadata[1]->meta_id );
$object->save();
$object->read( $object_id ); // rereads from the DB
$object = new WC_Mock_WC_Data( $object_id ); // rereads from the DB
$this->assertEmpty( $object->get_meta( 'test_meta_key' ) );
$this->assertEquals( 'updated_value', $object->get_meta( 'test_meta_key_2' ) );
@ -234,9 +238,9 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case {
function test_usermeta() {
$object = $this->create_test_user();
$object_id = $object->get_id();
add_metadata( 'user', $object_id, 'test_meta_key', 'val1', true );
add_metadata( 'user', $object_id, 'test_meta_key_2', 'val2', true );
$object->read( $object_id );
$object->add_meta_data( 'test_meta_key', 'val1', true );
$object->add_meta_data( 'test_meta_key_2', 'val2', true );
$object->save_meta_data();
$this->assertEquals( 'val1', $object->get_meta( 'test_meta_key' ) );
$this->assertEquals( 'val2', $object->get_meta( 'test_meta_key_2' ) );

View File

@ -301,7 +301,7 @@ final class WooCommerce {
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/class-wc-order-item-type-data-store-interface.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/class-wc-order-item-data-store-interface.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/class-wc-order-item-product-data-store-interface.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-data-store-wp.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-coupon-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-data-store-cpt.php' );
include_once( WC_ABSPATH . 'includes/data-stores/class-wc-product-grouped-data-store-cpt.php' );