Merge branch 'performance/wc-get-product-class'

This commit is contained in:
Mike Jolley 2019-02-07 13:34:18 +00:00
commit 889cf64ac4
7 changed files with 130 additions and 77 deletions

View File

@ -88,6 +88,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
'attributes' => array(),
'default_attributes' => array(),
'menu_order' => 0,
'post_password' => '',
'virtual' => false,
'downloadable' => false,
'category_ids' => array(),
@ -549,6 +550,17 @@ class WC_Product extends WC_Abstract_Legacy_Product {
return $this->get_prop( 'menu_order', $context );
}
/**
* Get post password.
*
* @since 3.6.0
* @param string $context What the value is for. Valid values are view and edit.
* @return int
*/
public function get_post_password( $context = 'view' ) {
return $this->get_prop( 'post_password', $context );
}
/**
* Get category ids.
*
@ -1124,6 +1136,16 @@ class WC_Product extends WC_Abstract_Legacy_Product {
$this->set_prop( 'menu_order', intval( $menu_order ) );
}
/**
* Set post password.
*
* @since 3.6.0
* @param int $post_password Post password.
*/
public function set_post_password( $post_password ) {
$this->set_prop( 'post_password', $post_password );
}
/**
* Set the product categories.
*

View File

@ -111,6 +111,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'comment_status' => $product->get_reviews_allowed() ? 'open' : 'closed',
'ping_status' => 'closed',
'menu_order' => $product->get_menu_order(),
'post_password' => $product->get_post_password( 'edit' ),
'post_date' => gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getOffsetTimestamp() ),
'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getTimestamp() ),
'post_name' => $product->get_slug( 'edit' ),
@ -162,6 +163,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'short_description' => $post_object->post_excerpt,
'parent_id' => $post_object->post_parent,
'menu_order' => $post_object->menu_order,
'post_password' => $post_object->post_password,
'reviews_allowed' => 'open' === $post_object->comment_status,
)
);
@ -193,6 +195,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
'comment_status' => $product->get_reviews_allowed( 'edit' ) ? 'open' : 'closed',
'post_status' => $product->get_status( 'edit' ) ? $product->get_status( 'edit' ) : 'publish',
'menu_order' => $product->get_menu_order( 'edit' ),
'post_password' => $product->get_post_password( 'edit' ),
'post_name' => $product->get_slug( 'edit' ),
'post_type' => 'product',
);

View File

@ -494,12 +494,10 @@ function wc_product_post_class( $classes, $class = '', $post_id = 0 ) {
if ( $product->get_type() ) {
$classes[] = 'product-type-' . $product->get_type();
}
if ( $product->is_type( 'variable' ) ) {
if ( ! $product->get_default_attributes() ) {
if ( $product->is_type( 'variable' ) && $product->get_default_attributes() ) {
$classes[] = 'has-default-attributes';
}
}
}
$key = array_search( 'hentry', $classes, true );
if ( false !== $key ) {
@ -546,7 +544,11 @@ function wc_get_product_taxonomy_class( $term_ids, $taxonomy ) {
/**
* Retrieves the classes for the post div as an array.
*
* This method is clone from WordPress's get_post_class(), allowing removing taxonomies.
* This method was modified from WordPress's get_post_class() to allow the removal of taxonomies
* (for performance reasons).
*
* Previously wc_product_post_class was hooked into post_class. That still happens, but this function
* negates the need for it and thus unhooks it when running the post_class hook. @since 3.6.0
*
* @since 3.4.0
* @param string|array $class One or more classes to add to the class list.
@ -557,87 +559,96 @@ function wc_get_product_class( $class = '', $product_id = null ) {
if ( is_a( $product_id, 'WC_Product' ) ) {
$product = $product_id;
$product_id = $product_id->get_id();
$post = get_post( $product_id );
} else {
$post = get_post( $product_id );
$product = wc_get_product( $post->ID );
$product = wc_get_product( $product_id );
}
$classes = array();
if ( $class ) {
if ( ! is_array( $class ) ) {
$class = preg_split( '#\s+#', $class );
}
$classes = array_map( 'esc_attr', $class );
$classes = preg_split( '#\s+#', $class );
} else {
// Ensure that we always coerce class to being an array.
$class = array();
$classes = $class;
}
if ( ! $post || ! $product ) {
return $classes;
if ( ! $product ) {
return array_map( 'esc_attr', $classes );
}
$classes[] = 'post-' . $post->ID;
if ( ! is_admin() ) {
$classes[] = $post->post_type;
}
$classes[] = 'type-' . $post->post_type;
$classes[] = 'status-' . $post->post_status;
$classes = array_merge(
$classes,
array(
'product',
'type-product',
'post-' . $product->get_id(),
'status-' . $product->get_status(),
wc_get_loop_class(),
$product->get_stock_status(),
),
wc_get_product_taxonomy_class( $product->get_category_ids(), 'product_cat' ),
wc_get_product_taxonomy_class( $product->get_tag_ids(), 'product_tag' )
);
// Post format.
if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
$post_format = get_post_format( $post->ID );
if ( $post_format && ! is_wp_error( $post_format ) ) {
$classes[] = 'format-' . sanitize_html_class( $post_format );
} else {
$classes[] = 'format-standard';
}
}
// Post requires password.
$post_password_required = post_password_required( $post->ID );
if ( $post_password_required ) {
$classes[] = 'post-password-required';
} elseif ( ! empty( $post->post_password ) ) {
$classes[] = 'post-password-protected';
}
// Post thumbnails.
if ( current_theme_supports( 'post-thumbnails' ) && $product->get_image_id() && ! is_attachment( $post ) && ! $post_password_required ) {
if ( $product->get_image_id() ) {
$classes[] = 'has-post-thumbnail';
}
// Sticky for Sticky Posts.
if ( is_sticky( $post->ID ) ) {
if ( is_home() && ! is_paged() ) {
$classes[] = 'sticky';
} elseif ( is_admin() ) {
$classes[] = 'status-sticky';
if ( $product->get_post_password() ) {
$classes[] = post_password_required( $product->get_id() ) ? 'post-password-required' : 'post-password-protected';
}
if ( $product->is_on_sale() ) {
$classes[] = 'sale';
}
if ( $product->is_featured() ) {
$classes[] = 'featured';
}
if ( $product->is_downloadable() ) {
$classes[] = 'downloadable';
}
if ( $product->is_virtual() ) {
$classes[] = 'virtual';
}
if ( $product->is_sold_individually() ) {
$classes[] = 'sold-individually';
}
if ( $product->is_taxable() ) {
$classes[] = 'taxable';
}
if ( $product->is_shipping_taxable() ) {
$classes[] = 'shipping-taxable';
}
if ( $product->is_purchasable() ) {
$classes[] = 'purchasable';
}
if ( $product->get_type() ) {
$classes[] = 'product-type-' . $product->get_type();
}
if ( $product->is_type( 'variable' ) && $product->get_default_attributes() ) {
$classes[] = 'has-default-attributes';
}
// Hentry for hAtom compliance.
$classes[] = 'hentry';
// Include attributes and any extra taxonomy.
// Include attributes and any extra taxonomies only if enabled via the hook - this is a performance issue.
if ( apply_filters( 'woocommerce_get_product_class_include_taxonomies', false ) ) {
$taxonomies = get_taxonomies( array( 'public' => true ) );
$type = 'variation' === $product->get_type() ? 'product_variation' : 'product';
foreach ( (array) $taxonomies as $taxonomy ) {
if ( is_object_in_taxonomy( $post->post_type, $taxonomy ) && ! in_array( $taxonomy, array( 'product_cat', 'product_tag' ), true ) ) {
$classes = array_merge( $classes, wc_get_product_taxonomy_class( (array) get_the_terms( $post->ID, $taxonomy ), $taxonomy ) );
if ( is_object_in_taxonomy( $type, $taxonomy ) && ! in_array( $taxonomy, array( 'product_cat', 'product_tag' ), true ) ) {
$classes = array_merge( $classes, wc_get_product_taxonomy_class( (array) get_the_terms( $product->get_id(), $taxonomy ), $taxonomy ) );
}
}
}
// Categories.
$classes = array_merge( $classes, wc_get_product_taxonomy_class( $product->get_category_ids(), 'product_cat' ) );
// Tags.
$classes = array_merge( $classes, wc_get_product_taxonomy_class( $product->get_tag_ids(), 'product_tag' ) );
// If using `wc_get_product_class` instead of `get_post_class`, we don't need to hook `wc_product_post_class` function.
$filtered = has_filter( 'post_class', 'wc_product_post_class' );
return array_filter( array_unique( apply_filters( 'post_class', $classes, $class, $post->ID ) ) );
if ( $filtered ) {
remove_filter( 'post_class', 'wc_product_post_class', 20, 3 );
}
$classes = apply_filters( 'post_class', $classes, $class, $product->get_id() );
if ( $filtered ) {
add_filter( 'post_class', 'wc_product_post_class', 20, 3 );
}
return array_filter( array_map( 'esc_attr', array_unique( $classes ) ) );
}
/**

View File

@ -12,7 +12,7 @@
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce/Templates
* @version 3.4.0
* @version 3.6.0
*/
defined( 'ABSPATH' ) || exit;
@ -24,7 +24,7 @@ if ( empty( $product ) || ! $product->is_visible() ) {
return;
}
?>
<li <?php wc_product_class(); ?>>
<li <?php wc_product_class( '', $product ); ?>>
<?php
/**
* Hook: woocommerce_before_shop_loop_item.

View File

@ -12,11 +12,13 @@
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce/Templates
* @version 3.4.0
* @version 3.6.0
*/
defined( 'ABSPATH' ) || exit;
global $product;
/**
* Hook: woocommerce_before_single_product.
*
@ -29,7 +31,7 @@ if ( post_password_required() ) {
return;
}
?>
<div id="product-<?php the_ID(); ?>" <?php wc_product_class(); ?>>
<div id="product-<?php the_ID(); ?>" <?php wc_product_class( '', $product ); ?>>
<?php
/**

View File

@ -39,7 +39,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
$post = $post_object; // WPCS: override ok.
setup_postdata( $post );
echo '<tr id="product-' . esc_attr( $grouped_product_child->get_id() ) . '" class="woocommerce-grouped-product-list-item ' . esc_attr( implode( ' ', wc_get_product_class( '', $grouped_product_child->get_id() ) ) ) . '">';
echo '<tr id="product-' . esc_attr( $grouped_product_child->get_id() ) . '" class="woocommerce-grouped-product-list-item ' . esc_attr( implode( ' ', wc_get_product_class( '', $grouped_product_child ) ) ) . '">';
// Output columns for each product.
foreach ( $grouped_product_columns as $column_id ) {

View File

@ -1,11 +1,14 @@
<?php
/**
* Test template funcitons.
*
* @package WooCommerce/Tests/Templates
* @since 3.4.0
*/
/**
* WC_Tests_Template_Functions class.
*/
class WC_Tests_Template_Functions extends WC_Unit_Test_Case {
/**
@ -26,52 +29,61 @@ class WC_Tests_Template_Functions extends WC_Unit_Test_Case {
$product->set_category_ids( array( $category['term_id'] ) );
$product->save();
$product = wc_get_product( $product ); // Reload so status is current.
$expected = array(
'foo',
'post-' . $product->get_id(),
'product',
'type-product',
'post-' . $product->get_id(),
'status-publish',
'product_cat-some-category',
'first',
'instock',
'product_cat-some-category',
'sale',
'virtual',
'purchasable',
'product-type-simple',
);
$actual = array_values( wc_get_product_class( 'foo', $product ) );
$this->assertEquals( $expected, array_values( wc_get_product_class( 'foo', $product ) ) );
$this->assertEquals( $expected, $actual, print_r( $actual, true ) );
// All taxonomies.
add_filter( 'woocommerce_get_product_class_include_taxonomies', '__return_true' );
$expected = array(
'foo',
'post-' . $product->get_id(),
'product',
'type-product',
'post-' . $product->get_id(),
'status-publish',
'product_cat-some-category',
'instock',
'product_cat-some-category',
'sale',
'virtual',
'purchasable',
'product-type-simple',
);
$actual = array_values( wc_get_product_class( 'foo', $product ) );
$this->assertEquals( $expected, array_values( wc_get_product_class( 'foo', $product ) ) );
$this->assertEquals( $expected, $actual, print_r( $actual, true ) );
add_filter( 'woocommerce_get_product_class_include_taxonomies', '__return_false' );
$product->delete( true );
wp_delete_term( $category['term_id'], 'product_cat' );
}
/**
* Test: test_wc_dropdown_variation_attribute_options_no_attributes.
*/
public function test_wc_dropdown_variation_attribute_options_no_attributes() {
$this->expectOutputString( '<select id="" class="" name="attribute_" data-attribute_name="attribute_" data-show_option_none="yes"><option value="">Choose an option</option></select>' );
wc_dropdown_variation_attribute_options();
}
/**
* Test: test_wc_dropdown_variation_attribute_options_should_return_attributes_list.
*/
public function test_wc_dropdown_variation_attribute_options_should_return_attributes_list() {
$product = WC_Helper_Product::create_variation_product();
@ -85,6 +97,9 @@ class WC_Tests_Template_Functions extends WC_Unit_Test_Case {
);
}
/**
* Test: test_wc_dropdown_variation_attribute_options_should_return_attributes_list_and_selected_element.
*/
public function test_wc_dropdown_variation_attribute_options_should_return_attributes_list_and_selected_element() {
$product = WC_Helper_Product::create_variation_product();
$_REQUEST['attribute_pa_size'] = 'large';