Woocommerce Object Caching === # Data Handling Classes ## Class List | # | Class Name | Extends | Uses | | ---- | :------------------------- | -------------------------- | ----------------------------- | | 1 | [WC_Product_Factory](#WC_Product_Factory) | | [WC_Product_Simple](#WC_Product_Simple) | | 2 | [WC_Product_Simple](#WC_Product_Simple) | [WC_Product](#WC_Product) | | | 3 | [WC_Product](#WC_Product) | [WC_Abstract_Legacy_Product](#WC_Abstract_Legacy_Product) | [WC_Data_Store](#WC_Data_Store) | | 4 | [WC_Abstract_Legacy_Product](#WC_Abstract_Legacy_Product) | [WC_Data](#WC_Data) | | | 5 | [WC_Data](#WC_Data) | | [WC_Cache_Helper](#WC_Product), [WC_Meta_Data](#WC_Meta_Data) | | 6 | [WC_Data_Store](#WC_Product) | | [WC_Product_Data_Store_CPT](#WC_Product_Data_Store_CPT) | | 7 | [WC_Product_Data_Store_CPT](#WC_Product_Data_Store_CPT) | [WC_Data_Store_WP](#WC_Data_Store_WP) | | | 8 | [WC_Data_Store_WP](#WC_Data_Store_WP) | | | | 9 | [WC_Cache_Helper](#WC_Cache_Helper) | | | | 10 | [WC_Meta_Data](#WC_Meta_Data) | | | --- ## WC_Product_Factory ```php /** * Product Factory * * The WooCommerce product factory creating the right product object. * * @package WooCommerce/Classes * @version 3.0.0 */ /** * Product factory class. */ class WC_Product_Factory {} ``` ### Overview This class is used to find the targeted product class by the product type using product ID. The `get_product()` is the main method in this class which retrieves the right product class using the given product ID. ### Method: get_product() ```php /** * Get a product. * * @param mixed $product_id WC_Product|WP_Post|int|bool $product Product instance, post instance, numeric or false to use global $post. * @param array $deprecated Previously used to pass arguments to the factory, e.g. to force a type. * @return WC_Product|bool Product object or false if the product cannot be loaded. */ public function get_product( $product_id = false, $deprecated = array() ) { $product_id = $this->get_product_id( $product_id ); if ( ! $product_id ) { return false; } $product_type = $this->get_product_type( $product_id ); // Backwards compatibility. if ( ! empty( $deprecated ) ) { wc_deprecated_argument( 'args', '3.0', 'Passing args to the product factory is deprecated. If you need to force a type, construct the product class directly.' ); if ( isset( $deprecated['product_type'] ) ) { $product_type = $this->get_classname_from_product_type( $deprecated['product_type'] ); } } $classname = $this->get_product_classname( $product_id, $product_type ); try { return new $classname( $product_id, $deprecated ); } catch ( Exception $e ) { return false; } } ``` --- ## WC_Product_Simple ```php /** * Simple Product Class. * * The default product type kinda product. * * @package WooCommerce/Classes/Products */ /** * Simple product class. */ class WC_Product_Simple extends WC_Product {} ``` ### Overview This is the default product class which handles the data processing of the default product type by extending the `WC_Product` class. The main data processing tasks are maintained in the parent class. So, only the helper functions used in the product loop are maintained here. ### Method: Constructor ```php /** * Initialize simple product. * * @param WC_Product|int $product Product instance or ID. */ public function __construct( $product = 0 ) { $this->supports[] = 'ajax_add_to_cart'; parent::__construct( $product ); } ``` --- ## WC_Product ```php /** * WooCommerce product base class. * * @package WooCommerce/Abstracts */ /** * Abstract Product Class * * The WooCommerce product class handles individual product data. * * @version 3.0.0 * @package WooCommerce/Abstracts */ class WC_Product extends WC_Abstract_Legacy_Product {} ``` ### Overview This class handles individual product data ### Class Data Variables ```php /** * This is the name of this object type. * * @var string */ protected $object_type = 'product'; /** * Post type. * * @var string */ protected $post_type = 'product'; /** * Cache group. * * @var string */ protected $cache_group = 'products'; /** * Stores product data. * * @var array */ protected $data = array( 'name' => '', 'slug' => '', 'date_created' => null, 'date_modified' => null, 'status' => false, 'featured' => false, 'catalog_visibility' => 'visible', 'description' => '', 'short_description' => '', 'sku' => '', 'price' => '', 'regular_price' => '', 'sale_price' => '', 'date_on_sale_from' => null, 'date_on_sale_to' => null, 'total_sales' => '0', 'tax_status' => 'taxable', 'tax_class' => '', 'manage_stock' => false, 'stock_quantity' => null, 'stock_status' => 'instock', 'backorders' => 'no', 'low_stock_amount' => '', 'sold_individually' => false, 'weight' => '', 'length' => '', 'width' => '', 'height' => '', 'upsell_ids' => array(), 'cross_sell_ids' => array(), 'parent_id' => 0, 'reviews_allowed' => true, 'purchase_note' => '', 'attributes' => array(), 'default_attributes' => array(), 'menu_order' => 0, 'post_password' => '', 'virtual' => false, 'downloadable' => false, 'category_ids' => array(), 'tag_ids' => array(), 'shipping_class_id' => 0, 'downloads' => array(), 'image_id' => '', 'gallery_image_ids' => array(), 'download_limit' => -1, 'download_expiry' => -1, 'rating_counts' => array(), 'average_rating' => 0, 'review_count' => 0, ); /** * Supported features such as 'ajax_add_to_cart'. * * @var array */ protected $supports = array(); ``` ### Method: Constructor ```php /** * Get the product if ID is passed, otherwise the product is new and empty. * This class should NOT be instantiated, but the wc_get_product() function * should be used. It is possible, but the wc_get_product() is preferred. * * @param int|WC_Product|object $product Product to init. */ public function __construct( $product = 0 ) { parent::__construct( $product ); if ( is_numeric( $product ) && $product > 0 ) { $this->set_id( $product ); } elseif ( $product instanceof self ) { $this->set_id( absint( $product->get_id() ) ); } elseif ( ! empty( $product->ID ) ) { $this->set_id( absint( $product->ID ) ); } else { $this->set_object_read( true ); } $this->data_store = WC_Data_Store::load( 'product-' . $this->get_type() ); if ( $this->get_id() > 0 ) { $this->data_store->read( $this ); } } ``` --- # Template Hierarchy ## Shortcode Template ### WC_Shortcodes ```php // Path: wp-content\plugins\woocommerce\includes\class-wc-shortcodes.php /** * Shortcodes * * @package WooCommerce/Classes * @version 3.2.0 */ /** * WooCommerce Shortcodes class. */ class WC_Shortcodes {} ``` #### Method: init() ```php /** * Init shortcodes. */ public static function init() { $shortcodes = array( 'product' => __CLASS__ . '::product', 'product_page' => __CLASS__ . '::product_page', 'product_category' => __CLASS__ . '::product_category', 'product_categories' => __CLASS__ . '::product_categories', 'add_to_cart' => __CLASS__ . '::product_add_to_cart', 'add_to_cart_url' => __CLASS__ . '::product_add_to_cart_url', 'products' => __CLASS__ . '::products', 'recent_products' => __CLASS__ . '::recent_products', 'sale_products' => __CLASS__ . '::sale_products', 'best_selling_products' => __CLASS__ . '::best_selling_products', 'top_rated_products' => __CLASS__ . '::top_rated_products', 'featured_products' => __CLASS__ . '::featured_products', 'product_attribute' => __CLASS__ . '::product_attribute', 'related_products' => __CLASS__ . '::related_products', 'shop_messages' => __CLASS__ . '::shop_messages', 'woocommerce_order_tracking' => __CLASS__ . '::order_tracking', 'woocommerce_cart' => __CLASS__ . '::cart', 'woocommerce_checkout' => __CLASS__ . '::checkout', 'woocommerce_my_account' => __CLASS__ . '::my_account', ); foreach ( $shortcodes as $shortcode => $function ) { add_shortcode( apply_filters( "{$shortcode}_shortcode_tag", $shortcode ), $function ); } // Alias for pre 2.1 compatibility. add_shortcode( 'woocommerce_messages', __CLASS__ . '::shop_messages' ); } ``` #### Method: products() ```php /** * List multiple products shortcode. * * @param array $atts Attributes. * @return string */ public static function products( $atts ) { $atts = (array) $atts; $type = 'products'; // Allow list product based on specific cases. if ( isset( $atts['on_sale'] ) && wc_string_to_bool( $atts['on_sale'] ) ) { $type = 'sale_products'; } elseif ( isset( $atts['best_selling'] ) && wc_string_to_bool( $atts['best_selling'] ) ) { $type = 'best_selling_products'; } elseif ( isset( $atts['top_rated'] ) && wc_string_to_bool( $atts['top_rated'] ) ) { $type = 'top_rated_products'; } $shortcode = new WC_Shortcode_Products( $atts, $type ); return $shortcode->get_content(); } ``` --- ### WC_Shortcode_Products ```php // Path: /** * Products shortcode * * @package WooCommerce/Shortcodes * @version 3.2.4 */ /** * Products shortcode class. */ class WC_Shortcode_Products {} ``` #### Method: __construct() ```php /** * Initialize shortcode. * * @since 3.2.0 * @param array $attributes Shortcode attributes. * @param string $type Shortcode type. */ public function __construct( $attributes = array(), $type = 'products' ) { $this->type = $type; $this->attributes = $this->parse_attributes( $attributes ); $this->query_args = $this->parse_query_args(); } ``` #### Method: get_content() ```php /** * Get shortcode content. * * @since 3.2.0 * @return string */ public function get_content() { return $this->product_loop(); } ``` #### Method: product_loop() ```php /** * Loop over found products. * * @since 3.2.0 * @return string */ protected function product_loop() { $columns = absint( $this->attributes['columns'] ); $classes = $this->get_wrapper_classes( $columns ); $products = $this->get_query_results(); ob_start(); if ( $products && $products->ids ) { // Prime caches to reduce future queries. if ( is_callable( '_prime_post_caches' ) ) { _prime_post_caches( $products->ids ); } // Setup the loop. wc_setup_loop( array( 'columns' => $columns, 'name' => $this->type, 'is_shortcode' => true, 'is_search' => false, 'is_paginated' => wc_string_to_bool( $this->attributes['paginate'] ), 'total' => $products->total, 'total_pages' => $products->total_pages, 'per_page' => $products->per_page, 'current_page' => $products->current_page, ) ); $original_post = $GLOBALS['post']; do_action( "woocommerce_shortcode_before_{$this->type}_loop", $this->attributes ); // Fire standard shop loop hooks when paginating results so we can show result counts and so on. if ( wc_string_to_bool( $this->attributes['paginate'] ) ) { do_action( 'woocommerce_before_shop_loop' ); } woocommerce_product_loop_start(); if ( wc_get_loop_prop( 'total' ) ) { foreach ( $products->ids as $product_id ) { $GLOBALS['post'] = get_post( $product_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited setup_postdata( $GLOBALS['post'] ); // Set custom product visibility when quering hidden products. add_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) ); // Render product template. wc_get_template_part( 'content', 'product' ); // Restore product visibility. remove_action( 'woocommerce_product_is_visible', array( $this, 'set_product_as_visible' ) ); } } $GLOBALS['post'] = $original_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited woocommerce_product_loop_end(); // Fire standard shop loop hooks when paginating results so we can show result counts and so on. if ( wc_string_to_bool( $this->attributes['paginate'] ) ) { do_action( 'woocommerce_after_shop_loop' ); } do_action( "woocommerce_shortcode_after_{$this->type}_loop", $this->attributes ); wp_reset_postdata(); wc_reset_loop(); } else { do_action( "woocommerce_shortcode_{$this->type}_loop_no_results", $this->attributes ); } return '<div class="' . esc_attr( implode( ' ', $classes ) ) . '">' . ob_get_clean() . '</div>'; } ``` ```php $products = $this->get_query_results(); ``` ```php // Prime caches to reduce future queries. if ( is_callable( '_prime_post_caches' ) ) { _prime_post_caches( $products->ids ); } ``` ```php // Render product template. wc_get_template_part( 'content', 'product' ); ``` #### Method: get_query_results() ```php /** * Run the query and return an array of data, including queried ids and pagination information. * * @since 3.3.0 * @return object Object with the following props; ids, per_page, found_posts, max_num_pages, current_page */ protected function get_query_results() { $transient_name = $this->get_transient_name(); $transient_version = WC_Cache_Helper::get_transient_version( 'product_query' ); $cache = wc_string_to_bool( $this->attributes['cache'] ) === true; $transient_value = $cache ? get_transient( $transient_name ) : false; if ( isset( $transient_value['value'], $transient_value['version'] ) && $transient_value['version'] === $transient_version ) { $results = $transient_value['value']; } else { $query = new WP_Query( $this->query_args ); $paginated = ! $query->get( 'no_found_rows' ); $results = (object) array( 'ids' => wp_parse_id_list( $query->posts ), 'total' => $paginated ? (int) $query->found_posts : count( $query->posts ), 'total_pages' => $paginated ? (int) $query->max_num_pages : 1, 'per_page' => (int) $query->get( 'posts_per_page' ), 'current_page' => $paginated ? (int) max( 1, $query->get( 'paged', 1 ) ) : 1, ); if ( $cache ) { $transient_value = array( 'version' => $transient_version, 'value' => $results, ); set_transient( $transient_name, $transient_value, DAY_IN_SECONDS * 30 ); } } // Remove ordering query arguments which may have been added by get_catalog_ordering_args. WC()->query->remove_ordering_args(); /** * Filter shortcode products query results. * * @since 4.0.0 * @param stdClass $results Query results. * @param WC_Shortcode_Products $this WC_Shortcode_Products instance. */ return apply_filters( 'woocommerce_shortcode_products_query_results', $results, $this ); } ``` --- ## Page Template ### Product Page ```php // Path: wp-content\plugins\woocommerce\templates\archive-product.php woocommerce_product_loop_start(); if ( wc_get_loop_prop( 'total' ) ) { while ( have_posts() ) { the_post(); /** * Hook: woocommerce_shop_loop. */ do_action( 'woocommerce_shop_loop' ); wc_get_template_part( 'content', 'product' ); } } woocommerce_product_loop_end(); ``` ```php // Path: wp-content\plugins\woocommerce\templates\content-product.php global $product; // Ensure visibility. if ( empty( $product ) || ! $product->is_visible() ) { return; } ?> <li <?php wc_product_class( '', $product ); ?>> <?php /** * Hook: woocommerce_before_shop_loop_item. * * @hooked woocommerce_template_loop_product_link_open - 10 */ do_action( 'woocommerce_before_shop_loop_item' ); /** * Hook: woocommerce_before_shop_loop_item_title. * * @hooked woocommerce_show_product_loop_sale_flash - 10 * @hooked woocommerce_template_loop_product_thumbnail - 10 */ do_action( 'woocommerce_before_shop_loop_item_title' ); /** * Hook: woocommerce_shop_loop_item_title. * * @hooked woocommerce_template_loop_product_title - 10 */ do_action( 'woocommerce_shop_loop_item_title' ); /** * Hook: woocommerce_after_shop_loop_item_title. * * @hooked woocommerce_template_loop_rating - 5 * @hooked woocommerce_template_loop_price - 10 */ do_action( 'woocommerce_after_shop_loop_item_title' ); /** * Hook: woocommerce_after_shop_loop_item. * * @hooked woocommerce_template_loop_product_link_close - 5 * @hooked woocommerce_template_loop_add_to_cart - 10 */ do_action( 'woocommerce_after_shop_loop_item' ); ?> </li> ``` ```php // Path: wp-content\plugins\woocommerce\includes\wc-template-functions.php /** * When the_post is called, put product data into a global. * * @param mixed $post Post Object. * @return WC_Product */ function wc_setup_product_data( $post ) { unset( $GLOBALS['product'] ); if ( is_int( $post ) ) { $post = get_post( $post ); } if ( empty( $post->post_type ) || ! in_array( $post->post_type, array( 'product', 'product_variation' ), true ) ) { return; } $GLOBALS['product'] = wc_get_product( $post ); return $GLOBALS['product']; } add_action( 'the_post', 'wc_setup_product_data' ); ``` ```php // Path: wp-content\plugins\woocommerce\includes\wc-product-functions.php /** * Main function for returning products, uses the WC_Product_Factory class. * * This function should only be called after 'init' action is finished, as there might be taxonomies that are getting * registered during the init action. * * @since 2.2.0 * * @param mixed $the_product Post object or post ID of the product. * @param array $deprecated Previously used to pass arguments to the factory, e.g. to force a type. * @return WC_Product|null|false */ function wc_get_product( $the_product = false, $deprecated = array() ) { if ( ! did_action( 'woocommerce_init' ) || ! did_action( 'woocommerce_after_register_taxonomy' ) || ! did_action( 'woocommerce_after_register_post_type' ) ) { /* translators: 1: wc_get_product 2: woocommerce_init 3: woocommerce_after_register_taxonomy 4: woocommerce_after_register_post_type */ wc_doing_it_wrong( __FUNCTION__, sprintf( __( '%1$s should not be called before the %2$s, %3$s and %4$s actions have finished.', 'woocommerce' ), 'wc_get_product', 'woocommerce_init', 'woocommerce_after_register_taxonomy', 'woocommerce_after_register_post_type' ), '3.9' ); return false; } if ( ! empty( $deprecated ) ) { wc_deprecated_argument( 'args', '3.0', 'Passing args to wc_get_product is deprecated. If you need to force a type, construct the product class directly.' ); } // new WC_Product_Factory() return WC()->product_factory->get_product( $the_product, $deprecated ); } ``` ```php // Path: wp-content\plugins\woocommerce\includes\class-wc-product-factory.php /** * Product Factory * * The WooCommerce product factory creating the right product object. * * @package WooCommerce/Classes * @version 3.0.0 */ defined( 'ABSPATH' ) || exit; /** * Product factory class. */ class WC_Product_Factory { /** * Get a product. * * @param mixed $product_id WC_Product|WP_Post|int|bool $product Product instance, post instance, numeric or false to use global $post. * @param array $deprecated Previously used to pass arguments to the factory, e.g. to force a type. * @return WC_Product|bool Product object or false if the product cannot be loaded. */ public function get_product( $product_id = false, $deprecated = array() ) { $product_id = $this->get_product_id( $product_id ); if ( ! $product_id ) { return false; } $product_type = $this->get_product_type( $product_id ); // Backwards compatibility. if ( ! empty( $deprecated ) ) { wc_deprecated_argument( 'args', '3.0', 'Passing args to the product factory is deprecated. If you need to force a type, construct the product class directly.' ); if ( isset( $deprecated['product_type'] ) ) { $product_type = $this->get_classname_from_product_type( $deprecated['product_type'] ); } } $classname = $this->get_product_classname( $product_id, $product_type ); try { // new WC_Product_Simple() return new $classname( $product_id, $deprecated ); } catch ( Exception $e ) { return false; } } } ``` ```php // Path: wp-content\plugins\woocommerce\includes\class-wc-product-simple.php /** * Simple Product Class. * * The default product type kinda product. * * @package WooCommerce/Classes/Products */ defined( 'ABSPATH' ) || exit; /** * Simple product class. */ class WC_Product_Simple extends WC_Product { /** * Initialize simple product. * * @param WC_Product|int $product Product instance or ID. */ public function __construct( $product = 0 ) { $this->supports[] = 'ajax_add_to_cart'; parent::__construct( $product ); } } ``` ```php // Path: wp-content\plugins\woocommerce\includes\abstracts\abstract-wc-product.php /** * WooCommerce product base class. * * @package WooCommerce/Abstracts */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Legacy product contains all deprecated methods for this class and can be * removed in the future. */ require_once WC_ABSPATH . 'includes/legacy/abstract-wc-legacy-product.php'; /** * Abstract Product Class * * The WooCommerce product class handles individual product data. * * @version 3.0.0 * @package WooCommerce/Abstracts */ class WC_Product extends WC_Abstract_Legacy_Product { /** * This is the name of this object type. * * @var string */ protected $object_type = 'product'; /** * Post type. * * @var string */ protected $post_type = 'product'; /** * Cache group. * * @var string */ protected $cache_group = 'products'; /** * Stores product data. * * @var array */ protected $data = array( 'name' => '', 'slug' => '', 'date_created' => null, 'date_modified' => null, 'status' => false, 'featured' => false, 'catalog_visibility' => 'visible', 'description' => '', 'short_description' => '', 'sku' => '', 'price' => '', 'regular_price' => '', 'sale_price' => '', 'date_on_sale_from' => null, 'date_on_sale_to' => null, 'total_sales' => '0', 'tax_status' => 'taxable', 'tax_class' => '', 'manage_stock' => false, 'stock_quantity' => null, 'stock_status' => 'instock', 'backorders' => 'no', 'low_stock_amount' => '', 'sold_individually' => false, 'weight' => '', 'length' => '', 'width' => '', 'height' => '', 'upsell_ids' => array(), 'cross_sell_ids' => array(), 'parent_id' => 0, 'reviews_allowed' => true, 'purchase_note' => '', 'attributes' => array(), 'default_attributes' => array(), 'menu_order' => 0, 'post_password' => '', 'virtual' => false, 'downloadable' => false, 'category_ids' => array(), 'tag_ids' => array(), 'shipping_class_id' => 0, 'downloads' => array(), 'image_id' => '', 'gallery_image_ids' => array(), 'download_limit' => -1, 'download_expiry' => -1, 'rating_counts' => array(), 'average_rating' => 0, 'review_count' => 0, ); /** * Supported features such as 'ajax_add_to_cart'. * * @var array */ protected $supports = array(); /** * Get the product if ID is passed, otherwise the product is new and empty. * This class should NOT be instantiated, but the wc_get_product() function * should be used. It is possible, but the wc_get_product() is preferred. * * @param int|WC_Product|object $product Product to init. */ public function __construct( $product = 0 ) { parent::__construct( $product ); if ( is_numeric( $product ) && $product > 0 ) { $this->set_id( $product ); } elseif ( $product instanceof self ) { $this->set_id( absint( $product->get_id() ) ); } elseif ( ! empty( $product->ID ) ) { $this->set_id( absint( $product->ID ) ); } else { $this->set_object_read( true ); } $this->data_store = WC_Data_Store::load( 'product-' . $this->get_type() ); if ( $this->get_id() > 0 ) { $this->data_store->read( $this ); } } } ``` ```php // Path: wp-content\plugins\woocommerce\includes\class-wc-data-store.php /** * WC Data Store. * * @package WooCommerce\Classes * @since 3.0.0 * @version 3.0.0 */ defined( 'ABSPATH' ) || exit; /** * Data store class. */ class WC_Data_Store { /** * Contains an instance of the data store class that we are working with. * * @var WC_Data_Store */ private $instance = null; /** * Contains an array of default WC supported data stores. * Format of object name => class name. * Example: 'product' => 'WC_Product_Data_Store_CPT' * You can also pass something like product_<type> for product stores and * that type will be used first when available, if a store is requested like * this and doesn't exist, then the store would fall back to 'product'. * Ran through `woocommerce_data_stores`. * * @var array */ private $stores = array( 'coupon' => 'WC_Coupon_Data_Store_CPT', 'customer' => 'WC_Customer_Data_Store', 'customer-download' => 'WC_Customer_Download_Data_Store', 'customer-download-log' => 'WC_Customer_Download_Log_Data_Store', 'customer-session' => 'WC_Customer_Data_Store_Session', 'order' => 'WC_Order_Data_Store_CPT', 'order-refund' => 'WC_Order_Refund_Data_Store_CPT', 'order-item' => 'WC_Order_Item_Data_Store', 'order-item-coupon' => 'WC_Order_Item_Coupon_Data_Store', 'order-item-fee' => 'WC_Order_Item_Fee_Data_Store', 'order-item-product' => 'WC_Order_Item_Product_Data_Store', 'order-item-shipping' => 'WC_Order_Item_Shipping_Data_Store', 'order-item-tax' => 'WC_Order_Item_Tax_Data_Store', 'payment-token' => 'WC_Payment_Token_Data_Store', 'product' => 'WC_Product_Data_Store_CPT', 'product-grouped' => 'WC_Product_Grouped_Data_Store_CPT', 'product-variable' => 'WC_Product_Variable_Data_Store_CPT', 'product-variation' => 'WC_Product_Variation_Data_Store_CPT', 'shipping-zone' => 'WC_Shipping_Zone_Data_Store', 'webhook' => 'WC_Webhook_Data_Store', ); /** * Contains the name of the current data store's class name. * * @var string */ private $current_class_name = ''; /** * The object type this store works with. * * @var string */ private $object_type = ''; /** * Tells WC_Data_Store which object (coupon, product, order, etc) * store we want to work with. * * @throws Exception When validation fails. * @param string $object_type Name of object. */ public function __construct( $object_type ) { $this->object_type = $object_type; $this->stores = apply_filters( 'woocommerce_data_stores', $this->stores ); // If this object type can't be found, check to see if we can load one // level up (so if product-type isn't found, we try product). if ( ! array_key_exists( $object_type, $this->stores ) ) { $pieces = explode( '-', $object_type ); $object_type = $pieces[0]; } if ( array_key_exists( $object_type, $this->stores ) ) { $store = apply_filters( 'woocommerce_' . $object_type . '_data_store', $this->stores[ $object_type ] ); if ( is_object( $store ) ) { if ( ! $store instanceof WC_Object_Data_Store_Interface ) { throw new Exception( __( 'Invalid data store.', 'woocommerce' ) ); } $this->current_class_name = get_class( $store ); $this->instance = $store; } else { if ( ! class_exists( $store ) ) { throw new Exception( __( 'Invalid data store.', 'woocommerce' ) ); } $this->current_class_name = $store; $this->instance = new $store(); } } else { throw new Exception( __( 'Invalid data store.', 'woocommerce' ) ); } } /** * Reads an object from the data store. * * @since 3.0.0 * @param WC_Data $data WooCommerce data instance. */ public function read( &$data ) { $this->instance->read( $data ); } } ``` ```php // Path: wp-content\plugins\woocommerce\includes\data-stores\class-wc-product-data-store-cpt.php /** * WC_Product_Data_Store_CPT class file. * * @package WooCommerce/Classes */ use Automattic\Jetpack\Constants; if ( ! defined( 'ABSPATH' ) ) { exit; } /** * WC Product Data Store: Stored in CPT. * * @version 3.0.0 */ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Data_Store_Interface, WC_Product_Data_Store_Interface { /** * Data stored in meta keys, but not considered "meta". * * @since 3.0.0 * @var array */ protected $internal_meta_keys = array( '_visibility', '_sku', '_price', '_regular_price', '_sale_price', '_sale_price_dates_from', '_sale_price_dates_to', 'total_sales', '_tax_status', '_tax_class', '_manage_stock', '_stock', '_stock_status', '_backorders', '_low_stock_amount', '_sold_individually', '_weight', '_length', '_width', '_height', '_upsell_ids', '_crosssell_ids', '_purchase_note', '_default_attributes', '_product_attributes', '_virtual', '_downloadable', '_download_limit', '_download_expiry', '_featured', '_downloadable_files', '_wc_rating_count', '_wc_average_rating', '_wc_review_count', '_variation_description', '_thumbnail_id', '_file_paths', '_product_image_gallery', '_product_version', '_wp_old_slug', '_edit_last', '_edit_lock', ); /** * Meta data which should exist in the DB, even if empty. * * @since 3.6.0 * * @var array */ protected $must_exist_meta_keys = array( '_tax_class', ); /** * If we have already saved our extra data, don't do automatic / default handling. * * @var bool */ protected $extra_data_saved = false; /** * Stores updated props. * * @var array */ protected $updated_props = array(); /* |-------------------------------------------------------------------------- | CRUD Methods |-------------------------------------------------------------------------- */ /** * Method to read a product from the database. * * @param WC_Product $product Product object. * @throws Exception If invalid product. */ public function read( &$product ) { $product->set_defaults(); $post_object = get_post( $product->get_id() ); if ( ! $product->get_id() || ! $post_object || 'product' !== $post_object->post_type ) { throw new Exception( __( 'Invalid product.', 'woocommerce' ) ); } $product->set_props( array( 'name' => $post_object->post_title, 'slug' => $post_object->post_name, 'date_created' => 0 < $post_object->post_date_gmt ? wc_string_to_timestamp( $post_object->post_date_gmt ) : null, 'date_modified' => 0 < $post_object->post_modified_gmt ? wc_string_to_timestamp( $post_object->post_modified_gmt ) : null, 'status' => $post_object->post_status, 'description' => $post_object->post_content, '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, ) ); $this->read_attributes( $product ); $this->read_downloads( $product ); $this->read_visibility( $product ); $this->read_product_data( $product ); $this->read_extra_data( $product ); $product->set_object_read( true ); do_action( 'woocommerce_product_read', $product->get_id() ); } } ``` ```php // Path: wp-content\plugins\woocommerce\includes\data-stores\class-wc-data-store-wp.php /** * Shared logic for WP based data. * Contains functions like meta handling for all default data stores. * Your own data store doesn't need to use WC_Data_Store_WP -- you can write * your own meta handling functions. * * @version 3.0.0 * @package WooCommerce/Classes */ defined( 'ABSPATH' ) || exit; /** * WC_Data_Store_WP class. */ class WC_Data_Store_WP { /** * Meta type. This should match up with * the types available at https://developer.wordpress.org/reference/functions/add_metadata/. * WP defines 'post', 'user', 'comment', and 'term'. * * @var string */ protected $meta_type = 'post'; /** * This only needs set if you are using a custom metadata type (for example payment tokens. * This should be the name of the field your table uses for associating meta with objects. * For example, in payment_tokenmeta, this would be payment_token_id. * * @var string */ protected $object_id_field_for_meta = ''; /** * Data stored in meta keys, but not considered "meta" for an object. * * @since 3.0.0 * * @var array */ protected $internal_meta_keys = array(); /** * Meta data which should exist in the DB, even if empty. * * @since 3.6.0 * * @var array */ protected $must_exist_meta_keys = array(); } ``` ```php // Path: wp-content\plugins\woocommerce\includes\abstracts\abstract-wc-data.php /** * Abstract Data. * * Handles generic data interaction which is implemented by * the different data store classes. * * @class WC_Data * @version 3.0.0 * @package WooCommerce/Classes */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Abstract WC Data Class * * Implemented by classes using the same CRUD(s) pattern. * * @version 2.6.0 * @package WooCommerce/Abstracts */ abstract class WC_Data { /** * ID for this object. * * @since 3.0.0 * @var int */ protected $id = 0; /** * Core data for this object. Name value pairs (name + default value). * * @since 3.0.0 * @var array */ protected $data = array(); /** * Core data changes for this object. * * @since 3.0.0 * @var array */ protected $changes = array(); /** * This is false until the object is read from the DB. * * @since 3.0.0 * @var bool */ protected $object_read = false; /** * This is the name of this object type. * * @since 3.0.0 * @var string */ protected $object_type = 'data'; /** * Extra data for this object. Name value pairs (name + default value). * Used as a standard way for sub classes (like product types) to add * additional information to an inherited class. * * @since 3.0.0 * @var array */ protected $extra_data = array(); /** * Set to _data on construct so we can track and reset data if needed. * * @since 3.0.0 * @var array */ protected $default_data = array(); /** * Contains a reference to the data store for this class. * * @since 3.0.0 * @var object */ protected $data_store; /** * Stores meta in cache for future reads. * A group must be set to to enable caching. * * @since 3.0.0 * @var string */ protected $cache_group = ''; /** * Stores additional meta data. * * @since 3.0.0 * @var array */ protected $meta_data = null; /** * Default constructor. * * @param int|object|array $read ID to load from the DB (optional) or already queried data. */ public function __construct( $read = 0 ) { $this->data = array_merge( $this->data, $this->extra_data ); $this->default_data = $this->data; } /** * Set object read property. * * @since 3.0.0 * @param boolean $read Should read?. */ public function set_object_read( $read = true ) { $this->object_read = (bool) $read; } } ```