diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index a9eae2af8d8..daacaee3f37 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -157,6 +157,34 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { $this->data['price'] = $this->data['price'] + $price; } + /** + * Returns the product categories. + * + * @deprecated 2.7.0 + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string + */ + public function get_categories( $sep = ', ', $before = '', $after = '' ) { + _deprecated_function( 'WC_Product::get_categories', '2.7', 'wc_get_product_category_list' ); + return wc_get_product_category_list( $this->get_id(), $sep, $before, $after ); + } + + /** + * Returns the product tags. + * + * @deprecated 2.7.0 + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return array + */ + public function get_tags( $sep = ', ', $before = '', $after = '' ) { + _deprecated_function( 'WC_Product::get_tags', '2.7', 'wc_get_product_tag_list' ); + return wc_get_product_tag_list( $this->get_id(), $sep, $before, $after ); + } + /** * Returns the availability of the product. * diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 82257f83981..7fdee53c660 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -37,7 +37,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'description' => '', 'short_description' => '', 'sku' => '', - 'price' => '', // @todo save and set this + 'price' => '', 'regular_price' => '', 'sale_price' => '', 'date_on_sale_from' => '', @@ -64,6 +64,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'menu_order' => 0, 'virtual' => false, // @todo 'downloadable' => false, // @todo + 'category_ids' => array(), + 'tag_ids' => array(), ); /** @@ -88,7 +90,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * * @param int|WC_Product|object $product Product to init. */ - public function __construct( $product = 0 ) { + function __construct( $product = 0 ) { parent::__construct( $product ); if ( is_numeric( $product ) && $product > 0 ) { $this->read( $product ); @@ -492,27 +494,23 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Returns the product categories. @todo store in class and save? + * Get category ids. * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return string + * @since 2.7.0 + * @return array */ - public function get_categories( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); + public function get_category_ids() { + return $this->data['category_ids']; } /** - * Returns the product tags. @todo store in class and save? + * Get tag ids. * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). + * @since 2.7.0 * @return array */ - public function get_tags( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + public function get_tag_ids() { + return $this->data['tag_ids']; } /* @@ -912,23 +910,23 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Set the product categories. @todo store in class and save? + * Set the product categories. * * @since 2.7.0 - * @param array $terms_id List of terms IDs. + * @param array $term_ids List of terms IDs. */ - public function set_categories( $terms_id ) { - //$this->save_taxonomy_terms( $terms_id, 'cat' ); + public function set_category_ids( $term_ids ) { + $this->data['category_ids'] = $this->sanitize_term_ids( $term_ids, 'product_cat' ); } /** - * Set the product tags. @todo store in class and save? + * Set the product tags. * * @since 2.7.0 - * @param array $terms_id List of terms IDs. + * @param array $term_ids List of terms IDs. */ - public function set_tags( $terms_id ) { - //$this->save_taxonomy_terms( $terms_id, 'tag' ); + public function set_tag_ids( $term_ids ) { + $this->data['tag_ids'] = $this->sanitize_term_ids( $term_ids, 'product_tag' ); } /* @@ -942,6 +940,42 @@ class WC_Product extends WC_Abstract_Legacy_Product { | on if the order exists yet). */ + /** + * Get and store terms from a taxonomy. + * + * @since 2.7.0 + * @param string $taxonomy Taxonomy name e.g. product_cat + * @return array of terms + */ + protected function get_term_ids( $taxonomy ) { + return wp_get_post_terms( $this->get_id(), $taxonomy, array( 'fields' => 'ids' ) ); + } + + /** + * Get term ids from either a list of names, ids, or terms. + * + * @since 2.7.0 + * @param array $terms + * @param string $taxonomy + */ + protected function sanitize_term_ids( $terms, $taxonomy ) { + $term_ids = array(); + foreach ( $terms as $term ) { + if ( is_object( $term ) ) { + $term_ids[] = $term->term_id; + } elseif ( is_integer( $term ) ) { + $term_ids[] = absint( $term ); + } else { + $term_object = get_term_by( 'name', $term, $taxonomy ); + + if ( $term_object && ! is_wp_error( $term_object ) ) { + $term_ids[] = $term_object->term_id; + } + } + } + return $term_ids; + } + /** * Reads a product from the database and sets its data to the class. * @@ -993,6 +1027,8 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'attributes' => get_post_meta( $id, '_product_attributes', true ), 'default_attributes' => get_post_meta( $id, '_default_attributes', true ), 'menu_order' => $post_object->menu_order, + 'category_ids' => $this->get_term_ids( 'product_cat' ), + 'tag_ids' => $this->get_term_ids( 'product_tag' ), ) ); if ( $this->is_on_sale() ) { $this->set_price( $this->get_sale_price() ); @@ -1012,9 +1048,9 @@ class WC_Product extends WC_Abstract_Legacy_Product { $id = wp_insert_post( apply_filters( 'woocommerce_new_product_data', array( 'post_type' => 'product', - 'post_status' => $this->get_status(), + 'post_status' => $this->get_status() ? $this->get_status() : 'publish', 'post_author' => get_current_user_id(), - 'post_title' => $this->get_name(), + 'post_title' => $this->get_name() ? $this->get_name() : __( 'Product', 'woocommerce' ), 'post_content' => $this->get_description(), 'post_excerpt' => $this->get_short_description(), 'post_parent' => $this->get_parent_id(), @@ -1024,9 +1060,10 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ), ) ), true ); - if ( $id ) { + if ( $id && ! is_wp_error( $id ) ) { $this->set_id( $id ); - $this->update_post_meta( $id ); + $this->update_post_meta(); + $this->update_terms(); $this->save_meta_data(); do_action( 'woocommerce_new_product', $id ); } @@ -1045,11 +1082,12 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'post_title' => $this->get_name(), 'post_parent' => $this->get_parent_id(), 'comment_status' => $this->get_reviews_allowed(), - 'post_status' => $this->get_status(), + 'post_status' => $this->get_status() ? $this->get_status() : 'publish', 'menu_order' => $this->get_menu_order(), ); wp_update_post( $post_data ); - $this->update_post_meta( $this->get_id() ); + $this->update_post_meta(); + $this->update_terms(); $this->save_meta_data(); do_action( 'woocommerce_update_product', $this->get_id() ); } @@ -1117,6 +1155,16 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } + /** + * For all stored terms in all taxonomies, save them to the DB. + * + * @since 2.7.0 + */ + protected function update_terms() { + wp_set_post_terms( $this->get_id(), $this->data['category_ids'], 'product_cat', false ); + wp_set_post_terms( $this->get_id(), $this->data['tag_ids'], 'product_tag', false ); + } + /* |-------------------------------------------------------------------------- | Conditionals diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 65ed5210015..a0e83a37abe 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -959,3 +959,29 @@ function wc_get_price_to_display( $product, $args = array() ) { return 'incl' === get_option( 'woocommerce_tax_display_shop' ) ? wc_get_price_including_tax( $product, array( 'qty' => $qty, 'price' => $price ) ) : wc_get_price_excluding_tax( $product, array( 'qty' => $qty, 'price' => $price ) ); } + +/** + * Returns the product categories in a list. + * + * @param int $product_id + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string + */ +function wc_get_product_category_list( $product_id, $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $product_id, 'product_cat', $before, $sep, $after ); +} + +/** + * Returns the product tags in a list. + * + * @param int $product_id + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string + */ +function wc_get_product_tag_list( $product_id, $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $product_id, 'product_tag', $before, $sep, $after ); +} diff --git a/templates/single-product/meta.php b/templates/single-product/meta.php index a2852bafea9..ad54e4e4621 100644 --- a/templates/single-product/meta.php +++ b/templates/single-product/meta.php @@ -13,18 +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; // Exit if accessed directly + exit; } -global $post, $product; - -$cat_count = sizeof( get_the_terms( $post->ID, 'product_cat' ) ); -$tag_count = sizeof( get_the_terms( $post->ID, 'product_tag' ) ); - +global $product; ?>
@@ -36,9 +32,9 @@ $tag_count = sizeof( get_the_terms( $post->ID, 'product_tag' ) ); - get_categories( ', ', '' . _n( 'Category:', 'Categories:', $cat_count, 'woocommerce' ) . ' ', '' ); ?> + ' . _n( 'Category:', 'Categories:', count( $product->get_category_ids() ), 'woocommerce' ) . ' ', '' ); ?> - get_tags( ', ', '' . _n( 'Tag:', 'Tags:', $tag_count, 'woocommerce' ) . ' ', '' ); ?> + ' . _n( 'Tag:', 'Tags:', count( $product->get_tag_ids() ), 'woocommerce' ) . ' ', '' ); ?> diff --git a/tests/framework/helpers/class-wc-helper-product.php b/tests/framework/helpers/class-wc-helper-product.php index 0806c799539..5741efe7d9f 100644 --- a/tests/framework/helpers/class-wc-helper-product.php +++ b/tests/framework/helpers/class-wc-helper-product.php @@ -70,7 +70,7 @@ class WC_Helper_Product { ) ); $simple_product_1 = self::create_simple_product( $product ); $simple_product_2 = self::create_simple_product( $product ); - update_post_meta( $product, '_children', array( $simple_product_1->id, $simple_product_2->id ) ); + update_post_meta( $product, '_children', array( $simple_product_1->get_id(), $simple_product_2->get_id() ) ); update_post_meta( $product, '_sku', 'DUMMY GROUPED SKU' ); update_post_meta( $product, '_manage_stock', 'no' ); update_post_meta( $product, '_tax_status', 'taxable' ); diff --git a/tests/unit-tests/product/crud.php b/tests/unit-tests/product/crud.php index a20e73ce431..64ae5905d94 100644 --- a/tests/unit-tests/product/crud.php +++ b/tests/unit-tests/product/crud.php @@ -67,42 +67,42 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { /** * Test product setters and getters - * @todo needs tests for tags, categories, and attributes + * @todo needs tests for attributes * @since 2.7.0 */ - public function test_product_getters_and_setters() { - $getters_and_setters = array( - 'name' => 'Test', - 'slug' => 'test', - 'status' => 'publish', - 'catalog_visibility' => 'search', - 'featured' => false, - 'description' => 'Hello world', - 'short_description' => 'hello', - 'sku' => 'TEST SKU', - 'regular_price' => 15.00, - 'sale_price' => 10.00, - 'date_on_sale_from' => '1475798400', - 'date_on_sale_to' => '1477267200', - 'total_sales' => 20, - 'tax_status' => 'none', - 'tax_class' => '', - 'manage_stock' => true, - 'stock_quantity' => 10, - 'stock_status' => 'instock', - 'backorders' => 'notify', - 'sold_individually' => false, - 'weight' => 100, - 'length' => 10, - 'width' => 10, - 'height' => 10, - 'upsell_ids' => array( 2, 3 ), - 'cross_sell_ids' => array( 4, 5 ), - 'parent_id' => 0, - 'reviews_allowed' => true, - 'default_attributes' => array(), - 'purchase_note' => 'A note', - 'menu_order' => 2, + public function test_product_getters_and_setters() { + $getters_and_setters = array( + 'name' => 'Test', + 'slug' => 'test', + 'status' => 'publish', + 'catalog_visibility' => 'search', + 'featured' => false, + 'description' => 'Hello world', + 'short_description' => 'hello', + 'sku' => 'TEST SKU', + 'regular_price' => 15.00, + 'sale_price' => 10.00, + 'date_on_sale_from' => '1475798400', + 'date_on_sale_to' => '1477267200', + 'total_sales' => 20, + 'tax_status' => 'none', + 'tax_class' => '', + 'manage_stock' => true, + 'stock_quantity' => 10, + 'stock_status' => 'instock', + 'backorders' => 'notify', + 'sold_individually' => false, + 'weight' => 100, + 'length' => 10, + 'width' => 10, + 'height' => 10, + 'upsell_ids' => array( 2, 3 ), + 'cross_sell_ids' => array( 4, 5 ), + 'parent_id' => 0, + 'reviews_allowed' => true, + 'default_attributes' => array(), + 'purchase_note' => 'A note', + 'menu_order' => 2, ); $product = new WC_Product; foreach ( $getters_and_setters as $function => $value ) { @@ -115,6 +115,32 @@ class WC_Tests_Product_CRUD extends WC_Unit_Test_Case { } } + /** + * Test product term setters and getters + * @since 2.7.0 + */ + public function test_product_term_getters_and_setters() { + $test_cat_1 = wp_insert_term( 'Testing 1', 'product_cat' ); + $test_cat_2 = wp_insert_term( 'Testing 2', 'product_cat' ); + + $test_tag_1 = wp_insert_term( 'Tag 1', 'product_tag' ); + $test_tag_2 = wp_insert_term( 'Tag 2', 'product_tag' ); + + $getters_and_setters = array( + 'tag_ids' => array( 'Tag 1', 'Tag 2' ), + 'category_ids' => array( $test_cat_1['term_id'], $test_cat_2['term_id'] ), + ); + $product = new WC_Product; + foreach ( $getters_and_setters as $function => $value ) { + $product->{"set_{$function}"}( $value ); + } + $product->create(); + $product = new WC_Product_Simple( $product->get_id() ); + + $this->assertEquals( array( $test_cat_1['term_id'], $test_cat_2['term_id'] ), $product->get_category_ids() ); + $this->assertEquals( array( $test_tag_1['term_id'], $test_tag_2['term_id'] ), $product->get_tag_ids() ); + } + /** * Test creating a new grouped product. *