2016-11-09 12:21:18 +00:00
< ? php
2018-03-08 19:27:31 +00:00
/**
* Class WC_Coupon_Data_Store_CPT file .
*
* @ package WooCommerce\DataStore
*/
2016-11-09 12:21:18 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
exit ;
}
/**
* WC Coupon Data Store : Custom Post Type .
*
2017-03-15 16:36:53 +00:00
* @ version 3.0 . 0
2016-11-09 12:21:18 +00:00
*/
2016-11-22 13:54:51 +00:00
class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Data_Store_Interface , WC_Object_Data_Store_Interface {
2016-11-21 23:48:49 +00:00
/**
* Internal meta type used to store coupon data .
2018-03-07 19:16:01 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-21 23:48:49 +00:00
* @ var string
*/
protected $meta_type = 'post' ;
/**
* Data stored in meta keys , but not considered " meta " for a coupon .
2018-03-07 19:16:01 +00:00
*
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 (
'discount_type' ,
'coupon_amount' ,
'expiry_date' ,
2017-03-15 14:38:57 +00:00
'date_expires' ,
2016-11-21 23:48:49 +00:00
'usage_count' ,
'individual_use' ,
'product_ids' ,
'exclude_product_ids' ,
'usage_limit' ,
'usage_limit_per_user' ,
'limit_usage_to_x_items' ,
'free_shipping' ,
'product_categories' ,
'exclude_product_categories' ,
'exclude_sale_items' ,
'minimum_amount' ,
'maximum_amount' ,
'customer_email' ,
'_used_by' ,
'_edit_lock' ,
'_edit_last' ,
);
2016-11-09 12:21:18 +00:00
/**
* Method to create a new coupon in the database .
2016-11-14 10:29:25 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
2016-11-09 12:21:18 +00:00
*/
public function create ( & $coupon ) {
2017-03-13 21:24:05 +00:00
$coupon -> set_date_created ( current_time ( 'timestamp' , true ) );
2016-11-09 12:21:18 +00:00
2018-03-07 19:16:01 +00:00
$coupon_id = wp_insert_post (
apply_filters (
'woocommerce_new_coupon_data' ,
array (
'post_type' => 'shop_coupon' ,
'post_status' => 'publish' ,
'post_author' => get_current_user_id (),
'post_title' => $coupon -> get_code ( 'edit' ),
'post_content' => '' ,
'post_excerpt' => $coupon -> get_description ( 'edit' ),
'post_date' => gmdate ( 'Y-m-d H:i:s' , $coupon -> get_date_created () -> getOffsetTimestamp () ),
'post_date_gmt' => gmdate ( 'Y-m-d H:i:s' , $coupon -> get_date_created () -> getTimestamp () ),
)
2018-11-23 14:57:51 +00:00
),
true
2018-03-07 19:16:01 +00:00
);
2016-11-09 12:21:18 +00:00
if ( $coupon_id ) {
$coupon -> set_id ( $coupon_id );
2017-01-23 20:20:29 +00:00
$this -> update_post_meta ( $coupon );
2016-11-09 12:21:18 +00:00
$coupon -> save_meta_data ();
2016-11-15 23:25:36 +00:00
$coupon -> apply_changes ();
2018-09-09 00:04:13 +00:00
delete_transient ( 'rest_api_coupons_type_count' );
2019-04-17 11:50:46 +00:00
do_action ( 'woocommerce_new_coupon' , $coupon_id , $coupon );
2016-11-09 12:21:18 +00:00
}
}
/**
* Method to read a coupon .
2016-11-14 10:29:25 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-05-12 08:52:26 +00:00
*
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
2017-05-12 08:52:26 +00:00
*
2018-03-08 19:27:31 +00:00
* @ throws Exception If invalid coupon .
2016-11-09 12:21:18 +00:00
*/
public function read ( & $coupon ) {
$coupon -> set_defaults ();
2018-03-08 19:27:31 +00:00
$post_object = get_post ( $coupon -> get_id () );
if ( ! $coupon -> get_id () || ! $post_object || 'shop_coupon' !== $post_object -> post_type ) {
2016-11-09 12:21:18 +00:00
throw new Exception ( __ ( 'Invalid coupon.' , 'woocommerce' ) );
}
$coupon_id = $coupon -> get_id ();
2018-03-07 19:16:01 +00:00
$coupon -> set_props (
array (
'code' => $post_object -> post_title ,
'description' => $post_object -> post_excerpt ,
'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 ,
2019-05-15 09:23:41 +00:00
'date_expires' => metadata_exists ( 'post' , $coupon_id , 'date_expires' ) ? get_post_meta ( $coupon_id , 'date_expires' , true ) : get_post_meta ( $coupon_id , 'expiry_date' , true ), // @todo: Migrate expiry_date meta to date_expires in upgrade routine.
2018-03-07 19:16:01 +00:00
'discount_type' => get_post_meta ( $coupon_id , 'discount_type' , true ),
'amount' => get_post_meta ( $coupon_id , 'coupon_amount' , true ),
'usage_count' => get_post_meta ( $coupon_id , 'usage_count' , true ),
'individual_use' => 'yes' === get_post_meta ( $coupon_id , 'individual_use' , true ),
'product_ids' => array_filter ( ( array ) explode ( ',' , get_post_meta ( $coupon_id , 'product_ids' , true ) ) ),
'excluded_product_ids' => array_filter ( ( array ) explode ( ',' , get_post_meta ( $coupon_id , 'exclude_product_ids' , true ) ) ),
'usage_limit' => get_post_meta ( $coupon_id , 'usage_limit' , true ),
'usage_limit_per_user' => get_post_meta ( $coupon_id , 'usage_limit_per_user' , true ),
'limit_usage_to_x_items' => 0 < get_post_meta ( $coupon_id , 'limit_usage_to_x_items' , true ) ? get_post_meta ( $coupon_id , 'limit_usage_to_x_items' , true ) : null ,
'free_shipping' => 'yes' === get_post_meta ( $coupon_id , 'free_shipping' , true ),
'product_categories' => array_filter ( ( array ) get_post_meta ( $coupon_id , 'product_categories' , true ) ),
'excluded_product_categories' => array_filter ( ( array ) get_post_meta ( $coupon_id , 'exclude_product_categories' , true ) ),
'exclude_sale_items' => 'yes' === get_post_meta ( $coupon_id , 'exclude_sale_items' , true ),
'minimum_amount' => get_post_meta ( $coupon_id , 'minimum_amount' , true ),
'maximum_amount' => get_post_meta ( $coupon_id , 'maximum_amount' , true ),
'email_restrictions' => array_filter ( ( array ) get_post_meta ( $coupon_id , 'customer_email' , true ) ),
'used_by' => array_filter ( ( array ) get_post_meta ( $coupon_id , '_used_by' ) ),
)
);
2016-11-09 12:21:18 +00:00
$coupon -> read_meta_data ();
$coupon -> set_object_read ( true );
do_action ( 'woocommerce_coupon_loaded' , $coupon );
}
/**
* Updates a coupon in the database .
2016-11-14 10:29:25 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
2016-11-09 12:21:18 +00:00
*/
public function update ( & $coupon ) {
2017-04-05 21:39:41 +00:00
$coupon -> save_meta_data ();
2017-04-15 20:18:24 +00:00
$changes = $coupon -> get_changes ();
2017-04-05 21:39:41 +00:00
2017-04-15 20:48:22 +00:00
if ( array_intersect ( array ( 'code' , 'description' , 'date_created' , 'date_modified' ), array_keys ( $changes ) ) ) {
2017-04-15 20:18:24 +00:00
$post_data = array (
2017-04-15 20:48:22 +00:00
'post_title' => $coupon -> get_code ( 'edit' ),
'post_excerpt' => $coupon -> get_description ( 'edit' ),
2017-04-15 20:18:24 +00:00
'post_date' => gmdate ( 'Y-m-d H:i:s' , $coupon -> get_date_created ( 'edit' ) -> getOffsetTimestamp () ),
'post_date_gmt' => gmdate ( 'Y-m-d H:i:s' , $coupon -> get_date_created ( 'edit' ) -> getTimestamp () ),
'post_modified' => isset ( $changes [ 'date_modified' ] ) ? gmdate ( 'Y-m-d H:i:s' , $coupon -> get_date_modified ( 'edit' ) -> getOffsetTimestamp () ) : current_time ( 'mysql' ),
2018-03-07 19:16:01 +00:00
'post_modified_gmt' => isset ( $changes [ 'date_modified' ] ) ? gmdate ( 'Y-m-d H:i:s' , $coupon -> 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' => $coupon -> get_id () ) );
clean_post_cache ( $coupon -> get_id () );
} else {
wp_update_post ( array_merge ( array ( 'ID' => $coupon -> get_id () ), $post_data ) );
}
$coupon -> read_meta_data ( true ); // Refresh internal meta data, in case things were hooked into `save_post` or another WP hook.
}
2016-11-09 12:21:18 +00:00
$this -> update_post_meta ( $coupon );
2016-11-15 23:25:36 +00:00
$coupon -> apply_changes ();
2018-09-09 00:04:13 +00:00
delete_transient ( 'rest_api_coupons_type_count' );
2019-04-17 11:50:46 +00:00
do_action ( 'woocommerce_update_coupon' , $coupon -> get_id (), $coupon );
2016-11-09 12:21:18 +00:00
}
/**
* Deletes a coupon from the database .
2016-11-14 10:29:25 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2017-05-12 08:48:46 +00:00
*
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
2017-05-12 08:48:46 +00:00
* @ param array $args Array of args to pass to the delete method .
2016-11-09 12:21:18 +00:00
*/
2016-11-15 18:11:25 +00:00
public function delete ( & $coupon , $args = array () ) {
2018-03-07 19:16:01 +00:00
$args = wp_parse_args (
2018-11-23 14:57:51 +00:00
$args ,
array (
2018-03-07 19:16:01 +00:00
'force_delete' => false ,
)
);
2016-11-15 18:11:25 +00:00
2016-11-09 12:21:18 +00:00
$id = $coupon -> get_id ();
2016-11-15 18:11:25 +00:00
2017-05-30 13:44:28 +00:00
if ( ! $id ) {
return ;
}
2016-11-15 18:11:25 +00:00
if ( $args [ 'force_delete' ] ) {
2017-05-30 13:44:28 +00:00
wp_delete_post ( $id );
2017-09-10 02:47:25 +00:00
wp_cache_delete ( WC_Cache_Helper :: get_cache_prefix ( 'coupons' ) . 'coupon_id_from_code_' . $coupon -> get_code (), 'coupons' );
2016-11-09 12:21:18 +00:00
$coupon -> set_id ( 0 );
2016-12-29 22:22:12 +00:00
do_action ( 'woocommerce_delete_coupon' , $id );
2016-11-09 12:21:18 +00:00
} else {
2017-05-30 13:44:28 +00:00
wp_trash_post ( $id );
2016-12-29 22:22:12 +00:00
do_action ( 'woocommerce_trash_coupon' , $id );
2016-11-09 12:21:18 +00:00
}
}
/**
* Helper method that updates all the post meta for a coupon based on it ' s settings in the WC_Coupon class .
2016-11-14 10:29:25 +00:00
*
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-09 12:21:18 +00:00
*/
2017-01-23 20:20:29 +00:00
private function update_post_meta ( & $coupon ) {
2016-11-09 12:21:18 +00:00
$meta_key_to_props = array (
'discount_type' => 'discount_type' ,
'coupon_amount' => 'amount' ,
'individual_use' => 'individual_use' ,
'product_ids' => 'product_ids' ,
'exclude_product_ids' => 'excluded_product_ids' ,
'usage_limit' => 'usage_limit' ,
'usage_limit_per_user' => 'usage_limit_per_user' ,
'limit_usage_to_x_items' => 'limit_usage_to_x_items' ,
'usage_count' => 'usage_count' ,
2018-03-07 19:16:01 +00:00
'date_expires' => 'date_expires' ,
2016-11-09 12:21:18 +00:00
'free_shipping' => 'free_shipping' ,
'product_categories' => 'product_categories' ,
'exclude_product_categories' => 'excluded_product_categories' ,
'exclude_sale_items' => 'exclude_sale_items' ,
'minimum_amount' => 'minimum_amount' ,
'maximum_amount' => 'maximum_amount' ,
'customer_email' => 'email_restrictions' ,
);
2017-01-23 20:20:29 +00:00
$props_to_update = $this -> get_props_to_update ( $coupon , $meta_key_to_props );
foreach ( $props_to_update as $meta_key => $prop ) {
2016-11-09 12:21:18 +00:00
$value = $coupon -> { " get_ $prop " }( 'edit' );
2019-02-19 15:35:05 +00:00
$value = is_string ( $value ) ? wp_slash ( $value ) : $value ;
2016-11-09 12:21:18 +00:00
switch ( $prop ) {
2018-03-07 19:16:01 +00:00
case 'individual_use' :
case 'free_shipping' :
case 'exclude_sale_items' :
2019-02-19 15:35:05 +00:00
$value = wc_bool_to_string ( $value );
2016-11-09 12:21:18 +00:00
break ;
2018-03-07 19:16:01 +00:00
case 'product_ids' :
case 'excluded_product_ids' :
2019-02-19 15:35:05 +00:00
$value = implode ( ',' , array_filter ( array_map ( 'intval' , $value ) ) );
2016-12-12 15:29:53 +00:00
break ;
2018-03-07 19:16:01 +00:00
case 'product_categories' :
case 'excluded_product_categories' :
2019-02-19 15:35:05 +00:00
$value = array_filter ( array_map ( 'intval' , $value ) );
2016-11-09 12:21:18 +00:00
break ;
2018-03-07 19:16:01 +00:00
case 'email_restrictions' :
2019-02-19 15:35:05 +00:00
$value = array_filter ( array_map ( 'sanitize_email' , $value ) );
2016-11-09 12:21:18 +00:00
break ;
2018-03-07 19:16:01 +00:00
case 'date_expires' :
2019-02-19 15:35:05 +00:00
$value = $value ? $value -> getTimestamp () : null ;
2016-11-09 12:21:18 +00:00
break ;
}
2019-02-19 15:35:05 +00:00
$updated = $this -> update_or_delete_post_meta ( $coupon , $meta_key , $value );
2016-11-09 12:21:18 +00:00
if ( $updated ) {
2019-02-19 15:35:05 +00:00
$this -> updated_props [] = $prop ;
2016-11-09 12:21:18 +00:00
}
}
2017-01-24 21:38:02 +00:00
2019-11-09 21:07:39 +00:00
do_action ( 'woocommerce_coupon_object_updated_props' , $coupon , $this -> updated_props );
2016-11-09 12:21:18 +00:00
}
/**
* Increase usage count for current coupon .
2016-11-14 10:29:25 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
* @ param string $used_by Either user ID or billing email .
* @ return int New usage count .
2016-11-09 12:21:18 +00:00
*/
public function increase_usage_count ( & $coupon , $used_by = '' ) {
2017-03-10 17:18:14 +00:00
$new_count = $this -> update_usage_count_meta ( $coupon , 'increase' );
2016-11-09 12:21:18 +00:00
if ( $used_by ) {
add_post_meta ( $coupon -> get_id (), '_used_by' , strtolower ( $used_by ) );
$coupon -> set_used_by ( ( array ) get_post_meta ( $coupon -> get_id (), '_used_by' ) );
}
2018-05-06 11:09:59 +00:00
do_action ( 'woocommerce_increase_coupon_usage_count' , $coupon , $new_count , $used_by );
2017-03-08 23:27:37 +00:00
return $new_count ;
2016-11-09 12:21:18 +00:00
}
/**
* Decrease usage count for current coupon .
2016-11-14 10:29:25 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
* @ param string $used_by Either user ID or billing email .
* @ return int New usage count .
2016-11-09 12:21:18 +00:00
*/
public function decrease_usage_count ( & $coupon , $used_by = '' ) {
global $wpdb ;
2017-03-10 17:18:14 +00:00
$new_count = $this -> update_usage_count_meta ( $coupon , 'decrease' );
2016-11-09 12:21:18 +00:00
if ( $used_by ) {
/**
* We ' re doing this the long way because `delete_post_meta( $id, $key, $value )` deletes .
* all instances where the key and value match , and we only want to delete one .
*/
$meta_id = $wpdb -> get_var ( $wpdb -> prepare ( " SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_used_by' AND meta_value = %s AND post_id = %d LIMIT 1; " , $used_by , $coupon -> get_id () ) );
if ( $meta_id ) {
delete_metadata_by_mid ( 'post' , $meta_id );
$coupon -> set_used_by ( ( array ) get_post_meta ( $coupon -> get_id (), '_used_by' ) );
}
}
2018-05-06 11:09:59 +00:00
do_action ( 'woocommerce_decrease_coupon_usage_count' , $coupon , $new_count , $used_by );
2017-03-08 23:27:37 +00:00
return $new_count ;
}
/**
2017-03-09 16:51:47 +00:00
* Increase or decrease the usage count for a coupon by 1.
2017-03-08 23:27:37 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
* @ param string $operation 'increase' or 'decrease' .
2017-03-08 23:27:37 +00:00
* @ return int New usage count
*/
2017-03-10 17:18:14 +00:00
private function update_usage_count_meta ( & $coupon , $operation = 'increase' ) {
2017-03-08 23:27:37 +00:00
global $wpdb ;
2018-03-07 19:16:01 +00:00
$id = $coupon -> get_id ();
2017-03-09 16:51:47 +00:00
$operator = ( 'increase' === $operation ) ? '+' : '-' ;
2017-03-08 23:27:37 +00:00
2017-03-10 18:28:56 +00:00
add_post_meta ( $id , 'usage_count' , $coupon -> get_usage_count ( 'edit' ), true );
2018-03-08 19:27:31 +00:00
$wpdb -> query (
$wpdb -> prepare (
2018-11-23 17:10:52 +00:00
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2018-11-23 14:57:51 +00:00
" UPDATE $wpdb->postmeta SET meta_value = meta_value { $operator } 1 WHERE meta_key = 'usage_count' AND post_id = %d; " ,
2018-03-08 19:27:31 +00:00
$id
)
);
2017-03-08 23:27:37 +00:00
2017-03-10 18:28:56 +00:00
// Get the latest value direct from the DB, instead of possibly the WP meta cache.
return ( int ) $wpdb -> get_var ( $wpdb -> prepare ( " SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = 'usage_count' AND post_id = %d; " , $id ) );
2016-11-09 12:21:18 +00:00
}
/**
* Get the number of uses for a coupon by user ID .
2016-11-14 10:29:25 +00:00
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-03-08 19:27:31 +00:00
* @ param WC_Coupon $coupon Coupon object .
* @ param id $user_id User ID .
2016-11-09 12:21:18 +00:00
* @ return int
*/
public function get_usage_by_user_id ( & $coupon , $user_id ) {
global $wpdb ;
return $wpdb -> get_var ( $wpdb -> prepare ( " SELECT COUNT( meta_id ) FROM { $wpdb -> postmeta } WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %d; " , $coupon -> get_id (), $user_id ) );
}
2019-05-27 14:45:29 +00:00
/**
* Get the number of uses for a coupon by email address
*
* @ since 3.6 . 4
* @ param WC_Coupon $coupon Coupon object .
* @ param string $email Email address .
* @ return int
*/
public function get_usage_by_email ( & $coupon , $email ) {
global $wpdb ;
return $wpdb -> get_var ( $wpdb -> prepare ( " SELECT COUNT( meta_id ) FROM { $wpdb -> postmeta } WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %s; " , $coupon -> get_id (), $email ) );
}
2016-11-14 10:29:25 +00:00
/**
* Return a coupon code for a specific ID .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-03-08 19:27:31 +00:00
* @ param int $id Coupon ID .
2016-11-14 10:29:25 +00:00
* @ return string Coupon Code
*/
public function get_code_by_id ( $id ) {
global $wpdb ;
2018-03-07 19:16:01 +00:00
return $wpdb -> get_var (
$wpdb -> prepare (
" SELECT post_title
FROM $wpdb -> posts
WHERE ID = % d
AND post_type = 'shop_coupon'
AND post_status = 'publish' " ,
$id
)
);
2016-11-14 10:29:25 +00:00
}
/**
* Return an array of IDs for for a specific coupon code .
* Can return multiple to check for existence .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2018-03-08 19:27:31 +00:00
* @ param string $code Coupon code .
2016-11-14 10:29:25 +00:00
* @ return array Array of IDs .
*/
public function get_ids_by_code ( $code ) {
global $wpdb ;
2018-03-07 19:16:01 +00:00
return $wpdb -> get_col (
$wpdb -> prepare (
" SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' ORDER BY post_date DESC " ,
$code
)
);
2016-11-14 10:29:25 +00:00
}
2016-11-09 12:21:18 +00:00
}