2016-11-17 14:37:29 +00:00
< ? php
2017-11-23 14:43:20 +00:00
/**
* Abstract_WC_Order_Data_Store_CPT class file .
*
2020-08-05 16:36:24 +00:00
* @ package WooCommerce\Classes
2017-11-23 14:43:20 +00:00
*/
2020-02-01 06:18:47 +00:00
use Automattic\Jetpack\Constants ;
2016-11-17 14:37:29 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
exit ;
}
/**
* Abstract Order Data Store : Stored in CPT .
*
2017-03-15 16:36:53 +00:00
* @ version 3.0 . 0
2016-11-17 14:37:29 +00:00
*/
2016-11-22 13:54:51 +00:00
abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Data_Store_Interface , WC_Abstract_Order_Data_Store_Interface {
2016-11-21 23:48:49 +00:00
/**
* Internal meta type used to store order data .
*
* @ var string
*/
protected $meta_type = 'post' ;
/**
* Data stored in meta keys , but not considered " meta " for an order .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-21 23:48:49 +00:00
* @ var array
*/
protected $internal_meta_keys = array (
'_order_currency' ,
'_cart_discount' ,
'_cart_discount_tax' ,
'_order_shipping' ,
'_order_shipping_tax' ,
'_order_tax' ,
'_order_total' ,
'_order_version' ,
'_prices_include_tax' ,
'_payment_tokens' ,
);
2016-11-17 14:37:29 +00:00
/*
|--------------------------------------------------------------------------
| CRUD Methods
|--------------------------------------------------------------------------
*/
/**
* Method to create a new order in the database .
2017-11-23 12:41:31 +00:00
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
2016-11-17 14:37:29 +00:00
*/
public function create ( & $order ) {
2020-02-01 06:18:47 +00:00
$order -> set_version ( Constants :: get_constant ( 'WC_VERSION' ) );
2016-11-17 14:37:29 +00:00
$order -> set_currency ( $order -> get_currency () ? $order -> get_currency () : get_woocommerce_currency () );
2020-05-20 21:04:16 +00:00
if ( ! $order -> get_date_created ( 'edit' ) ) {
$order -> set_date_created ( time () );
}
2016-11-17 14:37:29 +00:00
2017-11-23 12:41:31 +00:00
$id = wp_insert_post (
apply_filters (
'woocommerce_new_order_data' ,
array (
'post_date' => gmdate ( 'Y-m-d H:i:s' , $order -> get_date_created ( 'edit' ) -> getOffsetTimestamp () ),
'post_date_gmt' => gmdate ( 'Y-m-d H:i:s' , $order -> get_date_created ( 'edit' ) -> getTimestamp () ),
'post_type' => $order -> get_type ( 'edit' ),
2019-04-03 13:21:38 +00:00
'post_status' => $this -> get_post_status ( $order ),
2017-11-23 12:41:31 +00:00
'ping_status' => 'closed' ,
2018-10-29 14:48:44 +00:00
'post_author' => 1 ,
2017-11-23 12:41:31 +00:00
'post_title' => $this -> get_post_title (),
2020-05-20 20:14:17 +00:00
'post_password' => $this -> get_order_key ( $order ),
2017-11-23 12:41:31 +00:00
'post_parent' => $order -> get_parent_id ( 'edit' ),
'post_excerpt' => $this -> get_post_excerpt ( $order ),
)
2019-02-13 17:52:28 +00:00
),
true
2017-11-23 12:41:31 +00:00
);
2016-11-17 14:37:29 +00:00
if ( $id && ! is_wp_error ( $id ) ) {
$order -> set_id ( $id );
2017-01-23 20:20:29 +00:00
$this -> update_post_meta ( $order );
2016-11-17 14:37:29 +00:00
$order -> save_meta_data ();
$order -> apply_changes ();
$this -> clear_caches ( $order );
}
}
/**
* Method to read an order from the database .
2017-05-12 08:52:26 +00:00
*
2020-06-24 16:25:19 +00:00
* @ param WC_Order $order Order object .
2017-05-12 08:52:26 +00:00
*
2017-11-23 14:43:20 +00:00
* @ throws Exception If passed order is invalid .
2016-11-17 14:37:29 +00:00
*/
public function read ( & $order ) {
$order -> set_defaults ();
2017-11-23 14:43:20 +00:00
$post_object = get_post ( $order -> get_id () );
2018-03-08 19:27:03 +00:00
if ( ! $order -> get_id () || ! $post_object || ! in_array ( $post_object -> post_type , wc_get_order_types (), true ) ) {
2016-11-17 14:37:29 +00:00
throw new Exception ( __ ( 'Invalid order.' , 'woocommerce' ) );
}
2017-11-23 12:41:31 +00:00
$order -> set_props (
array (
'parent_id' => $post_object -> post_parent ,
2020-10-22 07:21:15 +00:00
'date_created' => $this -> string_to_timestamp ( $post_object -> post_date_gmt ),
'date_modified' => $this -> string_to_timestamp ( $post_object -> post_modified_gmt ),
2017-11-23 12:41:31 +00:00
'status' => $post_object -> post_status ,
)
);
2017-03-08 16:51:46 +00:00
2016-11-17 14:37:29 +00:00
$this -> read_order_data ( $order , $post_object );
2020-09-16 15:54:12 +00:00
$order -> read_meta_data ();
$order -> set_object_read ( true );
/**
* In older versions , discounts may have been stored differently .
* Update them now so if the object is saved , the correct values are
* stored . @ todo When meta is flattened , handle this during migration .
*/
if ( version_compare ( $order -> get_version ( 'edit' ), '2.3.7' , '<' ) && $order -> get_prices_include_tax ( 'edit' ) ) {
$order -> set_discount_total ( ( float ) get_post_meta ( $order -> get_id (), '_cart_discount' , true ) - ( float ) get_post_meta ( $order -> get_id (), '_cart_discount_tax' , true ) );
}
2020-06-24 16:25:19 +00:00
}
2017-02-28 17:49:54 +00:00
2016-11-17 14:37:29 +00:00
/**
* Method to update an order in the database .
2017-11-23 12:41:31 +00:00
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
2016-11-17 14:37:29 +00:00
*/
public function update ( & $order ) {
2017-04-05 21:39:41 +00:00
$order -> save_meta_data ();
2020-02-01 06:18:47 +00:00
$order -> set_version ( Constants :: get_constant ( 'WC_VERSION' ) );
2016-11-17 14:37:29 +00:00
2018-05-15 18:15:42 +00:00
if ( null === $order -> get_date_created ( 'edit' ) ) {
2019-12-20 17:23:05 +00:00
$order -> set_date_created ( time () );
2018-05-15 18:15:42 +00:00
}
2017-03-02 16:46:59 +00:00
$changes = $order -> get_changes ();
2016-11-17 14:37:29 +00:00
2017-03-02 16:46:59 +00:00
// Only update the post when the post data changes.
2018-10-29 14:48:44 +00:00
if ( array_intersect ( array ( 'date_created' , 'date_modified' , 'status' , 'parent_id' , 'post_excerpt' ), array_keys ( $changes ) ) ) {
2017-04-15 20:18:24 +00:00
$post_data = array (
2017-03-22 12:01:38 +00:00
'post_date' => gmdate ( 'Y-m-d H:i:s' , $order -> get_date_created ( 'edit' ) -> getOffsetTimestamp () ),
'post_date_gmt' => gmdate ( 'Y-m-d H:i:s' , $order -> get_date_created ( 'edit' ) -> getTimestamp () ),
2019-04-03 13:21:38 +00:00
'post_status' => $this -> get_post_status ( $order ),
2017-03-02 16:46:59 +00:00
'post_parent' => $order -> get_parent_id (),
'post_excerpt' => $this -> get_post_excerpt ( $order ),
2017-03-22 12:01:38 +00:00
'post_modified' => isset ( $changes [ 'date_modified' ] ) ? gmdate ( 'Y-m-d H:i:s' , $order -> get_date_modified ( 'edit' ) -> getOffsetTimestamp () ) : current_time ( 'mysql' ),
'post_modified_gmt' => isset ( $changes [ 'date_modified' ] ) ? gmdate ( 'Y-m-d H:i:s' , $order -> get_date_modified ( 'edit' ) -> getTimestamp () ) : current_time ( 'mysql' , 1 ),
2017-04-15 20:18:24 +00:00
);
/**
* When updating this object , to prevent infinite loops , use $wpdb
* to update data , since wp_update_post spawns more calls to the
* save_post action .
*
* This ensures hooks are fired by either WP itself ( admin screen save ),
* or an update purely from CRUD .
*/
if ( doing_action ( 'save_post' ) ) {
$GLOBALS [ 'wpdb' ] -> update ( $GLOBALS [ 'wpdb' ] -> posts , $post_data , array ( 'ID' => $order -> get_id () ) );
clean_post_cache ( $order -> get_id () );
} else {
wp_update_post ( array_merge ( array ( 'ID' => $order -> get_id () ), $post_data ) );
}
2017-04-05 21:39:41 +00:00
$order -> read_meta_data ( true ); // Refresh internal meta data, in case things were hooked into `save_post` or another WP hook.
2017-03-02 16:46:59 +00:00
}
2016-11-17 14:37:29 +00:00
$this -> update_post_meta ( $order );
$order -> apply_changes ();
$this -> clear_caches ( $order );
}
/**
* Method to delete an order from the database .
2017-11-23 12:41:31 +00:00
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
2017-11-23 12:41:31 +00:00
* @ param array $args Array of args to pass to the delete method .
2017-11-23 14:43:20 +00:00
*
* @ return void
2016-11-17 14:37:29 +00:00
*/
public function delete ( & $order , $args = array () ) {
$id = $order -> get_id ();
2017-11-23 12:41:31 +00:00
$args = wp_parse_args (
$args ,
array (
'force_delete' => false ,
)
);
2016-11-17 14:37:29 +00:00
2017-05-30 13:44:28 +00:00
if ( ! $id ) {
return ;
}
2016-11-17 14:37:29 +00:00
if ( $args [ 'force_delete' ] ) {
wp_delete_post ( $id );
$order -> set_id ( 0 );
2016-12-29 22:22:12 +00:00
do_action ( 'woocommerce_delete_order' , $id );
2016-11-17 14:37:29 +00:00
} else {
wp_trash_post ( $id );
$order -> set_status ( 'trash' );
2016-12-29 22:22:12 +00:00
do_action ( 'woocommerce_trash_order' , $id );
2016-11-17 14:37:29 +00:00
}
}
/*
|--------------------------------------------------------------------------
| Additional Methods
|--------------------------------------------------------------------------
*/
2019-04-03 13:21:38 +00:00
/**
* Get the status to save to the post object .
*
* Plugins extending the order classes can override this to change the stored status / add prefixes etc .
*
* @ since 3.6 . 0
* @ param WC_order $order Order object .
* @ return string
*/
protected function get_post_status ( $order ) {
$order_status = $order -> get_status ( 'edit' );
if ( ! $order_status ) {
$order_status = apply_filters ( 'woocommerce_default_order_status' , 'pending' );
}
$post_status = $order_status ;
$valid_statuses = get_post_stati ();
// Add a wc- prefix to the status, but exclude some core statuses which should not be prefixed.
// @todo In the future this should only happen based on `wc_is_order_status`, but in order to
// preserve back-compatibility this happens to all statuses except a select few. A doing_it_wrong
// Notice will be needed here, followed by future removal.
if ( ! in_array ( $post_status , array ( 'auto-draft' , 'draft' , 'trash' ), true ) && in_array ( 'wc-' . $post_status , $valid_statuses , true ) ) {
$post_status = 'wc-' . $post_status ;
}
return $post_status ;
}
2016-11-25 21:46:45 +00:00
/**
* Excerpt for post .
*
2017-11-23 14:43:20 +00:00
* @ param WC_order $order Order object .
2016-11-25 21:46:45 +00:00
* @ return string
*/
protected function get_post_excerpt ( $order ) {
return '' ;
}
2016-11-21 16:46:32 +00:00
/**
* Get a title for the new post type .
*
* @ return string
*/
protected function get_post_title () {
// @codingStandardsIgnoreStart
/* translators: %s: Order date */
return sprintf ( __ ( 'Order – %s' , 'woocommerce' ), strftime ( _x ( '%b %d, %Y @ %I:%M %p' , 'Order date parsed by strftime' , 'woocommerce' ) ) );
// @codingStandardsIgnoreEnd
}
2020-05-20 20:14:17 +00:00
/**
* Get order key .
*
* @ since 4.3 . 0
* @ param WC_order $order Order object .
* @ return string
*/
protected function get_order_key ( $order ) {
return wc_generate_order_key ();
}
2016-11-17 14:37:29 +00:00
/**
* Read order data . Can be overridden by child classes to load other props .
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
* @ param object $post_object Post object .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-17 14:37:29 +00:00
*/
protected function read_order_data ( & $order , $post_object ) {
$id = $order -> get_id ();
2017-11-23 12:41:31 +00:00
$order -> set_props (
array (
'currency' => get_post_meta ( $id , '_order_currency' , true ),
'discount_total' => get_post_meta ( $id , '_cart_discount' , true ),
'discount_tax' => get_post_meta ( $id , '_cart_discount_tax' , true ),
'shipping_total' => get_post_meta ( $id , '_order_shipping' , true ),
'shipping_tax' => get_post_meta ( $id , '_order_shipping_tax' , true ),
'cart_tax' => get_post_meta ( $id , '_order_tax' , true ),
'total' => get_post_meta ( $id , '_order_total' , true ),
'version' => get_post_meta ( $id , '_order_version' , true ),
'prices_include_tax' => metadata_exists ( 'post' , $id , '_prices_include_tax' ) ? 'yes' === get_post_meta ( $id , '_prices_include_tax' , true ) : 'yes' === get_option ( 'woocommerce_prices_include_tax' ),
)
);
2016-11-17 14:37:29 +00:00
// Gets extra data associated with the order if needed.
foreach ( $order -> get_extra_data_keys () as $key ) {
$function = 'set_' . $key ;
if ( is_callable ( array ( $order , $function ) ) ) {
$order -> { $function }( get_post_meta ( $order -> get_id (), '_' . $key , true ) );
}
}
}
/**
* Helper method that updates all the post meta for an order based on it ' s settings in the WC_Order class .
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-17 14:37:29 +00:00
*/
2017-01-23 20:20:29 +00:00
protected function update_post_meta ( & $order ) {
2016-11-17 14:37:29 +00:00
$updated_props = array ();
$meta_key_to_props = array (
'_order_currency' => 'currency' ,
'_cart_discount' => 'discount_total' ,
'_cart_discount_tax' => 'discount_tax' ,
'_order_shipping' => 'shipping_total' ,
'_order_shipping_tax' => 'shipping_tax' ,
'_order_tax' => 'cart_tax' ,
'_order_total' => 'total' ,
'_order_version' => 'version' ,
'_prices_include_tax' => 'prices_include_tax' ,
);
2017-01-23 20:20:29 +00:00
$props_to_update = $this -> get_props_to_update ( $order , $meta_key_to_props );
2017-03-09 14:40:19 +00:00
2017-01-23 20:20:29 +00:00
foreach ( $props_to_update as $meta_key => $prop ) {
2017-02-28 17:49:54 +00:00
$value = $order -> { " get_ $prop " }( 'edit' );
2019-02-19 15:35:05 +00:00
$value = is_string ( $value ) ? wp_slash ( $value ) : $value ;
2017-02-28 17:49:54 +00:00
if ( 'prices_include_tax' === $prop ) {
$value = $value ? 'yes' : 'no' ;
}
2019-02-19 15:35:05 +00:00
$updated = $this -> update_or_delete_post_meta ( $order , $meta_key , $value );
if ( $updated ) {
2016-11-17 14:37:29 +00:00
$updated_props [] = $prop ;
}
}
2017-01-24 21:38:02 +00:00
do_action ( 'woocommerce_order_object_updated_props' , $order , $updated_props );
2016-11-17 14:37:29 +00:00
}
/**
* Clear any caches .
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-17 14:37:29 +00:00
*/
protected function clear_caches ( & $order ) {
clean_post_cache ( $order -> get_id () );
2016-11-18 11:14:09 +00:00
wc_delete_shop_order_transients ( $order );
2017-03-21 23:50:05 +00:00
wp_cache_delete ( 'order-items-' . $order -> get_id (), 'orders' );
2016-11-17 14:37:29 +00:00
}
/**
* Read order items of a specific type from the database for this order .
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
* @ param string $type Order item type .
2016-11-17 14:37:29 +00:00
* @ return array
*/
public function read_items ( $order , $type ) {
global $wpdb ;
2017-03-21 23:37:47 +00:00
// Get from cache if available.
2018-06-14 13:52:25 +00:00
$items = 0 < $order -> get_id () ? wp_cache_get ( 'order-items-' . $order -> get_id (), 'orders' ) : false ;
2017-03-21 23:37:47 +00:00
if ( false === $items ) {
2017-11-23 14:43:20 +00:00
$items = $wpdb -> get_results (
$wpdb -> prepare ( " SELECT order_item_type, order_item_id, order_id, order_item_name FROM { $wpdb -> prefix } woocommerce_order_items WHERE order_id = %d ORDER BY order_item_id; " , $order -> get_id () )
);
2017-03-21 23:50:05 +00:00
foreach ( $items as $item ) {
wp_cache_set ( 'item-' . $item -> order_item_id , $item , 'order-items' );
}
2018-06-14 13:52:25 +00:00
if ( 0 < $order -> get_id () ) {
wp_cache_set ( 'order-items-' . $order -> get_id (), $items , 'orders' );
}
2017-03-21 23:37:47 +00:00
}
$items = wp_list_filter ( $items , array ( 'order_item_type' => $type ) );
2016-11-17 14:37:29 +00:00
if ( ! empty ( $items ) ) {
$items = array_map ( array ( 'WC_Order_Factory' , 'get_order_item' ), array_combine ( wp_list_pluck ( $items , 'order_item_id' ), $items ) );
} else {
$items = array ();
}
return $items ;
}
/**
* Remove all line items ( products , coupons , shipping , taxes ) from the order .
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
2017-08-18 16:18:19 +00:00
* @ param string $type Order item type . Default null .
2016-11-17 14:37:29 +00:00
*/
public function delete_items ( $order , $type = null ) {
global $wpdb ;
if ( ! empty ( $type ) ) {
$wpdb -> query ( $wpdb -> prepare ( " DELETE FROM itemmeta USING { $wpdb -> prefix } woocommerce_order_itemmeta itemmeta INNER JOIN { $wpdb -> prefix } woocommerce_order_items items WHERE itemmeta.order_item_id = items.order_item_id AND items.order_id = %d AND items.order_item_type = %s " , $order -> get_id (), $type ) );
$wpdb -> query ( $wpdb -> prepare ( " DELETE FROM { $wpdb -> prefix } woocommerce_order_items WHERE order_id = %d AND order_item_type = %s " , $order -> get_id (), $type ) );
} else {
$wpdb -> query ( $wpdb -> prepare ( " DELETE FROM itemmeta USING { $wpdb -> prefix } woocommerce_order_itemmeta itemmeta INNER JOIN { $wpdb -> prefix } woocommerce_order_items items WHERE itemmeta.order_item_id = items.order_item_id and items.order_id = %d " , $order -> get_id () ) );
$wpdb -> query ( $wpdb -> prepare ( " DELETE FROM { $wpdb -> prefix } woocommerce_order_items WHERE order_id = %d " , $order -> get_id () ) );
}
2017-03-21 23:37:47 +00:00
$this -> clear_caches ( $order );
2016-11-17 14:37:29 +00:00
}
/**
* Get token ids for an order .
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
2016-11-17 14:37:29 +00:00
* @ return array
*/
public function get_payment_token_ids ( $order ) {
$token_ids = array_filter ( ( array ) get_post_meta ( $order -> get_id (), '_payment_tokens' , true ) );
return $token_ids ;
}
/**
* Update token ids for an order .
*
2017-11-23 14:43:20 +00:00
* @ param WC_Order $order Order object .
* @ param array $token_ids Payment token ids .
2016-11-17 14:37:29 +00:00
*/
public function update_payment_token_ids ( $order , $token_ids ) {
update_post_meta ( $order -> get_id (), '_payment_tokens' , $token_ids );
}
}