Product CRUD improvements (#12359)

* args is not used any more - remove todo

* Added test for attributes

* wc_get_price_excluding_tax usage

* parent usage

* Fix rating counts

* Test fixes

* Cleanup after tests

* Make sure status transition code runs even during API calls, not just in admin.

* Default visibility

* Fix attribute setting in API

* Use get name instead of get title

* variation id usage

* Improved cross sell templates

* variation_data

* Grouped product sync

* Notices

* Sync is not needed in API

* Delete

* Rename interfaces

* Update counts in data store
This commit is contained in:
Mike Jolley 2016-11-16 12:17:00 +00:00 committed by GitHub
parent d8fcfcefb8
commit 599a2ad296
58 changed files with 518 additions and 574 deletions

View File

@ -101,6 +101,16 @@ abstract class WC_Data {
$this->default_data = $this->data;
}
/**
* Get the data store.
*
* @since 2.7.0
* @return object
*/
public function get_data_store() {
return $this->data_store;
}
/**
* Returns the unique ID for this object.
* @return int

View File

@ -153,8 +153,8 @@ abstract class WC_Abstract_Legacy_Order extends WC_Data {
if ( $product->backorders_require_notification() && $product->is_on_backorder( $args['qty'] ) ) {
$item->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $args['qty'] - max( 0, $product->get_stock_quantity() ), true );
}
$args['subtotal'] = $args['subtotal'] ? $args['subtotal'] : $product->get_price_excluding_tax( $args['qty'] );
$args['total'] = $args['total'] ? $args['total'] : $product->get_price_excluding_tax( $args['qty'] );
$args['subtotal'] = $args['subtotal'] ? $args['subtotal'] : wc_get_price_excluding_tax( $product, array( 'qty' => $args['qty'] ) );
$args['total'] = $args['total'] ? $args['total'] : wc_get_price_excluding_tax( $product, array( 'qty' => $args['qty'] ) );
}
$item->set_order_id( $this->get_id() );

View File

@ -481,7 +481,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data {
*/
public function get_upsells() {
_deprecated_function( 'WC_Product::get_upsells', '2.7', 'WC_Product::get_upsell_ids' );
return apply_filters( 'woocommerce_product_upsell_ids', (array) maybe_unserialize( $this->upsell_ids ), $this );
return apply_filters( 'woocommerce_product_upsell_ids', $this->get_upsell_ids(), $this );
}
/**
@ -492,7 +492,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data {
*/
public function get_cross_sells() {
_deprecated_function( 'WC_Product::get_cross_sells', '2.7', 'WC_Product::get_cross_sell_ids' );
return apply_filters( 'woocommerce_product_crosssell_ids', (array) maybe_unserialize( $this->crosssell_ids ), $this );
return apply_filters( 'woocommerce_product_crosssell_ids', $this->get_cross_sell_ids(), $this );
}
/**
@ -725,4 +725,11 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data {
wc_soft_deprecated_function( 'WC_Product::get_files', '2.7', '2.8', 'WC_Product::get_downloads' );
return $this->get_downloads();
}
/**
* @deprected 2.7.0 Sync is taken care of during save - no need to call this directly.
*/
public function grouped_product_sync() {
_deprecated_function( 'WC_Product::grouped_product_sync', '2.7' );
}
}

View File

@ -978,7 +978,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
public function add_product( $product, $qty = 1, $args = array() ) {
if ( $product ) {
$default_args = array(
'name' => $product->get_title(),
'name' => $product->get_name(),
'tax_class' => $product->get_tax_class(),
'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(),
'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0,

View File

@ -118,6 +118,9 @@ class WC_Product extends WC_Abstract_Legacy_Product {
'_downloadable',
'_featured',
'_downloadable_files',
'_wc_rating_count',
'_wc_average_rating',
'_wc_review_count',
);
/**
@ -1346,6 +1349,9 @@ class WC_Product extends WC_Abstract_Legacy_Product {
} else {
$this->data_store->create( $this );
}
if ( $this->get_parent_id() ) {
wp_schedule_single_event( time(), 'woocommerce_deferred_product_sync', array( 'product_id' => $this->get_parent_id() ) );
}
return $this->get_id();
}
}

View File

@ -274,7 +274,7 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
$items_in_package = array();
foreach ( $args['package']['contents'] as $item ) {
$product = $item['data'];
$items_in_package[] = $product->get_title() . ' × ' . $item['quantity'];
$items_in_package[] = $product->get_name() . ' × ' . $item['quantity'];
}
$rate->add_meta_data( __( 'Items', 'woocommerce' ), implode( ', ', $items_in_package ) );
}

View File

@ -71,13 +71,6 @@ class WC_Admin_Post_Types {
add_filter( 'parse_query', array( $this, 'product_filters_query' ) );
add_filter( 'posts_search', array( $this, 'product_search' ) );
// Status transitions
add_action( 'delete_post', array( $this, 'delete_post' ) );
add_action( 'wp_trash_post', array( $this, 'trash_post' ) );
add_action( 'untrash_post', array( $this, 'untrash_post' ) );
add_action( 'before_delete_post', array( $this, 'delete_order_items' ) );
add_action( 'before_delete_post', array( $this, 'delete_order_downloadable_permissions' ) );
// Edit post screens
add_filter( 'enter_title_here', array( $this, 'enter_title_here' ), 1, 2 );
add_action( 'edit_form_after_title', array( $this, 'edit_form_after_title' ) );
@ -1922,180 +1915,6 @@ class WC_Admin_Post_Types {
}
}
/**
* Removes variations etc belonging to a deleted post, and clears transients.
*
* @param mixed $id ID of post being deleted
*/
public function delete_post( $id ) {
global $woocommerce, $wpdb;
if ( ! current_user_can( 'delete_posts' ) ) {
return;
}
if ( $id > 0 ) {
$post_type = get_post_type( $id );
switch ( $post_type ) {
case 'product' :
$child_product_variations = get_children( 'post_parent=' . $id . '&post_type=product_variation' );
if ( ! empty( $child_product_variations ) ) {
foreach ( $child_product_variations as $child ) {
wp_delete_post( $child->ID, true );
}
}
$child_products = get_children( 'post_parent=' . $id . '&post_type=product' );
if ( ! empty( $child_products ) ) {
foreach ( $child_products as $child ) {
$child_post = array();
$child_post['ID'] = $child->ID;
$child_post['post_parent'] = 0;
wp_update_post( $child_post );
}
}
if ( $parent_id = wp_get_post_parent_id( $id ) ) {
wc_delete_product_transients( $parent_id );
}
break;
case 'product_variation' :
wc_delete_product_transients( wp_get_post_parent_id( $id ) );
break;
case 'shop_order' :
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
if ( ! is_null( $refunds ) ) {
foreach ( $refunds as $refund ) {
wp_delete_post( $refund->ID, true );
}
}
break;
}
}
}
/**
* woocommerce_trash_post function.
*
* @param mixed $id
*/
public function trash_post( $id ) {
global $wpdb;
if ( $id > 0 ) {
$post_type = get_post_type( $id );
if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) {
// Delete count - meta doesn't work on trashed posts
$user_id = get_post_meta( $id, '_customer_user', true );
if ( $user_id > 0 ) {
delete_user_meta( $user_id, '_money_spent' );
delete_user_meta( $user_id, '_order_count' );
}
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
foreach ( $refunds as $refund ) {
$wpdb->update( $wpdb->posts, array( 'post_status' => 'trash' ), array( 'ID' => $refund->ID ) );
}
delete_transient( 'woocommerce_processing_order_count' );
wc_delete_shop_order_transients( $id );
}
}
}
/**
* woocommerce_untrash_post function.
*
* @param mixed $id
*/
public function untrash_post( $id ) {
global $wpdb;
if ( $id > 0 ) {
$post_type = get_post_type( $id );
if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) {
// Delete count - meta doesn't work on trashed posts
$user_id = get_post_meta( $id, '_customer_user', true );
if ( $user_id > 0 ) {
delete_user_meta( $user_id, '_money_spent' );
delete_user_meta( $user_id, '_order_count' );
}
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
foreach ( $refunds as $refund ) {
$wpdb->update( $wpdb->posts, array( 'post_status' => 'wc-completed' ), array( 'ID' => $refund->ID ) );
}
delete_transient( 'woocommerce_processing_order_count' );
wc_delete_shop_order_transients( $id );
} elseif ( 'product' === $post_type ) {
// Check if SKU is valid before untrash the product.
$sku = get_post_meta( $id, '_sku', true );
if ( ! empty( $sku ) ) {
if ( ! wc_product_has_unique_sku( $id, $sku ) ) {
update_post_meta( $id, '_sku', '' );
}
}
}
}
}
/**
* Remove item meta on permanent deletion.
*/
public function delete_order_items( $postid ) {
global $wpdb;
if ( in_array( get_post_type( $postid ), wc_get_order_types() ) ) {
do_action( 'woocommerce_delete_order_items', $postid );
$wpdb->query( "
DELETE {$wpdb->prefix}woocommerce_order_items, {$wpdb->prefix}woocommerce_order_itemmeta
FROM {$wpdb->prefix}woocommerce_order_items
JOIN {$wpdb->prefix}woocommerce_order_itemmeta ON {$wpdb->prefix}woocommerce_order_items.order_item_id = {$wpdb->prefix}woocommerce_order_itemmeta.order_item_id
WHERE {$wpdb->prefix}woocommerce_order_items.order_id = '{$postid}';
" );
do_action( 'woocommerce_deleted_order_items', $postid );
}
}
/**
* Remove downloadable permissions on permanent order deletion.
*/
public function delete_order_downloadable_permissions( $postid ) {
global $wpdb;
if ( in_array( get_post_type( $postid ), wc_get_order_types() ) ) {
do_action( 'woocommerce_delete_order_downloadable_permissions', $postid );
$wpdb->query( $wpdb->prepare( "
DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions
WHERE order_id = %d
", $postid ) );
do_action( 'woocommerce_deleted_order_downloadable_permissions', $postid );
}
}
/**
* Change title boxes in admin.
* @param string $text

View File

@ -10,7 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<button type="button" data-permission_id="<?php echo absint( $download->permission_id ); ?>" rel="<?php echo absint( $download->product_id ) . ',' . esc_attr( $download->download_id ); ?>" class="revoke_access button"><?php _e( 'Revoke access', 'woocommerce' ); ?></button>
<div class="handlediv" aria-label="<?php esc_attr_e( 'Click to toggle', 'woocommerce' ); ?>"></div>
<strong>
<?php echo '#' . absint( $product->get_id() ) . ' &mdash; ' . apply_filters( 'woocommerce_admin_download_permissions_title', $product->get_title(), $download->product_id, $download->order_id, $download->order_key, $download->download_id ) . ' &mdash; ' . esc_html( $file_count ) . ': ' . wc_get_filename_from_url( $product->get_file_download_path( $download->download_id ) ) . ' &mdash; ' . sprintf( _n( 'Downloaded %s time', 'Downloaded %s times', absint( $download->download_count ), 'woocommerce' ), absint( $download->download_count ) ); ?>
<?php echo '#' . absint( $product->get_id() ) . ' &mdash; ' . apply_filters( 'woocommerce_admin_download_permissions_title', $product->get_name(), $download->product_id, $download->order_id, $download->order_key, $download->download_id ) . ' &mdash; ' . esc_html( $file_count ) . ': ' . wc_get_filename_from_url( $product->get_file_download_path( $download->download_id ) ) . ' &mdash; ' . sprintf( _n( 'Downloaded %s time', 'Downloaded %s times', absint( $download->download_count ), 'woocommerce' ), absint( $download->download_count ) ); ?>
</strong>
</h3>
<table cellpadding="0" cellspacing="0" class="wc-metabox-content">

View File

@ -87,7 +87,7 @@ class WC_Report_Stock extends WP_List_Table {
echo $sku . ' - ';
}
echo $product->get_title();
echo $product->get_name();
// Get variation data
if ( $product->is_type( 'variation' ) ) {

View File

@ -380,7 +380,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Products_Controller
'visible' => array(
'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ),
'type' => 'boolean',
'default' => false,
'default' => true,
'context' => array( 'view', 'edit' ),
),
'purchasable' => array(

View File

@ -1154,8 +1154,6 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
$product->set_date_on_sale_from( '' );
$product->set_price( '' );
} else {
$date_from = $date_to = '';
// Regular Price.
if ( isset( $request['regular_price'] ) ) {
$product->set_regular_price( $request['regular_price'] );
@ -1180,46 +1178,6 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
$product->set_parent_id( $request['parent_id'] );
}
// Update parent if grouped so price sorting works and stays in sync with the cheapest child.
if ( $product->get_parent_id() > 0 || $product->is_type( 'grouped' ) ) {
$clear_parent_ids = array();
if ( $product->get_parent_id() > 0 ) {
$clear_parent_ids[] = $product->get_parent_id();
$parent = wc_get_product( $product->get_parent_id() );
$children = $parent->get_children();
$parent->set_children( array_filter( array_merge( $children, array( $product->get_id() ) ) ) );
$parent->save();
}
if ( $product->is_type( 'grouped' ) ) {
$clear_parent_ids[] = $product->get_id();
}
if ( ! empty( $clear_parent_ids ) ) {
foreach ( $clear_parent_ids as $clear_id ) {
$clear_product = wc_get_product( $clear_id );
$children_by_price = get_posts( array(
'post_parent' => $clear_id,
'orderby' => 'meta_value_num',
'order' => 'asc',
'meta_key' => '_price',
'posts_per_page' => 1,
'post_type' => 'product',
'fields' => 'ids',
) );
if ( $children_by_price ) {
foreach ( $children_by_price as $child ) {
$child_product = wc_get_product( $child );
$clear_product->set_price( $child_product->get_price() );
}
}
}
}
}
// Sold individually.
if ( isset( $request['sold_individually'] ) ) {
$product->set_sold_individually( $request['sold_individually'] );
@ -1380,7 +1338,6 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
} else {
$variations = $request['variations'];
}
$attributes = $product->get_variation_attributes();
foreach ( $variations as $menu_order => $data ) {
$variation_id = isset( $data['id'] ) ? absint( $data['id'] ) : 0;
@ -1506,7 +1463,8 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
// Update taxonomies.
if ( isset( $data['attributes'] ) ) {
$_attributes = array();
$attributes = array();
$parent_attributes = $product->get_attributes();
foreach ( $data['attributes'] as $attribute ) {
$attribute_id = 0;
@ -1524,30 +1482,28 @@ class WC_REST_Products_Controller extends WC_REST_Posts_Controller {
continue;
}
if ( isset( $attributes[ $attribute_name ] ) ) {
$_attribute = $attributes[ $attribute_name ];
if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) {
continue;
}
if ( isset( $_attribute['is_variation'] ) && $_attribute['is_variation'] ) {
$_attribute_key = sanitize_title( $_attribute['name'] );
$attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : '';
$attribute_key = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() );
$attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : '';
if ( ! empty( $_attribute['is_taxonomy'] ) ) {
// If dealing with a taxonomy, we need to get the slug from the name posted to the API.
$term = get_term_by( 'name', $attribute_value, $attribute_name );
if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) {
// If dealing with a taxonomy, we need to get the slug from the name posted to the API.
$term = get_term_by( 'name', $attribute_value, $attribute_name );
if ( $term && ! is_wp_error( $term ) ) {
$attribute_value = $term->slug;
} else {
$attribute_value = sanitize_title( $attribute_value );
}
if ( $term && ! is_wp_error( $term ) ) {
$attribute_value = $term->slug;
} else {
$attribute_value = sanitize_title( $attribute_value );
}
$_attributes[ $_attribute_key ] = $attribute_value;
}
$attributes[ $attribute_key ] = $attribute_value;
}
$variation->set_attributes( $_attributes );
$variation->set_attributes( $attributes );
}
$variation->save();

View File

@ -80,7 +80,7 @@ class WC_REST_Report_Top_Sellers_Controller extends WC_REST_Report_Sales_Control
if ( $product ) {
$top_sellers[] = array(
'name' => $product->get_title(),
'name' => $product->get_name(),
'product_id' => (int) $item->product_id,
'quantity' => wc_stock_amount( $item->order_item_qty ),
);

View File

@ -315,7 +315,7 @@ class WC_API_Products extends WC_API_Resource {
'categories' => wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ),
'tags' => wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) ),
'images' => $this->get_images( $product ),
'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ),
'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->get_id() ) ),
'attributes' => $this->get_attributes( $product ),
'downloads' => $this->get_downloads( $product ),
'download_limit' => $product->get_download_limit(),

View File

@ -399,7 +399,7 @@ class WC_API_Reports extends WC_API_Resource {
$product = wc_get_product( $top_seller->product_id );
$top_sellers_data[] = array(
'title' => $product->get_title(),
'title' => $product->get_name(),
'product_id' => $top_seller->product_id,
'quantity' => $top_seller->order_item_qty,
);

View File

@ -735,7 +735,7 @@ class WC_API_Products extends WC_API_Resource {
'categories' => wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ),
'tags' => wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) ),
'images' => $this->get_images( $product ),
'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ),
'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->get_id() ) ),
'attributes' => $this->get_attributes( $product ),
'downloads' => $this->get_downloads( $product ),
'download_limit' => $product->get_download_limit(),
@ -1072,16 +1072,6 @@ class WC_API_Products extends WC_API_Resource {
$product->set_parent_id( absint( $data['parent_id'] ) );
}
// Update parent if grouped so price sorting works and stays in sync with the cheapest child
if ( $product->get_parent_id() > 0 || $product->is_type( 'grouped' ) ) {
if ( $product->get_parent_id() > 0 ) {
$parent = wc_get_product( $product->get_parent_id() );
$children = $parent->get_children();
$parent->set_children( array_filter( array_merge( $children, array( $product->get_id() ) ) ) );
$parent->save();
}
}
// Sold Individually
if ( isset( $data['sold_individually'] ) ) {
$product->set_sold_individually( true === $data['sold_individually'] ? 'yes' : '' );

View File

@ -247,7 +247,7 @@ class WC_API_Reports extends WC_API_Resource {
if ( $product ) {
$top_sellers_data[] = array(
'title' => $product->get_title(),
'title' => $product->get_name(),
'product_id' => $top_seller->product_id,
'quantity' => $top_seller->order_item_qty,
);

View File

@ -1180,7 +1180,7 @@ class WC_API_Products extends WC_API_Resource {
'categories' => wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) ),
'tags' => wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) ),
'images' => $this->get_images( $product ),
'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->get_id() ) ),
'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->get_id() ) ),
'attributes' => $this->get_attributes( $product ),
'downloads' => $this->get_downloads( $product ),
'download_limit' => $product->get_download_limit(),
@ -1557,16 +1557,6 @@ class WC_API_Products extends WC_API_Resource {
$product->set_parent_id( absint( $data['parent_id'] ) );
}
// Update parent if grouped so price sorting works and stays in sync with the cheapest child.
if ( $product->get_parent_id() > 0 || $product->is_type( 'grouped' ) ) {
if ( $product->get_parent_id() > 0 ) {
$parent = wc_get_product( $product->get_parent_id() );
$children = $parent->get_children();
$parent->set_children( array_filter( array_merge( $children, array( $product->get_id() ) ) ) );
$parent->save();
}
}
// Sold Individually.
if ( isset( $data['sold_individually'] ) ) {
$product->set_sold_individually( true === $data['sold_individually'] ? 'yes' : '' );

View File

@ -247,7 +247,7 @@ class WC_API_Reports extends WC_API_Resource {
if ( $product ) {
$top_sellers_data[] = array(
'title' => $product->get_title(),
'title' => $product->get_name(),
'product_id' => $top_seller->product_id,
'quantity' => $top_seller->order_item_qty,
);

View File

@ -1061,13 +1061,7 @@ class WC_AJAX {
$stock_change = apply_filters( 'woocommerce_reduce_order_stock_quantity', $order_item_qty[ $item_id ], $item_id );
$new_stock = $_product->reduce_stock( $stock_change );
$item_name = $_product->get_sku() ? $_product->get_sku() : $_product->get_id();
if ( ! empty( $_product->variation_id ) ) {
$note = sprintf( __( 'Item %1$s variation #%2$s stock reduced from %3$s to %4$s.', 'woocommerce' ), $item_name, $_product->variation_id, $new_stock + $stock_change, $new_stock );
} else {
$note = sprintf( __( 'Item %1$s stock reduced from %2$s to %3$s.', 'woocommerce' ), $item_name, $new_stock + $stock_change, $new_stock );
}
$note = sprintf( __( 'Item %1$s stock reduced from %2$s to %3$s.', 'woocommerce' ), $item_name, $new_stock + $stock_change, $new_stock );
$return[] = $note;
$order->add_order_note( $note );
}
@ -1107,13 +1101,7 @@ class WC_AJAX {
$stock_change = apply_filters( 'woocommerce_restore_order_stock_quantity', $order_item_qty[ $item_id ], $item_id );
$new_quantity = $_product->increase_stock( $stock_change );
$item_name = $_product->get_sku() ? $_product->get_sku() : $_product->get_id();
if ( ! empty( $_product->variation_id ) ) {
$note = sprintf( __( 'Item %1$s variation #%2$s stock increased from %3$s to %4$s.', 'woocommerce' ), $item_name, $_product->variation_id, $old_stock, $new_quantity );
} else {
$note = sprintf( __( 'Item %1$s stock increased from %2$s to %3$s.', 'woocommerce' ), $item_name, $old_stock, $new_quantity );
}
$note = sprintf( __( 'Item %1$s stock increased from %2$s to %3$s.', 'woocommerce' ), $item_name, $old_stock, $new_quantity );
$return[] = $note;
$order->add_order_note( $note );
}

View File

@ -236,7 +236,7 @@ class WC_Cart {
// Flag to indicate the stored cart should be update
$update_cart_session = true;
/* translators: %s: product name */
wc_add_notice( sprintf( __( '%s has been removed from your cart because it can no longer be purchased. Please contact us if you need assistance.', 'woocommerce' ), $product->get_title() ), 'error' );
wc_add_notice( sprintf( __( '%s has been removed from your cart because it can no longer be purchased. Please contact us if you need assistance.', 'woocommerce' ), $product->get_name() ), 'error' );
do_action( 'woocommerce_remove_cart_item_from_session', $key, $values );
} else {
@ -476,7 +476,7 @@ class WC_Cart {
*/
if ( ! $product->is_in_stock() ) {
/* translators: %s: product name */
$error->add( 'out-of-stock', sprintf( __( 'Sorry, "%s" is not in stock. Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_title() ) );
$error->add( 'out-of-stock', sprintf( __( 'Sorry, "%s" is not in stock. Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_name() ) );
return $error;
}
@ -489,7 +489,7 @@ class WC_Cart {
*/
if ( ! $product->has_enough_stock( $product_qty_in_cart[ $product->get_stock_managed_by_id() ] ) ) {
/* translators: 1: product name 2: quantity in stock */
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s in stock). Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_title(), $product->get_stock_quantity() ) );
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s in stock). Please edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_name(), $product->get_stock_quantity() ) );
return $error;
}
@ -518,7 +518,7 @@ class WC_Cart {
if ( $product->get_stock_quantity() < ( $held_stock + $product_qty_in_cart[ $product->get_stock_managed_by_id() ] ) ) {
/* translators: 1: product name 2: minutes */
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order right now. Please try again in %2$d minutes or edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_title(), get_option( 'woocommerce_hold_stock_minutes' ) ) );
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order right now. Please try again in %2$d minutes or edit your cart and try again. We apologise for any inconvenience caused.', 'woocommerce' ), $product->get_name(), get_option( 'woocommerce_hold_stock_minutes' ) ) );
return $error;
}
}
@ -538,12 +538,13 @@ class WC_Cart {
$item_data = array();
// Variation data
if ( ! empty( $cart_item['data']->variation_id ) && is_array( $cart_item['variation'] ) ) {
if ( $cart_item['data']->is_type( 'variation' ) && is_array( $cart_item['variation'] ) ) {
foreach ( $cart_item['variation'] as $name => $value ) {
if ( '' === $value )
if ( '' === $value ) {
continue;
}
$taxonomy = wc_attribute_taxonomy_name( str_replace( 'attribute_pa_', '', urldecode( $name ) ) );
@ -616,7 +617,7 @@ class WC_Cart {
if ( ! $this->is_empty() ) {
foreach ( $this->get_cart() as $cart_item_key => $values ) {
if ( $values['quantity'] > 0 ) {
$cross_sells = array_merge( $values['data']->get_cross_sells(), $cross_sells );
$cross_sells = array_merge( $values['data']->get_cross_sell_ids(), $cross_sells );
$in_cart[] = $values['product_id'];
}
}
@ -892,7 +893,7 @@ class WC_Cart {
$product_data = wc_get_product( $variation_id ? $variation_id : $product_id );
// Sanity check
if ( $quantity <= 0 || ! $product_data || 'trash' === $product_data->post->post_status ) {
if ( $quantity <= 0 || ! $product_data || 'trash' === $product_data->get_status() ) {
return false;
}
@ -912,7 +913,7 @@ class WC_Cart {
if ( $in_cart_quantity > 0 ) {
/* translators: %s: product name */
throw new Exception( sprintf( '<a href="%s" class="button wc-forward">%s</a> %s', wc_get_cart_url(), __( 'View cart', 'woocommerce' ), sprintf( __( 'You cannot add another "%s" to your cart.', 'woocommerce' ), $product_data->get_title() ) ) );
throw new Exception( sprintf( '<a href="%s" class="button wc-forward">%s</a> %s', wc_get_cart_url(), __( 'View cart', 'woocommerce' ), sprintf( __( 'You cannot add another "%s" to your cart.', 'woocommerce' ), $product_data->get_name() ) ) );
}
}
@ -923,12 +924,12 @@ class WC_Cart {
// Stock check - only check if we're managing stock and backorders are not allowed
if ( ! $product_data->is_in_stock() ) {
throw new Exception( sprintf( __( 'You cannot add &quot;%s&quot; to the cart because the product is out of stock.', 'woocommerce' ), $product_data->get_title() ) );
throw new Exception( sprintf( __( 'You cannot add &quot;%s&quot; to the cart because the product is out of stock.', 'woocommerce' ), $product_data->get_name() ) );
}
if ( ! $product_data->has_enough_stock( $quantity ) ) {
/* translators: 1: product name 2: quantity in stock */
throw new Exception( sprintf( __( 'You cannot add that amount of &quot;%1$s&quot; to the cart because there is not enough stock (%2$s remaining).', 'woocommerce' ), $product_data->get_title(), $product_data->get_stock_quantity() ) );
throw new Exception( sprintf( __( 'You cannot add that amount of &quot;%1$s&quot; to the cart because there is not enough stock (%2$s remaining).', 'woocommerce' ), $product_data->get_name(), $product_data->get_stock_quantity() ) );
}
// Stock check - this time accounting for whats already in-cart
@ -2127,7 +2128,7 @@ class WC_Cart {
if ( 'excl' === $this->tax_display_cart ) {
$row_price = $product->get_price_excluding_tax( $quantity );
$row_price = wc_get_price_excluding_tax( $product, array( 'qty' => $quantity ) );
$product_subtotal = wc_price( $row_price );
if ( $this->prices_include_tax && $this->tax_total > 0 ) {
@ -2135,7 +2136,7 @@ class WC_Cart {
}
} else {
$row_price = $product->get_price_including_tax( $quantity );
$row_price = wc_get_price_including_tax( $product, array( 'qty' => $quantity ) );
$product_subtotal = wc_price( $row_price );
if ( ! $this->prices_include_tax && $this->tax_total > 0 ) {

View File

@ -232,10 +232,10 @@ class WC_Checkout {
$product = $values['data'];
$item = new WC_Order_Item_Product( array(
'quantity' => $values['quantity'],
'name' => $product ? $product->get_title() : '',
'name' => $product ? $product->get_name() : '',
'tax_class' => $product ? $product->get_tax_class() : '',
'product_id' => $product && isset( $product->id ) ? $product->id : 0,
'variation_id' => $product && isset( $product->variation_id ) ? $product->variation_id : 0,
'product_id' => $product ? ( $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id() ) : 0,
'variation_id' => $product && $product->is_type( 'variation' ) ? $product->get_id() : 0,
'variation' => $values['variation'],
'subtotal' => $values['line_subtotal'],
'total' => $values['line_total'],

View File

@ -239,9 +239,9 @@ class WC_Comments {
if ( 'product' === get_post_type( $post_id ) ) {
$product = wc_get_product( $post_id );
update_post_meta( $post_id, '_wc_rating_count', self::get_rating_counts_for_product( $product ) );
update_post_meta( $post_id, '_wc_average_rating', self::get_average_rating_for_product( $product ) );
update_post_meta( $post_id, '_wc_review_count', self::get_review_count_for_product( $product ) );
self::get_rating_counts_for_product( $product );
self::get_average_rating_for_product( $product );
self::get_review_count_for_product( $product );
}
}
@ -336,7 +336,7 @@ class WC_Comments {
* @param WC_Product $product
* @return float
*/
public static function get_average_rating_for_product( $product ) {
public static function get_average_rating_for_product( &$product ) {
global $wpdb;
$count = $product->get_rating_count();
@ -355,7 +355,10 @@ class WC_Comments {
$average = 0;
}
update_post_meta( $product->get_id(), '_wc_average_rating', true );
$product->set_average_rating( $average );
$data_store = $product->get_data_store();
$data_store->update_average_rating( $product );
return $average;
}
@ -367,7 +370,7 @@ class WC_Comments {
* @param WC_Product $product
* @return int
*/
public static function get_review_count_for_product( $product ) {
public static function get_review_count_for_product( &$product ) {
global $wpdb;
$count = $wpdb->get_var( $wpdb->prepare("
@ -377,7 +380,10 @@ class WC_Comments {
AND comment_approved = '1'
", $product->get_id() ) );
update_post_meta( $product->get_id(), '_wc_review_count', true );
$product->set_review_count( $count );
$data_store = $product->get_data_store();
$data_store->update_review_count( $product );
return $count;
}
@ -389,7 +395,7 @@ class WC_Comments {
* @param WC_Product $product
* @return array of integers
*/
public static function get_rating_counts_for_product( $product ) {
public static function get_rating_counts_for_product( &$product ) {
global $wpdb;
$counts = array();
@ -407,7 +413,10 @@ class WC_Comments {
$counts[ $count->meta_value ] = absint( $count->meta_value_count );
}
update_post_meta( $product->get_id(), '_wc_rating_count', true );
$product->set_rating_counts( $counts );
$data_store = $product->get_data_store();
$data_store->update_rating_counts( $product );
return $counts;
}

View File

@ -391,9 +391,9 @@ class WC_Coupon extends WC_Legacy_Coupon {
* Uses price inc tax if prices include tax to work around https://github.com/woocommerce/woocommerce/issues/7669 and https://github.com/woocommerce/woocommerce/issues/8074.
*/
if ( wc_prices_include_tax() ) {
$discount_percent = ( $cart_item['data']->get_price_including_tax() * $cart_item_qty ) / WC()->cart->subtotal;
$discount_percent = ( wc_get_price_including_tax( $cart_item['data'] ) * $cart_item_qty ) / WC()->cart->subtotal;
} else {
$discount_percent = ( $cart_item['data']->get_price_excluding_tax() * $cart_item_qty ) / WC()->cart->subtotal_ex_tax;
$discount_percent = ( wc_get_price_excluding_tax( $cart_item['data'] ) * $cart_item_qty ) / WC()->cart->subtotal_ex_tax;
}
$discount = ( $this->get_amount() * $discount_percent ) / $cart_item_qty;
@ -845,7 +845,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
$valid_for_cart = false;
if ( ! WC()->cart->is_empty() ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $this->get_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_product_ids() ) || in_array( $cart_item['data']->get_parent(), $this->get_product_ids() ) ) {
if ( in_array( $cart_item['product_id'], $this->get_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_product_ids() ) || in_array( $cart_item['data']->get_parent_id(), $this->get_product_ids() ) ) {
$valid_for_cart = true;
}
}
@ -949,7 +949,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
$valid_for_cart = true;
if ( ! WC()->cart->is_empty() ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['data']->get_parent(), $this->get_excluded_product_ids() ) ) {
if ( in_array( $cart_item['product_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['data']->get_parent_id(), $this->get_excluded_product_ids() ) ) {
$valid_for_cart = false;
}
}
@ -1061,7 +1061,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
$valid = false;
$product_cats = wc_get_product_cat_ids( $product->get_id() );
$product_ids = array( $product->get_id(), ( isset( $product->variation_id ) ? $product->variation_id : 0 ), $product->get_parent() );
$product_ids = array( $product->get_id(), $product->get_parent_id() );
// Specific products get the discount
if ( sizeof( $this->get_product_ids() ) && sizeof( array_intersect( $product_ids, $this->get_product_ids() ) ) ) {
@ -1092,11 +1092,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
if ( $this->get_exclude_sale_items() ) {
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( isset( $product->variation_id ) ) {
if ( in_array( $product->variation_id, $product_ids_on_sale, true ) ) {
$valid = false;
}
} elseif ( in_array( $product->get_id(), $product_ids_on_sale, true ) ) {
if ( in_array( $product->get_id(), $product_ids_on_sale, true ) ) {
$valid = false;
}
}
@ -1197,7 +1193,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
$products = array();
if ( ! WC()->cart->is_empty() ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( in_array( $cart_item['product_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['data']->get_parent(), $this->get_excluded_product_ids() ) ) {
if ( in_array( $cart_item['product_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['variation_id'], $this->get_excluded_product_ids() ) || in_array( $cart_item['data']->get_parent_id(), $this->get_excluded_product_ids() ) ) {
$products[] = $cart_item['data']->get_title();
}
}

View File

@ -485,7 +485,7 @@ class WC_Form_Handler {
$product = wc_get_product( $cart_item['product_id'] );
$item_removed_title = apply_filters( 'woocommerce_cart_item_removed_title', $product ? sprintf( _x( '&ldquo;%s&rdquo;', 'Item name in quotes', 'woocommerce' ), $product->get_title() ) : __( 'Item', 'woocommerce' ), $cart_item );
$item_removed_title = apply_filters( 'woocommerce_cart_item_removed_title', $product ? sprintf( _x( '&ldquo;%s&rdquo;', 'Item name in quotes', 'woocommerce' ), $product->get_name() ) : __( 'Item', 'woocommerce' ), $cart_item );
// Don't show undo link if removed item is out of stock.
if ( $product->is_in_stock() && $product->has_enough_stock( $cart_item['quantity'] ) ) {
@ -542,7 +542,7 @@ class WC_Form_Handler {
// is_sold_individually
if ( $_product->is_sold_individually() && $quantity > 1 ) {
wc_add_notice( sprintf( __( 'You can only have 1 %s in your cart.', 'woocommerce' ), $_product->get_title() ), 'error' );
wc_add_notice( sprintf( __( 'You can only have 1 %s in your cart.', 'woocommerce' ), $_product->get_name() ), 'error' );
$passed_validation = false;
}
@ -805,13 +805,14 @@ class WC_Form_Handler {
$variation_id = $data_store->find_matching_product_variation( $adding_to_cart, wp_unslash( $_POST ) );
}
$variation = wc_get_product( $variation_id );
// Validate the attributes.
try {
if ( empty( $variation_id ) ) {
throw new Exception( __( 'Please choose product options&hellip;', 'woocommerce' ) );
}
$variation_data = wc_get_product_variation_attributes( $variation_id );
foreach ( $attributes as $attribute ) {
if ( ! $attribute['is_variation'] ) {
continue;
@ -829,7 +830,7 @@ class WC_Form_Handler {
}
// Get valid value from variation
$valid_value = isset( $variation->variation_data[ $taxonomy ] ) ? $variation->variation_data[ $taxonomy ] : '';
$valid_value = isset( $variation_data[ $taxonomy ] ) ? $variation_data[ $taxonomy ] : '';
// Allow if valid or show error.
if ( '' === $valid_value || $valid_value === $value ) {

View File

@ -371,7 +371,7 @@ class WC_Order_Item_Product extends WC_Order_Item {
} else {
$this->set_product_id( $product->get_id() );
}
$this->set_name( $product->get_title() );
$this->set_name( $product->get_name() );
$this->set_tax_class( $product->get_tax_class() );
}

View File

@ -40,6 +40,13 @@ class WC_Post_Data {
add_filter( 'update_order_item_metadata', array( __CLASS__, 'update_order_item_metadata' ), 10, 5 );
add_filter( 'update_post_metadata', array( __CLASS__, 'update_post_metadata' ), 10, 5 );
add_filter( 'wp_insert_post_data', array( __CLASS__, 'wp_insert_post_data' ) );
// Status transitions
add_action( 'delete_post', array( __CLASS__, 'delete_post' ) );
add_action( 'wp_trash_post', array( __CLASS__, 'trash_post' ) );
add_action( 'untrash_post', array( __CLASS__, 'untrash_post' ) );
add_action( 'before_delete_post', array( __CLASS__, 'delete_order_items' ) );
add_action( 'before_delete_post', array( __CLASS__, 'delete_order_downloadable_permissions' ) );
}
/**
@ -213,6 +220,180 @@ class WC_Post_Data {
return $data;
}
/**
* Removes variations etc belonging to a deleted post, and clears transients.
*
* @param mixed $id ID of post being deleted
*/
public static function delete_post( $id ) {
global $woocommerce, $wpdb;
if ( ! current_user_can( 'delete_posts' ) ) {
return;
}
if ( $id > 0 ) {
$post_type = get_post_type( $id );
switch ( $post_type ) {
case 'product' :
$child_product_variations = get_children( 'post_parent=' . $id . '&post_type=product_variation' );
if ( ! empty( $child_product_variations ) ) {
foreach ( $child_product_variations as $child ) {
wp_delete_post( $child->ID, true );
}
}
$child_products = get_children( 'post_parent=' . $id . '&post_type=product' );
if ( ! empty( $child_products ) ) {
foreach ( $child_products as $child ) {
$child_post = array();
$child_post['ID'] = $child->ID;
$child_post['post_parent'] = 0;
wp_update_post( $child_post );
}
}
if ( $parent_id = wp_get_post_parent_id( $id ) ) {
wc_delete_product_transients( $parent_id );
}
break;
case 'product_variation' :
wc_delete_product_transients( wp_get_post_parent_id( $id ) );
break;
case 'shop_order' :
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
if ( ! is_null( $refunds ) ) {
foreach ( $refunds as $refund ) {
wp_delete_post( $refund->ID, true );
}
}
break;
}
}
}
/**
* woocommerce_trash_post function.
*
* @param mixed $id
*/
public static function trash_post( $id ) {
global $wpdb;
if ( $id > 0 ) {
$post_type = get_post_type( $id );
if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) {
// Delete count - meta doesn't work on trashed posts
$user_id = get_post_meta( $id, '_customer_user', true );
if ( $user_id > 0 ) {
delete_user_meta( $user_id, '_money_spent' );
delete_user_meta( $user_id, '_order_count' );
}
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
foreach ( $refunds as $refund ) {
$wpdb->update( $wpdb->posts, array( 'post_status' => 'trash' ), array( 'ID' => $refund->ID ) );
}
delete_transient( 'woocommerce_processing_order_count' );
wc_delete_shop_order_transients( $id );
}
}
}
/**
* woocommerce_untrash_post function.
*
* @param mixed $id
*/
public static function untrash_post( $id ) {
global $wpdb;
if ( $id > 0 ) {
$post_type = get_post_type( $id );
if ( in_array( $post_type, wc_get_order_types( 'order-count' ) ) ) {
// Delete count - meta doesn't work on trashed posts
$user_id = get_post_meta( $id, '_customer_user', true );
if ( $user_id > 0 ) {
delete_user_meta( $user_id, '_money_spent' );
delete_user_meta( $user_id, '_order_count' );
}
$refunds = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order_refund' AND post_parent = %d", $id ) );
foreach ( $refunds as $refund ) {
$wpdb->update( $wpdb->posts, array( 'post_status' => 'wc-completed' ), array( 'ID' => $refund->ID ) );
}
delete_transient( 'woocommerce_processing_order_count' );
wc_delete_shop_order_transients( $id );
} elseif ( 'product' === $post_type ) {
// Check if SKU is valid before untrash the product.
$sku = get_post_meta( $id, '_sku', true );
if ( ! empty( $sku ) ) {
if ( ! wc_product_has_unique_sku( $id, $sku ) ) {
update_post_meta( $id, '_sku', '' );
}
}
}
}
}
/**
* Remove item meta on permanent deletion.
*/
public static function delete_order_items( $postid ) {
global $wpdb;
if ( in_array( get_post_type( $postid ), wc_get_order_types() ) ) {
do_action( 'woocommerce_delete_order_items', $postid );
$wpdb->query( "
DELETE {$wpdb->prefix}woocommerce_order_items, {$wpdb->prefix}woocommerce_order_itemmeta
FROM {$wpdb->prefix}woocommerce_order_items
JOIN {$wpdb->prefix}woocommerce_order_itemmeta ON {$wpdb->prefix}woocommerce_order_items.order_item_id = {$wpdb->prefix}woocommerce_order_itemmeta.order_item_id
WHERE {$wpdb->prefix}woocommerce_order_items.order_id = '{$postid}';
" );
do_action( 'woocommerce_deleted_order_items', $postid );
}
}
/**
* Remove downloadable permissions on permanent order deletion.
*/
public static function delete_order_downloadable_permissions( $postid ) {
global $wpdb;
if ( in_array( get_post_type( $postid ), wc_get_order_types() ) ) {
do_action( 'woocommerce_delete_order_downloadable_permissions', $postid );
$wpdb->query( $wpdb->prepare( "
DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions
WHERE order_id = %d
", $postid ) );
do_action( 'woocommerce_deleted_order_downloadable_permissions', $postid );
}
}
}
WC_Post_Data::init();

View File

@ -86,7 +86,7 @@ class WC_Product_Grouped extends WC_Product {
foreach ( $this->get_children() as $child_id ) {
$child = wc_get_product( $child_id );
if ( '' !== $child->get_price() ) {
$child_prices[] = 'incl' === $tax_display_mode ? $child->get_price_including_tax() : $child->get_price_excluding_tax();
$child_prices[] = 'incl' === $tax_display_mode ? wc_get_price_including_tax( $child ) : wc_get_price_excluding_tax( $child );
}
}
@ -105,7 +105,7 @@ class WC_Product_Grouped extends WC_Product {
if ( $is_free ) {
$price = apply_filters( 'woocommerce_grouped_free_price_html', __( 'Free!', 'woocommerce' ), $this );
} else {
$price = apply_filters( 'woocommerce_grouped_price_html', $price . $this->get_price_suffix(), $this, $child_prices );
$price = apply_filters( 'woocommerce_grouped_price_html', $price . wc_get_price_suffix( $this ), $this, $child_prices );
}
} else {
$price = apply_filters( 'woocommerce_grouped_empty_price_html', '', $this );
@ -148,4 +148,32 @@ class WC_Product_Grouped extends WC_Product {
public function set_children( $children ) {
$this->set_prop( 'children', array_filter( wp_parse_id_list( (array) $children ) ) );
}
/*
|--------------------------------------------------------------------------
| Sync with children.
|--------------------------------------------------------------------------
*/
/**
* Sync a grouped product with it's children. These sync functions sync
* upwards (from child to parent) when the variation is saved.
*
* @param WC_Product|int $product Product object or ID for which you wish to sync.
* @param bool $save If true, the prouduct object will be saved to the DB before returning it.
* @return WC_Product Synced product object.
*/
public static function sync( $product, $save = true ) {
if ( ! is_a( $product, 'WC_Product' ) ) {
$product = wc_get_product( $product );
}
if ( is_a( $product, 'WC_Product_Grouped' ) ) {
$data_store = WC_Data_Store::load( 'product_' . $product->get_type() );
$data_store->sync_price( $product );
if ( $save ) {
$product->save();
}
}
return $product;
}
}

View File

@ -55,48 +55,4 @@ class WC_Product_Simple extends WC_Product {
return apply_filters( 'woocommerce_product_add_to_cart_text', $text, $this );
}
/**
* Get the title of the post. @todo should this be deprecated or not? It's in deprecated class and needs review.
*
* @return string
*/
public function get_title() {
$post = get_post( $this->get_id() );
$title = $post->post_title;
if ( $this->get_parent_id() > 0 ) {
/* translators: 1: parent product title 2: product title */
$title = sprintf( __( '%1$s &rarr; %2$s' , 'woocommerce' ), get_the_title( $this->get_parent_id() ), $title );
}
return apply_filters( 'woocommerce_product_title', $title, $this );
}
/**
* Sync grouped products with the children lowest price (so they can be sorted by price accurately). @todo should this be here?
*/
public function grouped_product_sync() {
if ( ! $this->get_parent_id() ) return;
$children_by_price = get_posts( array(
'post_parent' => $this->get_parent_id(),
'orderby' => 'meta_value_num',
'order' => 'asc',
'meta_key' => '_price',
'posts_per_page' => 1,
'post_type' => 'product',
'fields' => 'ids',
));
if ( $children_by_price ) {
foreach ( $children_by_price as $child ) {
$child_price = get_post_meta( $child, '_price', true );
update_post_meta( $this->get_parent_id(), '_price', $child_price );
}
}
delete_transient( 'wc_products_onsale' );
do_action( 'woocommerce_grouped_product_sync', $this->id, $children_by_price );
}
}

View File

@ -485,26 +485,6 @@ class WC_Product_Variation extends WC_Product_Simple {
parent::update_post_meta();
}
/**
* Save data (either create or update depending on if we are working on an existing product).
*
* @since 2.7.0
*/
public function save() {
$this->validate_props();
if ( $this->data_store ) {
if ( $this->get_id() ) {
$this->data_store->update( $this );
} else {
$this->data_store->create( $this );
}
wp_schedule_single_event( time(), 'woocommerce_deferred_product_sync', array( 'product_id' => $this->get_parent_id() ) );
return $this->get_id();
}
}
/*
|--------------------------------------------------------------------------
| Conditionals

View File

@ -173,7 +173,7 @@ class WC_Structured_Data {
$markup['@type'] = 'Product';
$markup['@id'] = get_permalink( $product->get_id() );
$markup['url'] = $markup['@id'];
$markup['name'] = $product->get_title();
$markup['name'] = $product->get_name();
if ( apply_filters( 'woocommerce_structured_data_product_limit', is_product_taxonomy() || is_shop() ) ) {
$this->set_data( apply_filters( 'woocommerce_structured_data_product_limited', $markup, $product ) );

View File

@ -771,7 +771,7 @@ class WC_CLI_Product extends WC_CLI_Command {
// Add data that applies to every product type.
$product_data = array(
'title' => $product->get_title(),
'title' => $product->get_name(),
'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->get_id(),
'created_at' => $this->format_datetime( $product->get_post_data()->post_date_gmt ),
'updated_at' => $this->format_datetime( $product->get_post_data()->post_modified_gmt ),
@ -1292,43 +1292,6 @@ class WC_CLI_Product extends WC_CLI_Command {
wp_update_post( array( 'ID' => $product_id, 'post_parent' => absint( $data['parent_id'] ) ) );
}
// Update parent if grouped so price sorting works and stays in sync with the cheapest child
$_product = wc_get_product( $product_id );
if ( $_product && $_product->post->post_parent > 0 || 'grouped' === $product_type ) {
$clear_parent_ids = array();
if ( $_product->post->post_parent > 0 ) {
$clear_parent_ids[] = $_product->post->post_parent;
}
if ( 'grouped' === $product_type ) {
$clear_parent_ids[] = $product_id;
}
if ( ! empty( $clear_parent_ids ) ) {
foreach ( $clear_parent_ids as $clear_id ) {
$children_by_price = get_posts( array(
'post_parent' => $clear_id,
'orderby' => 'meta_value_num',
'order' => 'asc',
'meta_key' => '_price',
'posts_per_page' => 1,
'post_type' => 'product',
'fields' => 'ids',
) );
if ( $children_by_price ) {
foreach ( $children_by_price as $child ) {
$child_price = get_post_meta( $child, '_price', true );
update_post_meta( $clear_id, '_price', $child_price );
}
}
}
}
}
// Sold Individually
if ( isset( $data['sold_individually'] ) ) {
update_post_meta( $product_id, '_sold_individually', ( $this->is_true( $data['sold_individually'] ) ) ? 'yes' : '' );

View File

@ -10,7 +10,7 @@ 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 {
class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_Data_Store, WC_Product_Data_Store_Interface {
/**
* If we have already saved our extra data, don't do automatic / default handling.
@ -129,12 +129,17 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_D
/**
* Method to delete a product from the database.
* @param WC_Product
* @param array $args Array of args to pass to the delete method.
*/
public function delete( &$product, $force_delete = false ) {
$id = $product->get_id();
public function delete( &$product, $args = array() ) {
$id = $product->get_id();
$post_type = $product->is_type( 'variation' ) ? 'product_variation' : 'product';
if ( $force_delete ) {
$args = wp_parse_args( $args, array(
'force_delete' => false,
) );
if ( $args['force_delete'] ) {
wp_delete_post( $product->get_id() );
$product->set_id( 0 );
} else {
@ -157,26 +162,26 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_D
* @since 2.7.0
*/
protected function read_product_data( &$product ) {
$id = $product->get_id();
$review_count = get_post_meta( $id, '_wc_review_count', true );
$rating_counts = get_post_meta( $id, '_wc_rating_count', true );
$average_rating = get_post_meta( $id, '_wc_average_rating', true );
$id = $product->get_id();
if ( '' === $review_count ) {
$review_count = WC_Comments::get_review_count_for_product( $product );
if ( '' === ( $review_count = get_post_meta( $id, '_wc_review_count', true ) ) ) {
WC_Comments::get_review_count_for_product( $product );
} else {
$product->set_review_count( $review_count );
}
if ( '' === $rating_counts ) {
$rating_counts = WC_Comments::get_rating_counts_for_product( $product );
if ( '' === ( $rating_counts = get_post_meta( $id, '_wc_rating_count', true ) ) ) {
WC_Comments::get_rating_counts_for_product( $product );
} else {
$product->set_rating_counts( $rating_counts );
}
if ( '' === $average_rating ) {
$average_rating = WC_Comments::get_average_rating_for_product( $product );
if ( '' === ( $average_rating = get_post_meta( $id, '_wc_average_rating', true ) ) ) {
WC_Comments::get_average_rating_for_product( $product );
} else {
$product->set_average_rating( $average_rating );
}
$product->set_average_rating( $average_rating );
$product->set_rating_counts( $rating_counts );
$product->set_review_count( $review_count );
$product->set_props( array(
'featured' => get_post_meta( $id, '_featured', true ),
'catalog_visibility' => get_post_meta( $id, '_visibility', true ),
@ -750,7 +755,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_D
* @param int $limit Limit of results.
* @return string
*/
function get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit ) {
public function get_related_products_query( $cats_array, $tags_array, $exclude_ids, $limit ) {
global $wpdb;
// Arrays to string.
@ -812,7 +817,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_D
* @param int|null $stock_quantity
* @param string $operation set, increase and decrease.
*/
function update_product_stock( $product_id_with_stock, $stock_quantity = null, $operation = 'set' ) {
public function update_product_stock( $product_id_with_stock, $stock_quantity = null, $operation = 'set' ) {
global $wpdb;
add_post_meta( $product_id_with_stock, '_stock', 0, true );
@ -832,4 +837,34 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_CPT implements WC_Object_D
wp_cache_delete( $product_id_with_stock, 'post_meta' );
}
/**
* Update a products average rating meta.
*
* @since 2.7.0
* @param WC_Product $product
*/
public function update_average_rating( $product ) {
update_post_meta( $product->get_id(), '_wc_average_rating', $product->get_average_rating( 'edit' ) );
}
/**
* Update a products review count meta.
*
* @since 2.7.0
* @param WC_Product $product
*/
public function update_review_count( $product ) {
update_post_meta( $product->get_id(), '_wc_review_count', $product->get_review_count( 'edit' ) );
}
/**
* Update a products rating counts.
*
* @since 2.7.0
* @param WC_Product $product
*/
public function update_rating_counts( $product ) {
update_post_meta( $product->get_id(), '_wc_rating_count', $product->get_rating_counts( 'edit' ) );
}
}

View File

@ -41,4 +41,31 @@ class WC_Product_Grouped_Data_Store_CPT extends WC_Product_Data_Store_CPT implem
parent::update_post_meta( $product );
}
/**
* Sync grouped product prices with children.
*
* @since 2.7.0
* @param WC_Product|int $product
*/
public function sync_price( &$product ) {
global $wpdb;
$children_ids = get_posts( array(
'post_parent' => $product->get_id(),
'post_type' => 'product',
'fields' => 'ids',
) );
$prices = $children_ids ? array_unique( $wpdb->get_col( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = '_price' AND post_id IN ( " . implode( ',', array_map( 'absint', $children_ids ) ) . " )" ) ) : array();
delete_post_meta( $product->get_id(), '_price' );
delete_transient( 'wc_var_prices_' . $product->get_id() );
if ( $prices ) {
sort( $prices );
// To allow sorting and filtering by multiple values, we have no choice but to store child prices in this manner.
foreach ( $prices as $price ) {
add_post_meta( $product->get_id(), '_price', $price, false );
}
}
}
}

View File

@ -10,7 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Class
* @author WooThemes
*/
class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT implements WC_Object_Data_Store, WC_Product_Variable_Data_Store {
class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT implements WC_Object_Data_Store, WC_Product_Variable_Data_Store_Interface {
/**
* Cached & hashed prices array for child variations.
@ -196,7 +196,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
$sale_prices = array();
$variation_ids = $product->get_visible_children();
foreach ( $variation_ids as $variation_id ) {
if ( $variation = wc_get_product( $variation_id ) ) { // @todo loop from missing args? remember?
if ( $variation = wc_get_product( $variation_id ) ) {
$price = apply_filters( 'woocommerce_variation_prices_price', $variation->get_price(), $variation, $product );
$regular_price = apply_filters( 'woocommerce_variation_prices_regular_price', $variation->get_regular_price(), $variation, $product );
$sale_price = apply_filters( 'woocommerce_variation_prices_sale_price', $variation->get_sale_price(), $variation, $product );

View File

@ -12,7 +12,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Interface
* @author WooThemes
*/
interface WC_Product_Data_Store {
interface WC_Product_Data_Store_Interface {
/**
* Returns an array of on sale products, as an array of objects with an

View File

@ -12,7 +12,7 @@ if ( ! defined( 'ABSPATH' ) ) {
* @category Interface
* @author WooThemes
*/
interface WC_Product_Variable_Data_Store {
interface WC_Product_Variable_Data_Store_Interface {
/**
* Does a child have a weight set?
*

View File

@ -205,7 +205,7 @@ function wc_cart_totals_shipping_html() {
if ( sizeof( $packages ) > 1 ) {
foreach ( $package['contents'] as $item_id => $values ) {
$product_names[] = $values['data']->get_title() . ' &times;' . $values['quantity'];
$product_names[] = $values['data']->get_name() . ' &times;' . $values['quantity'];
}
}

View File

@ -481,18 +481,6 @@ function wc_scheduled_sales() {
}
$product->save();
$parent = $product->get_parent_id();
// Sync parent
if ( $parent ) {
// Clear prices transient for variable products.
delete_transient( 'wc_var_prices_' . $parent );
// Grouped products need syncing via a function
if ( $product->is_type( 'simple' ) ) {
$product->grouped_product_sync();
}
}
}
delete_transient( 'wc_products_onsale' );
@ -509,19 +497,6 @@ function wc_scheduled_sales() {
$product->set_date_on_sale_to( '' );
$product->set_date_on_sale_from( '' );
$product->save();
$parent = $product->get_parent_id();
// Sync parent
if ( $parent ) {
// Clear prices transient for variable products.
delete_transient( 'wc_var_prices_' . $parent );
// Grouped products need syncing via a function
if ( $product->is_type( 'simple' ) ) {
$product->grouped_product_sync();
}
}
}
WC_Cache_Helper::get_transient_version( 'product', true );

View File

@ -975,7 +975,7 @@ if ( ! function_exists( 'woocommerce_grouped_add_to_cart' ) ) {
wc_get_template( 'single-product/add-to-cart/grouped.php', array(
'grouped_product' => $product,
'grouped_products' => $product->get_children(),
'grouped_products' => array_filter( array_map( 'wc_get_product', $product->get_children() ), 'wc_products_array_filter_visible' ),
'quantites_required' => false,
) );
}
@ -1369,18 +1369,31 @@ if ( ! function_exists( 'woocommerce_cross_sell_display' ) ) {
/**
* Output the cart cross-sells.
*
* @param int $posts_per_page (default: 2)
* @param int $limit (default: 2)
* @param int $columns (default: 2)
* @param string $orderby (default: 'rand')
* @param string $order (default: 'desc')
*/
function woocommerce_cross_sell_display( $posts_per_page = 2, $columns = 2, $orderby = 'rand' ) {
function woocommerce_cross_sell_display( $limit = 2, $columns = 2, $orderby = 'rand', $order = 'desc' ) {
if ( is_checkout() ) {
return;
}
// Get visble cross sells then sort them at random.
$cross_sells = array_filter( array_map( 'wc_get_product', WC()->cart->get_cross_sells() ), 'wc_products_array_filter_visible' );
// Handle orderby and limit results.
$orderby = apply_filters( 'woocommerce_cross_sells_orderby', $orderby );
$cross_sells = wc_products_array_orderby( $cross_sells, $orderby, $order );
$limit = apply_filters( 'woocommerce_cross_sells_total', $limit );
$cross_sells = $limit > 0 ? array_slice( $cross_sells, 0, $limit ) : $cross_sells;
wc_get_template( 'cart/cross-sells.php', array(
'posts_per_page' => $posts_per_page,
'orderby' => $orderby,
'columns' => $columns,
'cross_sells' => $cross_sells,
// Not used now, but used in previous version of up-sells.php.
'posts_per_page' => $limit,
'orderby' => $orderby,
'columns' => $columns,
) );
}
}

View File

@ -437,7 +437,7 @@ function wc_get_customer_available_downloads( $customer_id ) {
// Download name will be 'Product Name' for products with a single downloadable file, and 'Product Name - File X' for products with multiple files
$download_name = apply_filters(
'woocommerce_downloadable_product_name',
$_product->get_title() . ' &ndash; ' . $download_file['name'],
$_product->get_name() . ' &ndash; ' . $download_file['name'],
$_product,
$result->download_id,
$file_number
@ -455,7 +455,7 @@ function wc_get_customer_available_downloads( $customer_id ) {
),
'download_id' => $result->download_id,
'product_id' => $_product->get_id(),
'product_name' => $_product->get_title(),
'product_name' => $_product->get_name(),
'download_name' => $download_name,
'order_id' => $order->get_id(),
'order_key' => $order->get_order_key(),

View File

@ -79,7 +79,7 @@ class WC_Widget_Recent_Reviews extends WC_Widget {
echo $_product->get_image();
echo $_product->get_title() . '</a>';
echo $_product->get_name() . '</a>';
echo $rating_html;

View File

@ -78,9 +78,9 @@ do_action( 'woocommerce_before_cart' ); ?>
<td class="product-name" data-title="<?php _e( 'Product', 'woocommerce' ); ?>">
<?php
if ( ! $product_permalink ) {
echo apply_filters( 'woocommerce_cart_item_name', $_product->get_title(), $cart_item, $cart_item_key ) . '&nbsp;';
echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . '&nbsp;';
} else {
echo apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $_product->get_title() ), $cart_item, $cart_item_key );
echo apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $_product->get_name() ), $cart_item, $cart_item_key );
}
// Meta data

View File

@ -13,34 +13,14 @@
* @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes
* @package WooCommerce/Templates
* @version 1.6.4
* @version 2.7.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
global $product, $woocommerce_loop;
if ( ! $crosssells = WC()->cart->get_cross_sells() ) {
return;
}
$args = array(
'post_type' => 'product',
'ignore_sticky_posts' => 1,
'no_found_rows' => 1,
'posts_per_page' => apply_filters( 'woocommerce_cross_sells_total', $posts_per_page ),
'orderby' => $orderby,
'post__in' => $crosssells,
'meta_query' => WC()->query->get_meta_query(),
);
$products = new WP_Query( $args );
$woocommerce_loop['name'] = 'cross-sells';
$woocommerce_loop['columns'] = apply_filters( 'woocommerce_cross_sells_columns', $columns );
if ( $products->have_posts() ) : ?>
if ( $cross_sells ) : ?>
<div class="cross-sells">
@ -48,11 +28,16 @@ if ( $products->have_posts() ) : ?>
<?php woocommerce_product_loop_start(); ?>
<?php while ( $products->have_posts() ) : $products->the_post(); ?>
<?php foreach ( $cross_sells as $cross_sell ) : ?>
<?php wc_get_template_part( 'content', 'product' ); ?>
<?php
$post_object = get_post( $cross_sell->get_id() );
<?php endwhile; // end of the loop. ?>
setup_postdata( $GLOBALS['post'] =& $post_object );
wc_get_template_part( 'content', 'product' ); ?>
<?php endforeach; ?>
<?php woocommerce_product_loop_end(); ?>
@ -60,4 +45,4 @@ if ( $products->have_posts() ) : ?>
<?php endif;
wp_reset_query();
wp_reset_postdata();

View File

@ -38,7 +38,7 @@ if ( ! defined( 'ABSPATH' ) ) {
$product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_widget_cart_item_visible', true, $cart_item, $cart_item_key ) ) {
$product_name = apply_filters( 'woocommerce_cart_item_name', $_product->get_title(), $cart_item, $cart_item_key );
$product_name = apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key );
$thumbnail = apply_filters( 'woocommerce_cart_item_thumbnail', $_product->get_image(), $cart_item, $cart_item_key );
$product_price = apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key );
$product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );

View File

@ -38,7 +38,7 @@ if ( ! defined( 'ABSPATH' ) ) {
?>
<tr class="<?php echo esc_attr( apply_filters( 'woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key ) ); ?>">
<td class="product-name">
<?php echo apply_filters( 'woocommerce_cart_item_name', $_product->get_title(), $cart_item, $cart_item_key ) . '&nbsp;'; ?>
<?php echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . '&nbsp;'; ?>
<?php echo apply_filters( 'woocommerce_checkout_cart_item_quantity', ' <strong class="product-quantity">' . sprintf( '&times; %s', $cart_item['quantity'] ) . '</strong>', $cart_item, $cart_item_key ); ?>
<?php echo WC()->cart->get_item_data( $cart_item ); ?>
</td>

View File

@ -26,7 +26,7 @@ global $product; ?>
<li>
<a href="<?php echo esc_url( $product->get_permalink() ); ?>">
<?php echo $product->get_image(); ?>
<span class="product-title"><?php echo $product->get_title(); ?></span>
<span class="product-title"><?php echo $product->get_name(); ?></span>
</a>
<?php if ( ! empty( $show_rating ) ) : ?>
<?php echo wc_get_rating_html( $product->get_average_rating() ); ?>

View File

@ -13,74 +13,58 @@
* @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes
* @package WooCommerce/Templates
* @version 2.1.7
* @version 2.7.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
global $product, $post;
$parent_product_post = $post;
do_action( 'woocommerce_before_add_to_cart_form' ); ?>
<form class="cart" method="post" enctype='multipart/form-data'>
<table cellspacing="0" class="group_table">
<tbody>
<?php
foreach ( $grouped_products as $product_id ) :
if ( ! $product = wc_get_product( $product_id ) ) {
continue;
}
foreach ( $grouped_products as $grouped_product ) {
$post_object = get_post( $grouped_product->get_id() );
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && ! $product->is_in_stock() ) {
continue;
}
$post = $product->post;
setup_postdata( $post );
setup_postdata( $GLOBALS['post'] =& $post_object );
?>
<tr>
<td>
<?php if ( $product->is_sold_individually() || ! $product->is_purchasable() ) : ?>
<?php if ( $grouped_product->is_sold_individually() || ! $grouped_product->is_purchasable() ) : ?>
<?php woocommerce_template_loop_add_to_cart(); ?>
<?php else : ?>
<?php
$quantites_required = true;
woocommerce_quantity_input( array(
'input_name' => 'quantity[' . $product_id . ']',
'input_value' => ( isset( $_POST['quantity'][ $product_id ] ) ? wc_stock_amount( $_POST['quantity'][ $product_id ] ) : 0 ),
'min_value' => apply_filters( 'woocommerce_quantity_input_min', 0, $product ),
'max_value' => apply_filters( 'woocommerce_quantity_input_max', $product->backorders_allowed() ? '' : $product->get_stock_quantity(), $product ),
'input_name' => 'quantity[' . $grouped_product->get_id() . ']',
'input_value' => ( isset( $_POST['quantity'][ $grouped_product->get_id() ] ) ? wc_stock_amount( $_POST['quantity'][ $grouped_product->get_id() ] ) : 0 ),
'min_value' => apply_filters( 'woocommerce_quantity_input_min', 0, $grouped_product ),
'max_value' => apply_filters( 'woocommerce_quantity_input_max', $grouped_product->backorders_allowed() || ! $grouped_product->get_manage_stock() ? '' : $grouped_product->get_stock_quantity(), $grouped_product ),
) );
?>
<?php endif; ?>
</td>
<td class="label">
<label for="product-<?php echo $product_id; ?>">
<?php echo $product->is_visible() ? '<a href="' . esc_url( apply_filters( 'woocommerce_grouped_product_list_link', get_permalink(), $product_id ) ) . '">' . get_the_title() . '</a>' : get_the_title(); ?>
<label for="product-<?php echo $grouped_product->get_id(); ?>">
<?php echo $product->is_visible() ? '<a href="' . esc_url( apply_filters( 'woocommerce_grouped_product_list_link', get_permalink(), $grouped_product->get_id() ) ) . '">' . get_the_title() . '</a>' : get_the_title(); ?>
</label>
</td>
<?php do_action( 'woocommerce_grouped_product_list_before_price', $product ); ?>
<?php do_action( 'woocommerce_grouped_product_list_before_price', $grouped_product ); ?>
<td class="price">
<?php
echo $product->get_price_html();
echo wc_get_stock_html( $product );
echo $grouped_product->get_price_html();
echo wc_get_stock_html( $grouped_product );
?>
</td>
</tr>
<?php
endforeach;
// Reset to parent grouped product
$post = $parent_product_post;
$product = wc_get_product( $parent_product_post->ID );
setup_postdata( $parent_product_post );
}
wp_reset_postdata();
?>
</tbody>
</table>

View File

@ -200,7 +200,7 @@ class WC_Helper_Product {
update_post_meta( $variation_id, '_regular_price', '15' );
// General meta
update_post_meta( $variation_id, '_sku', 'DUMMY SKU VARIABLE SMALL' );
update_post_meta( $variation_id, '_sku', 'DUMMY SKU VARIABLE LARGE' );
update_post_meta( $variation_id, '_manage_stock', 'no' );
update_post_meta( $variation_id, '_downloadable', 'no' );
update_post_meta( $variation_id, '_virtual', 'no' );

View File

@ -41,11 +41,11 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$product = WC_Helper_Product::create_variation_product();
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) );
$variations = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 2, count( $variations ) );
$this->assertEquals( 'DUMMY SKU VARIABLE SMALL', $variations[0]['sku'] );
$this->assertEquals( 'size', $variations[0]['attributes'][0]['name'] );
$product->delete( true );
}
/**
@ -58,6 +58,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$product = WC_Helper_Product::create_variation_product();
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) );
$this->assertEquals( 401, $response->get_status() );
$product->delete( true );
}
/**
@ -77,6 +78,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( $variation_id, $variation['id'] );
$this->assertEquals( 'size', $variation['attributes'][0]['name'] );
$product->delete( true );
}
/**
@ -91,6 +93,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$variation_id = $children[0];
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations/' . $variation_id ) );
$this->assertEquals( 401, $response->get_status() );
$product->delete( true );
}
/**
@ -112,6 +115,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) );
$variations = $response->get_data();
$this->assertEquals( 1, count( $variations ) );
$product->delete( true );
}
/**
@ -129,6 +133,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$request->set_param( 'force', true );
$response = $this->server->dispatch( $request );
$this->assertEquals( 401, $response->get_status() );
$product->delete( true );
}
/**
@ -143,6 +148,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$request->set_param( 'force', true );
$response = $this->server->dispatch( $request );
$this->assertEquals( 404, $response->get_status() );
$product->delete( true );
}
/**
@ -184,6 +190,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$this->assertContains( 'Dr1Bczxq4q', $variation['image'][0]['src'] );
$this->assertContains( 'test upload image', $variation['image'][0]['alt'] );
$product->delete( true );
}
/**
@ -203,6 +210,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
) );
$response = $this->server->dispatch( $request );
$this->assertEquals( 401, $response->get_status() );
$product->delete( true );
}
/**
@ -219,6 +227,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
) );
$response = $this->server->dispatch( $request );
$this->assertEquals( 400, $response->get_status() );
$product->delete( true );
}
/**
@ -254,6 +263,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/products/' . $product->get_id() . '/variations' ) );
$variations = $response->get_data();
$this->assertEquals( 3, count( $variations ) );
$product->delete( true );
}
/**
@ -274,6 +284,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
) );
$response = $this->server->dispatch( $request );
$this->assertEquals( 401, $response->get_status() );
$product->delete( true );
}
/**
@ -319,6 +330,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$data = $response->get_data();
$this->assertEquals( 2, count( $data ) );
$product->delete( true );
}
/**
@ -368,6 +380,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$this->assertArrayHasKey( 'shipping_class_id', $properties );
$this->assertArrayHasKey( 'image', $properties );
$this->assertArrayHasKey( 'attributes', $properties );
$product->delete( true );
}
}

View File

@ -198,6 +198,7 @@ class Products_API extends WC_REST_Unit_Test_Case {
$this->assertEquals( array( 'small' ), $data['attributes'][0]['options'] );
$this->assertEquals( array( 'red', 'yellow' ), $data['attributes'][1]['options'] );
$product->delete( true );
// test external product
$product = WC_Helper_Product::create_external_product();
@ -217,6 +218,7 @@ class Products_API extends WC_REST_Unit_Test_Case {
$this->assertEquals( 'Test API Update', $data['button_text'] );
$this->assertEquals( 'http://automattic.com', $data['external_url'] );
$product->delete( true );
}
/**
@ -233,6 +235,7 @@ class Products_API extends WC_REST_Unit_Test_Case {
) );
$response = $this->server->dispatch( $request );
$this->assertEquals( 401, $response->get_status() );
$product->delete( true );
}
/**
@ -249,6 +252,7 @@ class Products_API extends WC_REST_Unit_Test_Case {
) );
$response = $this->server->dispatch( $request );
$this->assertEquals( 400, $response->get_status() );
$product->delete( true );
}
/**
@ -408,6 +412,8 @@ class Products_API extends WC_REST_Unit_Test_Case {
$data = $response->get_data();
$this->assertEquals( 3, count( $data ) );
$product->delete( true );
$product_2->delete( true );
}
/*
@ -506,6 +512,7 @@ class Products_API extends WC_REST_Unit_Test_Case {
$data = $response->get_data();
$properties = $data['schema']['properties'];
$this->assertEquals( 61, count( $properties ) );
$product->delete( true );
}
}

View File

@ -232,6 +232,7 @@ class WC_Tests_Cart extends WC_Unit_Test_Case {
// Clean up the cart
WC()->cart->empty_cart();
$product->delete( true );
}
/**

View File

@ -125,7 +125,7 @@ class WC_Tests_Cart_Functions extends WC_Unit_Test_Case {
WC()->cart->add_to_cart( $product->get_id(), 1 );
$this->expectOutputString( wc_price( $product->price ), wc_cart_totals_subtotal_html() );
$this->expectOutputString( wc_price( $product->get_price( 'edit' ) ), wc_cart_totals_subtotal_html() );
WC_Helper_Product::delete_product( $product->get_id() );
}
@ -161,21 +161,21 @@ class WC_Tests_Cart_Functions extends WC_Unit_Test_Case {
$product = WC_Helper_Product::create_simple_product();
$message = wc_add_to_cart_message( array( $product->get_id() => 1 ), false, true );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View Cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$message = wc_add_to_cart_message( array( $product->get_id() => 3 ), false, true );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View Cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$message = wc_add_to_cart_message( array( $product->get_id() => 1 ), true, true );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View Cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$message = wc_add_to_cart_message( array( $product->get_id() => 3 ), true, true );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View Cart</a> 3 &times; &ldquo;Dummy Product&rdquo; have been added to your cart.', $message );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View cart</a> 3 &times; &ldquo;Dummy Product&rdquo; have been added to your cart.', $message );
$message = wc_add_to_cart_message( $product->get_id(), false, true );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View Cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$message = wc_add_to_cart_message( $product->get_id(), true, true );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View Cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
$this->assertEquals( '<a href="http://example.org" class="button wc-forward">View cart</a> &ldquo;Dummy Product&rdquo; has been added to your cart.', $message );
}
}

View File

@ -222,6 +222,7 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$expected_attributes = array( 'pa_size' => array( 'small', 'large' ) );
$this->assertEquals( $expected_attributes, $product->get_variation_attributes() );
$product->delete();
}
/**
@ -260,6 +261,7 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$this->assertEquals( 'Variation #1 of Dummy Variable CRUD Product', $variation->get_name() );
$this->assertEquals( 'CRUD DUMMY SKU VARIABLE GREEN', $variation->get_sku() );
$this->assertEquals( 10, $variation->get_price() );
$product->delete();
$product = new WC_Product_Variable( $product->get_id() );
$children = $product->get_children();
@ -283,6 +285,7 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$this->assertEquals( 'Variation #2 of Dummy Variable CRUD Product', $variation_2->get_name() );
$this->assertEquals( 'CRUD DUMMY SKU VARIABLE RED', $variation_2->get_sku() );
$this->assertEquals( 10, $variation_2->get_price() );
$product->delete();
$product = new WC_Product_Variable( $product->get_id() );
$children = $product->get_children();
@ -297,6 +300,7 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$variation_2->set_sale_price( 9.99 );
$variation_2->set_date_on_sale_to( '32532537600' );
$variation_2->save();
$product->delete();
$product = new WC_Product_Variable( $product->get_id() );
$expected_prices['price'][ $children[0] ] = 10.00;
@ -312,6 +316,7 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$product->set_name( 'Renamed Variable Product' );
$product->save();
$this->assertEquals( 'Renamed Variable Product', $product->get_name() );
$product->delete();
}
}

View File

@ -8,11 +8,21 @@ class WC_Tests_Product_Data extends WC_Unit_Test_Case {
/**
* Test product setters and getters
* @todo needs tests for attributes
* @since 2.7.0
*/
public function test_product_getters_and_setters() {
global $wpdb;
$attributes = array();
$attribute = new WC_Product_Attribute();
$attribute->set_id( 0 );
$attribute->set_name( 'Test Attribute' );
$attribute->set_options( array( 'Fish', 'Fingers' ) );
$attribute->set_position( 0 );
$attribute->set_visible( true );
$attribute->set_variation( false );
$attributes['test-attribute'] = $attribute;
$getters_and_setters = array(
'name' => 'Test',
'slug' => 'test',
@ -48,6 +58,7 @@ class WC_Tests_Product_Data extends WC_Unit_Test_Case {
'gallery_image_ids' => array(),
'download_expiry' => -1,
'download_limit' => 5,
'attributes' => $attributes,
);
$product = new WC_Product();
foreach ( $getters_and_setters as $function => $value ) {

View File

@ -24,6 +24,11 @@ class WC_Tests_Product_Factory extends WC_Unit_Test_Case {
$this->assertEquals( 'grouped', WC()->product_factory->get_product_type( $grouped->get_id() ) );
$this->assertEquals( 'variable', WC()->product_factory->get_product_type( $variable->get_id() ) );
$this->assertEquals( 'variation', WC()->product_factory->get_product_type( $child_id ) );
$simple->delete( true );
$external->delete( true );
$grouped->delete( true );
$variable->delete( true );
}
/**
@ -47,7 +52,7 @@ class WC_Tests_Product_Factory extends WC_Unit_Test_Case {
function test_get_product() {
$test_product = WC_Helper_Product::create_simple_product();
$get_product = WC()->product_factory->get_product( $test_product->get_id() );
$this->assertEquals( $test_product, $get_product );
$this->assertEquals( $test_product->get_data(), $get_product->get_data() );
}
/**

View File

@ -104,6 +104,8 @@ class WC_Tests_Product_Functions extends WC_Unit_Test_Case {
// test exclude
$products = wc_get_products( array( 'return' => 'ids', 'limit' => 200, 'exclude' => array( $product->get_id() ) ) );
$this->assertNotContains( $product->get_id(), $products );
$variation->delete( true );
}
/**

View File

@ -51,7 +51,7 @@ class WC_Tests_Product_Simple extends WC_Unit_Test_Case {
// Create product
$product = WC_Helper_Product::create_simple_product();
$this->assertEquals( 'Dummy Product', $product->get_title() );
$this->assertEquals( 'Dummy Product', $product->get_name() );
// Delete product
WC_Helper_Product::delete_product( $product->get_id() );

View File

@ -287,8 +287,8 @@ final class WooCommerce {
include_once( WC_ABSPATH . 'includes/class-wc-data-store.php' ); // WC_Data_Store for CRUD
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-object-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-coupon-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-product-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/interface-wc-product-variable-data-store.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/wc-product-data-store-interface.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/wc-product-variable-data-store-interface.php' );
include_once( WC_ABSPATH . 'includes/data-stores/interfaces/class-wc-payment-token-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-coupon-data-store-cpt.php' );