From fde97df50f3cb9f0dbd92179f20f6506742f5ffd Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Mon, 17 Oct 2016 13:22:38 -0700 Subject: [PATCH 1/2] Clean up the abstract product class a bit, deprecate two functions we have renamed, make update & create work properly, and add some tests for it. --- .../abstracts/abstract-wc-legacy-product.php | 22 +++ includes/abstracts/abstract-wc-product.php | 184 +++++++++--------- tests/unit-tests/product/crud.php | 117 +++++++++++ 3 files changed, 233 insertions(+), 90 deletions(-) create mode 100644 tests/unit-tests/product/crud.php diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index 3399a05320e..ddd687d011f 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -129,6 +129,7 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { * @return string */ public function get_title() { + _deprecated_function( 'WC_Product::get_title', '2.7', 'WC_Product::get_name' ); return apply_filters( 'woocommerce_product_title', $this->post ? $this->post->post_title : '', $this ); } @@ -139,6 +140,27 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { * @return int */ public function get_parent() { + _deprecated_function( 'WC_Product::get_parent', '2.7', 'WC_Product::get_parent_id' ); return apply_filters( 'woocommerce_product_parent', absint( $this->post->post_parent ), $this ); } + + /** + * Returns the upsell product ids. + * + * @return array + */ + 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 ); + } + + /** + * Returns the cross sell product ids. + * + * @return array + */ + 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 ); + } } diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index d4f51b08e95..377f12f1933 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -22,14 +22,13 @@ include_once( 'abstract-wc-legacy-product.php' ); class WC_Product extends WC_Abstract_Legacy_Product { /** - * Stores customer data. + * Stores product data. * * @var array */ protected $data = array( 'name' => '', 'slug' => '', - //'permalink' => '', 'date_created' => '', 'date_modified' => '', 'status' => '', @@ -87,6 +86,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int|WC_Product|object $product Product to init. */ public function __construct( $product = 0 ) { + parent::__construct( $product ); if ( is_numeric( $product ) && $product > 0 ) { $this->read( $product ); } elseif ( $product instanceof self ) { @@ -96,6 +96,14 @@ class WC_Product extends WC_Abstract_Legacy_Product { } } + /* + |-------------------------------------------------------------------------- + | Getters + |-------------------------------------------------------------------------- + | + | Methods for getting data from the product object. + */ + /** * Get internal type. * @since 2.7.0 @@ -113,14 +121,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return get_permalink( $this->get_id() ); } - /* - |-------------------------------------------------------------------------- - | Getters - |-------------------------------------------------------------------------- - | - | Methods for getting data from the product object. - */ - /** * Get product name. * @@ -442,14 +442,16 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @return array */ public function get_attributes() { - $attributes = $this->data['product_attributes']; + $attributes = $this->data['attributes']; $taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' ); // Check for any attributes which have been removed globally - foreach ( $attributes as $key => $attribute ) { - if ( $attribute['is_taxonomy'] ) { - if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { - unset( $attributes[ $key ] ); + if ( is_array( $attributes ) && count( $attributes ) > 0 ) { + foreach ( $attributes as $key => $attribute ) { + if ( $attribute['is_taxonomy'] ) { + if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) { + unset( $attributes[ $key ] ); + } } } } @@ -477,6 +479,30 @@ class WC_Product extends WC_Abstract_Legacy_Product { return $this->data['menu_order']; } + /** + * Returns the product categories. @todo store in class and save? + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return string + */ + public function get_categories( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); + } + + /** + * Returns the product tags. @todo store in class and save? + * + * @param string $sep (default: ', '). + * @param string $before (default: ''). + * @param string $after (default: ''). + * @return array + */ + public function get_tags( $sep = ', ', $before = '', $after = '' ) { + return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); + } + /* |-------------------------------------------------------------------------- | Setters @@ -835,13 +861,13 @@ class WC_Product extends WC_Abstract_Legacy_Product { } /** - * Returns product attributes. + * Set product attributes. * * @since 2.7.0 * @param array $attributes List of product attributes. */ public function set_attributes( $attributes ) { - $this->data['product_attributes'] = $attributes; // @todo ensure unserialised, array, and filtered out empty values + $this->data['attributes'] = $attributes; // @todo ensure unserialised, array, and filtered out empty values } /** @@ -864,47 +890,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->data['menu_order'] = intval( $menu_order ); } - - - - - - - - - - - - - - - - - - /** - * Returns the product categories. @todo store in class and save? - * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return string - */ - public function get_categories( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_cat', $before, $sep, $after ); - } - - /** - * Returns the product tags. @todo store in class and save? - * - * @param string $sep (default: ', '). - * @param string $before (default: ''). - * @param string $after (default: ''). - * @return array - */ - public function get_tags( $sep = ', ', $before = '', $after = '' ) { - return get_the_term_list( $this->get_id(), 'product_tag', $before, $sep, $after ); - } - /** * Set the product categories. @todo store in class and save? * @@ -925,8 +910,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->save_taxonomy_terms( $terms_id, 'tag' ); } - - /* |-------------------------------------------------------------------------- | CRUD methods @@ -961,7 +944,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'type' => '', 'status' => $post_object->post_status, 'featured' => get_post_meta( $id, '_featured', true ), - 'catalog_visibility' => 'hidden', + 'catalog_visibility' => get_post_meta( $id, '_visibility', true ), 'description' => $post_object->post_content, 'short_description' => $post_object->post_excerpt, 'sku' => get_post_meta( $id, '_sku', true ), @@ -972,7 +955,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'total_sales' => get_post_meta( $id, 'total_sales', true ), 'tax_status' => get_post_meta( $id, '_tax_status', true ), 'tax_class' => get_post_meta( $id, '_tax_class', true ), - 'manage_stock' => 'no', + 'manage_stock' => get_post_meta( $id, '_manage_stock', true ), 'stock_quantity' => get_post_meta( $id, '_stock', true ), 'stock_status' => get_post_meta( $id, '_stock_status', true ), 'backorders' => get_post_meta( $id, '_backorders', true ), @@ -1004,14 +987,17 @@ class WC_Product extends WC_Abstract_Legacy_Product { $this->set_date_created( current_time( 'timestamp' ) ); $id = wp_insert_post( apply_filters( 'woocommerce_new_product_data', array( - 'post_type' => 'product', - 'post_status' => 'publish', - 'post_author' => get_current_user_id(), - 'post_title' => $this->get_code(), - 'post_content' => '', - 'post_excerpt' => $this->get_description(), - 'post_date' => date( 'Y-m-d H:i:s', $this->get_date_created() ), - 'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ), + 'post_type' => 'product', + 'post_status' => 'publish', + 'post_author' => get_current_user_id(), + 'post_title' => $this->get_name(), + 'post_content' => $this->get_description(), + 'post_excerpt' => $this->get_short_description(), + 'post_parent' => $this->get_parent_id(), + 'comment_status' => $this->get_reviews_allowed(), + 'menu_order' => $this->get_menu_order(), + 'post_date' => date( 'Y-m-d H:i:s', $this->get_date_created() ), + 'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ), ) ), true ); if ( $id ) { @@ -1028,7 +1014,19 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @since 2.7.0 */ public function update() { - + $post_data = array( + 'ID' => $this->get_id(), + 'post_content' => $this->get_description(), + 'post_excerpt' => $this->get_short_description(), + 'post_title' => $this->get_name(), + 'post_parent' => $this->get_parent_id(), + 'comment_status' => $this->get_reviews_allowed(), + 'menu_order' => $this->get_menu_order(), + ); + wp_update_post( $post_data ); + $this->update_post_meta( $this->get_id() ); + $this->save_meta_data(); + do_action( 'woocommerce_update_product', $this->get_id() ); } /** @@ -1062,7 +1060,31 @@ class WC_Product extends WC_Abstract_Legacy_Product { * @param int $id Object ID. */ private function update_post_meta( $id ) { - // update_post_meta( $id, 'discount_type', $this->get_discount_type() ); + update_post_meta( $id, '_featured', $this->get_featured() ); + update_post_meta( $id, '_visibility', $this->get_catalog_visibility() ); + update_post_meta( $id, '_sku', $this->get_sku() ); + update_post_meta( $id, '_regular_price', $this->get_regular_price() ); + update_post_meta( $id, '_sale_price', $this->get_sale_price() ); + update_post_meta( $id, '_regular_price', $this->get_regular_price() ); + update_post_meta( $id, '_sale_price_dates_from', $this->get_date_on_sale_from() ); + update_post_meta( $id, '_sale_price_dates_to', $this->get_date_on_sale_to() ); + update_post_meta( $id, 'total_sales', $this->get_total_sales() ); + update_post_meta( $id, '_tax_status', $this->get_tax_status() ); + update_post_meta( $id, '_tax_class', $this->get_tax_class() ); + update_post_meta( $id, '_manage_stock', $this->get_manage_stock() ); + update_post_meta( $id, '_stock', $this->get_stock_quantity() ); + update_post_meta( $id, '_stock_status', $this->get_stock_status() ); + update_post_meta( $id, '_backorders', $this->get_backorders() ); + update_post_meta( $id, '_sold_individually', $this->get_sold_individually() ); + update_post_meta( $id, '_weight', $this->get_weight() ); + update_post_meta( $id, '_length', $this->get_length() ); + update_post_meta( $id, '_width', $this->get_width() ); + update_post_meta( $id, '_height', $this->get_height() ); + update_post_meta( $id, '_upsell_ids', $this->get_upsell_ids() ); + update_post_meta( $id, '_crosssell_ids', $this->get_cross_sell_ids() ); + update_post_meta( $id, '_purchase_note', $this->get_purchase_note() ); + update_post_meta( $id, '_attributes', $this->get_attributes() ); + update_post_meta( $id, '_default_attributes', $this->get_default_attributes() ); } /* @@ -2014,24 +2036,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { return apply_filters( 'woocommerce_product_review_count', $count, $this ); } - /** - * Returns the upsell product ids. - * - * @return array - */ - public function get_upsells() { - return apply_filters( 'woocommerce_product_upsell_ids', (array) maybe_unserialize( $this->upsell_ids ), $this ); - } - - /** - * Returns the cross sell product ids. - * - * @return array - */ - public function get_cross_sells() { - return apply_filters( 'woocommerce_product_crosssell_ids', (array) maybe_unserialize( $this->crosssell_ids ), $this ); - } - /** * Returns the product shipping class. * diff --git a/tests/unit-tests/product/crud.php b/tests/unit-tests/product/crud.php new file mode 100644 index 00000000000..e8285f8316b --- /dev/null +++ b/tests/unit-tests/product/crud.php @@ -0,0 +1,117 @@ +set_regular_price( 42 ); + $product->set_name( 'My Product' ); + $product->create(); + + $read_product = new WC_Product( $product->get_id() ); + + $this->assertEquals( '42', $read_product->get_regular_price() ); + $this->assertEquals( 'My Product', $read_product->get_name() ); + } + + /** + * Test reading a product. + * + * @since 2.7.0 + */ + function test_product_read() { + $product = WC_Helper_Product::create_simple_product(); + $product = new WC_Product( $product->get_id() ); + + $this->assertEquals( '10', $product->get_regular_price() ); + } + + /** + * Test updating a product. + * + * @since 2.7.0 + */ + function test_product_update() { + $product = WC_Helper_Product::create_simple_product(); + + $this->assertEquals( '10', $product->get_regular_price() ); + + $product->set_regular_price( 15 ); + $product->save(); + + // Reread from database + $product = new WC_Product( $product->get_id() ); + + $this->assertEquals( '15', $product->get_regular_price() ); + } + + /** + * Test deleting a product. + * + * @since 2.7.0 + */ + function test_product_delete() { + $product = WC_Helper_Product::create_simple_product(); + $product->delete(); + $this->assertEquals( 0, $product->get_id() ); + } + + /** + * Test product setters and getters + * @todo needs tests for tags, categories, and 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, + ); + $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() ); + foreach ( $getters_and_setters as $function => $value ) { + $this->assertEquals( $value, $product->{"get_{$function}"}(), $function ); + } + } +} From 6e69b0d924aa26a0bbe7dcdb7273253f5f9a951b Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Tue, 18 Oct 2016 07:47:28 -0700 Subject: [PATCH 2/2] Handle PR feedback: Remove duplicate regular_price update, allow changing of post status for products, remove deprecation for get_title since we might still offer it as a function --- includes/abstracts/abstract-wc-legacy-product.php | 1 - includes/abstracts/abstract-wc-product.php | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/includes/abstracts/abstract-wc-legacy-product.php b/includes/abstracts/abstract-wc-legacy-product.php index ddd687d011f..965346d7b08 100644 --- a/includes/abstracts/abstract-wc-legacy-product.php +++ b/includes/abstracts/abstract-wc-legacy-product.php @@ -129,7 +129,6 @@ abstract class WC_Abstract_Legacy_Product extends WC_Data { * @return string */ public function get_title() { - _deprecated_function( 'WC_Product::get_title', '2.7', 'WC_Product::get_name' ); return apply_filters( 'woocommerce_product_title', $this->post ? $this->post->post_title : '', $this ); } diff --git a/includes/abstracts/abstract-wc-product.php b/includes/abstracts/abstract-wc-product.php index 377f12f1933..cd13baca14f 100644 --- a/includes/abstracts/abstract-wc-product.php +++ b/includes/abstracts/abstract-wc-product.php @@ -31,7 +31,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { 'slug' => '', 'date_created' => '', 'date_modified' => '', - 'status' => '', + 'status' => 'publish', 'featured' => false, 'catalog_visibility' => 'hidden', 'description' => '', @@ -988,7 +988,7 @@ class WC_Product extends WC_Abstract_Legacy_Product { $id = wp_insert_post( apply_filters( 'woocommerce_new_product_data', array( 'post_type' => 'product', - 'post_status' => 'publish', + 'post_status' => $this->get_status(), 'post_author' => get_current_user_id(), 'post_title' => $this->get_name(), 'post_content' => $this->get_description(), @@ -1021,6 +1021,7 @@ 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(), 'menu_order' => $this->get_menu_order(), ); wp_update_post( $post_data ); @@ -1065,7 +1066,6 @@ class WC_Product extends WC_Abstract_Legacy_Product { update_post_meta( $id, '_sku', $this->get_sku() ); update_post_meta( $id, '_regular_price', $this->get_regular_price() ); update_post_meta( $id, '_sale_price', $this->get_sale_price() ); - update_post_meta( $id, '_regular_price', $this->get_regular_price() ); update_post_meta( $id, '_sale_price_dates_from', $this->get_date_on_sale_from() ); update_post_meta( $id, '_sale_price_dates_to', $this->get_date_on_sale_to() ); update_post_meta( $id, 'total_sales', $this->get_total_sales() );