| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- <?php
- /**
- * Class WC_Coupon_Data_Store_CPT file.
- *
- * @package WooCommerce\DataStore
- */
- if ( ! defined( 'ABSPATH' ) ) {
- exit;
- }
- /**
- * WC Coupon Data Store: Custom Post Type.
- *
- * @version 3.0.0
- */
- class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Data_Store_Interface, WC_Object_Data_Store_Interface {
- /**
- * Internal meta type used to store coupon data.
- *
- * @since 3.0.0
- * @var string
- */
- protected $meta_type = 'post';
- /**
- * Data stored in meta keys, but not considered "meta" for a coupon.
- *
- * @since 3.0.0
- * @var array
- */
- protected $internal_meta_keys = array(
- 'discount_type',
- 'coupon_amount',
- 'expiry_date',
- 'date_expires',
- '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',
- );
- /**
- * Method to create a new coupon in the database.
- *
- * @since 3.0.0
- * @param WC_Coupon $coupon Coupon object.
- */
- public function create( &$coupon ) {
- $coupon->set_date_created( current_time( 'timestamp', true ) );
- $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() ),
- )
- ), true
- );
- if ( $coupon_id ) {
- $coupon->set_id( $coupon_id );
- $this->update_post_meta( $coupon );
- $coupon->save_meta_data();
- $coupon->apply_changes();
- do_action( 'woocommerce_new_coupon', $coupon_id );
- }
- }
- /**
- * Method to read a coupon.
- *
- * @since 3.0.0
- *
- * @param WC_Coupon $coupon Coupon object.
- *
- * @throws Exception If invalid coupon.
- */
- public function read( &$coupon ) {
- $coupon->set_defaults();
- $post_object = get_post( $coupon->get_id() );
- if ( ! $coupon->get_id() || ! $post_object || 'shop_coupon' !== $post_object->post_type ) {
- throw new Exception( __( 'Invalid coupon.', 'woocommerce' ) );
- }
- $coupon_id = $coupon->get_id();
- $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,
- '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 ),
- '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' ) ),
- )
- );
- $coupon->read_meta_data();
- $coupon->set_object_read( true );
- do_action( 'woocommerce_coupon_loaded', $coupon );
- }
- /**
- * Updates a coupon in the database.
- *
- * @since 3.0.0
- * @param WC_Coupon $coupon Coupon object.
- */
- public function update( &$coupon ) {
- $coupon->save_meta_data();
- $changes = $coupon->get_changes();
- if ( array_intersect( array( 'code', 'description', 'date_created', 'date_modified' ), array_keys( $changes ) ) ) {
- $post_data = array(
- 'post_title' => $coupon->get_code( 'edit' ),
- 'post_excerpt' => $coupon->get_description( 'edit' ),
- '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' ),
- 'post_modified_gmt' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $coupon->get_date_modified( 'edit' )->getTimestamp() ) : current_time( 'mysql', 1 ),
- );
- /**
- * 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.
- }
- $this->update_post_meta( $coupon );
- $coupon->apply_changes();
- do_action( 'woocommerce_update_coupon', $coupon->get_id() );
- }
- /**
- * Deletes a coupon from the database.
- *
- * @since 3.0.0
- *
- * @param WC_Coupon $coupon Coupon object.
- * @param array $args Array of args to pass to the delete method.
- */
- public function delete( &$coupon, $args = array() ) {
- $args = wp_parse_args(
- $args, array(
- 'force_delete' => false,
- )
- );
- $id = $coupon->get_id();
- if ( ! $id ) {
- return;
- }
- if ( $args['force_delete'] ) {
- wp_delete_post( $id );
- wp_cache_delete( WC_Cache_Helper::get_cache_prefix( 'coupons' ) . 'coupon_id_from_code_' . $coupon->get_code(), 'coupons' );
- $coupon->set_id( 0 );
- do_action( 'woocommerce_delete_coupon', $id );
- } else {
- wp_trash_post( $id );
- do_action( 'woocommerce_trash_coupon', $id );
- }
- }
- /**
- * Helper method that updates all the post meta for a coupon based on it's settings in the WC_Coupon class.
- *
- * @param WC_Coupon $coupon Coupon object.
- * @since 3.0.0
- */
- private function update_post_meta( &$coupon ) {
- $updated_props = array();
- $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',
- 'date_expires' => 'date_expires',
- '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',
- );
- $props_to_update = $this->get_props_to_update( $coupon, $meta_key_to_props );
- foreach ( $props_to_update as $meta_key => $prop ) {
- $value = $coupon->{"get_$prop"}( 'edit' );
- switch ( $prop ) {
- case 'individual_use':
- case 'free_shipping':
- case 'exclude_sale_items':
- $updated = update_post_meta( $coupon->get_id(), $meta_key, wc_bool_to_string( $value ) );
- break;
- case 'product_ids':
- case 'excluded_product_ids':
- $updated = update_post_meta( $coupon->get_id(), $meta_key, implode( ',', array_filter( array_map( 'intval', $value ) ) ) );
- break;
- case 'product_categories':
- case 'excluded_product_categories':
- $updated = update_post_meta( $coupon->get_id(), $meta_key, array_filter( array_map( 'intval', $value ) ) );
- break;
- case 'email_restrictions':
- $updated = update_post_meta( $coupon->get_id(), $meta_key, array_filter( array_map( 'sanitize_email', $value ) ) );
- break;
- case 'date_expires':
- $updated = update_post_meta( $coupon->get_id(), $meta_key, ( $value ? $value->getTimestamp() : null ) );
- update_post_meta( $coupon->get_id(), 'expiry_date', ( $value ? $value->date( 'Y-m-d' ) : '' ) ); // Update the old meta key for backwards compatibility.
- break;
- default:
- $updated = update_post_meta( $coupon->get_id(), $meta_key, $value );
- break;
- }
- if ( $updated ) {
- $updated_props[] = $prop;
- }
- }
- do_action( 'woocommerce_coupon_object_updated_props', $coupon, $updated_props );
- }
- /**
- * Increase usage count for current coupon.
- *
- * @since 3.0.0
- * @param WC_Coupon $coupon Coupon object.
- * @param string $used_by Either user ID or billing email.
- * @return int New usage count.
- */
- public function increase_usage_count( &$coupon, $used_by = '' ) {
- $new_count = $this->update_usage_count_meta( $coupon, 'increase' );
- 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' ) );
- }
- return $new_count;
- }
- /**
- * Decrease usage count for current coupon.
- *
- * @since 3.0.0
- * @param WC_Coupon $coupon Coupon object.
- * @param string $used_by Either user ID or billing email.
- * @return int New usage count.
- */
- public function decrease_usage_count( &$coupon, $used_by = '' ) {
- global $wpdb;
- $new_count = $this->update_usage_count_meta( $coupon, 'decrease' );
- 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' ) );
- }
- }
- return $new_count;
- }
- /**
- * Increase or decrease the usage count for a coupon by 1.
- *
- * @since 3.0.0
- * @param WC_Coupon $coupon Coupon object.
- * @param string $operation 'increase' or 'decrease'.
- * @return int New usage count
- */
- private function update_usage_count_meta( &$coupon, $operation = 'increase' ) {
- global $wpdb;
- $id = $coupon->get_id();
- $operator = ( 'increase' === $operation ) ? '+' : '-';
- add_post_meta( $id, 'usage_count', $coupon->get_usage_count( 'edit' ), true );
- $wpdb->query(
- $wpdb->prepare(
- "UPDATE $wpdb->postmeta SET meta_value = meta_value {$operator} 1 WHERE meta_key = 'usage_count' AND post_id = %d;", // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared
- $id
- )
- );
- // 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 ) );
- }
- /**
- * Get the number of uses for a coupon by user ID.
- *
- * @since 3.0.0
- * @param WC_Coupon $coupon Coupon object.
- * @param id $user_id User ID.
- * @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 ) );
- }
- /**
- * Return a coupon code for a specific ID.
- *
- * @since 3.0.0
- * @param int $id Coupon ID.
- * @return string Coupon Code
- */
- public function get_code_by_id( $id ) {
- global $wpdb;
- 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
- )
- );
- }
- /**
- * Return an array of IDs for for a specific coupon code.
- * Can return multiple to check for existence.
- *
- * @since 3.0.0
- * @param string $code Coupon code.
- * @return array Array of IDs.
- */
- public function get_ids_by_code( $code ) {
- global $wpdb;
- 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
- )
- );
- }
- }
|