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(), 'attributes' => array(),
'default_attributes' => array(), 'default_attributes' => array(),
'menu_order' => 0, 'menu_order' => 0,
'post_password' => '',
'virtual' => false, 'virtual' => false,
'downloadable' => false, 'downloadable' => false,
'category_ids' => array(), 'category_ids' => array(),
@ -549,6 +550,17 @@ class WC_Product extends WC_Abstract_Legacy_Product {
return $this->get_prop( 'menu_order', $context ); 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. * Get category ids.
* *
@ -1124,6 +1136,16 @@ class WC_Product extends WC_Abstract_Legacy_Product {
$this->set_prop( 'menu_order', intval( $menu_order ) ); $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. * 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', 'comment_status' => $product->get_reviews_allowed() ? 'open' : 'closed',
'ping_status' => 'closed', 'ping_status' => 'closed',
'menu_order' => $product->get_menu_order(), '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' => 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_date_gmt' => gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getTimestamp() ),
'post_name' => $product->get_slug( 'edit' ), '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, 'short_description' => $post_object->post_excerpt,
'parent_id' => $post_object->post_parent, 'parent_id' => $post_object->post_parent,
'menu_order' => $post_object->menu_order, 'menu_order' => $post_object->menu_order,
'post_password' => $post_object->post_password,
'reviews_allowed' => 'open' === $post_object->comment_status, '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', 'comment_status' => $product->get_reviews_allowed( 'edit' ) ? 'open' : 'closed',
'post_status' => $product->get_status( 'edit' ) ? $product->get_status( 'edit' ) : 'publish', 'post_status' => $product->get_status( 'edit' ) ? $product->get_status( 'edit' ) : 'publish',
'menu_order' => $product->get_menu_order( 'edit' ), 'menu_order' => $product->get_menu_order( 'edit' ),
'post_password' => $product->get_post_password( 'edit' ),
'post_name' => $product->get_slug( 'edit' ), 'post_name' => $product->get_slug( 'edit' ),
'post_type' => 'product', 'post_type' => 'product',
); );

View File

@ -494,10 +494,8 @@ function wc_product_post_class( $classes, $class = '', $post_id = 0 ) {
if ( $product->get_type() ) { if ( $product->get_type() ) {
$classes[] = 'product-type-' . $product->get_type(); $classes[] = 'product-type-' . $product->get_type();
} }
if ( $product->is_type( 'variable' ) ) { if ( $product->is_type( 'variable' ) && $product->get_default_attributes() ) {
if ( ! $product->get_default_attributes() ) { $classes[] = 'has-default-attributes';
$classes[] = 'has-default-attributes';
}
} }
} }
@ -546,7 +544,11 @@ function wc_get_product_taxonomy_class( $term_ids, $taxonomy ) {
/** /**
* Retrieves the classes for the post div as an array. * 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 * @since 3.4.0
* @param string|array $class One or more classes to add to the class list. * @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' ) ) { if ( is_a( $product_id, 'WC_Product' ) ) {
$product = $product_id; $product = $product_id;
$product_id = $product_id->get_id(); $product_id = $product_id->get_id();
$post = get_post( $product_id );
} else { } else {
$post = get_post( $product_id ); $product = wc_get_product( $product_id );
$product = wc_get_product( $post->ID );
} }
$classes = array(); if ( ! is_array( $class ) ) {
$classes = preg_split( '#\s+#', $class );
if ( $class ) {
if ( ! is_array( $class ) ) {
$class = preg_split( '#\s+#', $class );
}
$classes = array_map( 'esc_attr', $class );
} else { } else {
// Ensure that we always coerce class to being an array. $classes = $class;
$class = array();
} }
if ( ! $post || ! $product ) { if ( ! $product ) {
return $classes; return array_map( 'esc_attr', $classes );
} }
$classes[] = 'post-' . $post->ID; $classes = array_merge(
if ( ! is_admin() ) { $classes,
$classes[] = $post->post_type; array(
} 'product',
$classes[] = 'type-' . $post->post_type; 'type-product',
$classes[] = 'status-' . $post->post_status; '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 ( $product->get_image_id() ) {
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 ) {
$classes[] = 'has-post-thumbnail'; $classes[] = 'has-post-thumbnail';
} }
if ( $product->get_post_password() ) {
// Sticky for Sticky Posts. $classes[] = post_password_required( $product->get_id() ) ? 'post-password-required' : 'post-password-protected';
if ( is_sticky( $post->ID ) ) { }
if ( is_home() && ! is_paged() ) { if ( $product->is_on_sale() ) {
$classes[] = 'sticky'; $classes[] = 'sale';
} elseif ( is_admin() ) { }
$classes[] = 'status-sticky'; 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. // Include attributes and any extra taxonomies only if enabled via the hook - this is a performance issue.
$classes[] = 'hentry';
// Include attributes and any extra taxonomy.
if ( apply_filters( 'woocommerce_get_product_class_include_taxonomies', false ) ) { if ( apply_filters( 'woocommerce_get_product_class_include_taxonomies', false ) ) {
$taxonomies = get_taxonomies( array( 'public' => true ) ); $taxonomies = get_taxonomies( array( 'public' => true ) );
$type = 'variation' === $product->get_type() ? 'product_variation' : 'product';
foreach ( (array) $taxonomies as $taxonomy ) { foreach ( (array) $taxonomies as $taxonomy ) {
if ( is_object_in_taxonomy( $post->post_type, $taxonomy ) && ! in_array( $taxonomy, array( 'product_cat', 'product_tag' ), true ) ) { 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( $post->ID, $taxonomy ), $taxonomy ) ); $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. // If using `wc_get_product_class` instead of `get_post_class`, we don't need to hook `wc_product_post_class` function.
$classes = array_merge( $classes, wc_get_product_taxonomy_class( $product->get_tag_ids(), 'product_tag' ) ); $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/ * @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce/Templates * @package WooCommerce/Templates
* @version 3.4.0 * @version 3.6.0
*/ */
defined( 'ABSPATH' ) || exit; defined( 'ABSPATH' ) || exit;
@ -24,7 +24,7 @@ if ( empty( $product ) || ! $product->is_visible() ) {
return; return;
} }
?> ?>
<li <?php wc_product_class(); ?>> <li <?php wc_product_class( '', $product ); ?>>
<?php <?php
/** /**
* Hook: woocommerce_before_shop_loop_item. * Hook: woocommerce_before_shop_loop_item.

View File

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

View File

@ -39,7 +39,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
$post = $post_object; // WPCS: override ok. $post = $post_object; // WPCS: override ok.
setup_postdata( $post ); 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. // Output columns for each product.
foreach ( $grouped_product_columns as $column_id ) { foreach ( $grouped_product_columns as $column_id ) {

View File

@ -1,11 +1,14 @@
<?php <?php
/** /**
* Test template funcitons. * Test template funcitons.
* *
* @package WooCommerce/Tests/Templates * @package WooCommerce/Tests/Templates
* @since 3.4.0 * @since 3.4.0
*/ */
/**
* WC_Tests_Template_Functions class.
*/
class WC_Tests_Template_Functions extends WC_Unit_Test_Case { 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->set_category_ids( array( $category['term_id'] ) );
$product->save(); $product->save();
$product = wc_get_product( $product ); // Reload so status is current.
$expected = array( $expected = array(
'foo', 'foo',
'post-' . $product->get_id(),
'product', 'product',
'type-product', 'type-product',
'post-' . $product->get_id(),
'status-publish', 'status-publish',
'product_cat-some-category',
'first', 'first',
'instock', 'instock',
'product_cat-some-category',
'sale', 'sale',
'virtual', 'virtual',
'purchasable', 'purchasable',
'product-type-simple', '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. // All taxonomies.
add_filter( 'woocommerce_get_product_class_include_taxonomies', '__return_true' ); add_filter( 'woocommerce_get_product_class_include_taxonomies', '__return_true' );
$expected = array( $expected = array(
'foo', 'foo',
'post-' . $product->get_id(),
'product', 'product',
'type-product', 'type-product',
'post-' . $product->get_id(),
'status-publish', 'status-publish',
'product_cat-some-category',
'instock', 'instock',
'product_cat-some-category',
'sale', 'sale',
'virtual', 'virtual',
'purchasable', 'purchasable',
'product-type-simple', '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' ); add_filter( 'woocommerce_get_product_class_include_taxonomies', '__return_false' );
$product->delete( true ); $product->delete( true );
wp_delete_term( $category['term_id'], 'product_cat' ); 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() { 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>' ); $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(); 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() { public function test_wc_dropdown_variation_attribute_options_should_return_attributes_list() {
$product = WC_Helper_Product::create_variation_product(); $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() { public function test_wc_dropdown_variation_attribute_options_should_return_attributes_list_and_selected_element() {
$product = WC_Helper_Product::create_variation_product(); $product = WC_Helper_Product::create_variation_product();
$_REQUEST['attribute_pa_size'] = 'large'; $_REQUEST['attribute_pa_size'] = 'large';