EDD_Discount
EDD_Discount Class
Description Description
Source Source
File: includes/class-edd-discount.php
class EDD_Discount {
/**
* Discount ID.
*
* @since 2.7
* @access protected
* @var int
*/
protected $ID = 0;
/**
* Discount Name.
*
* @since 2.7
* @access protected
* @var string
*/
protected $name = null;
/**
* Discount Code.
*
* @since 2.7
* @access protected
* @var string
*/
protected $code = null;
/**
* Discount Status (Active or Inactive).
*
* @since 2.7
* @access protected
* @var string
*/
protected $status = null;
/**
* Discount Type (Percentage or Flat Amount).
*
* @since 2.7
* @access protected
* @var string
*/
protected $type = null;
/**
* Discount Amount.
*
* @since 2.7
* @access protected
* @var mixed float|int
*/
protected $amount = null;
/**
* Download Requirements.
*
* @since 2.7
* @access protected
* @var array
*/
protected $product_reqs = null;
/**
* Excluded Downloads.
*
* @since 2.7
* @access protected
* @var array
*/
protected $excluded_products = null;
/**
* Start Date.
*
* @since 2.7
* @access protected
* @var string
*/
protected $start = null;
/**
* End Date.
*
* @since 2.7
* @access protected
* @var string
*/
protected $expiration = null;
/**
* Uses.
*
* @since 2.7
* @access protected
* @var int
*/
protected $uses = null;
/**
* Maximum Uses.
*
* @since 2.7
* @access protected
* @var int
*/
protected $max_uses = null;
/**
* Minimum Amount.
*
* @since 2.7
* @access protected
* @var mixed int|float
*/
protected $min_price = null;
/**
* Is Single Use?
*
* @since 2.7
* @access protected
* @var bool
*/
protected $is_single_use = null;
/**
* Is Not Global?
*
* @since 2.7
* @access protected
* @var bool
*/
protected $is_not_global = null;
/**
* Product Condition
*
* @since 2.7
* @access protected
* @var string
*/
protected $product_condition = null;
/**
* Array of items that have changed since the last save() was run
* This is for internal use, to allow fewer update_payment_meta calls to be run
*
* @since 2.7
* @access private
* @var array
*/
private $pending;
/**
* Declare the default properties in WP_Post as we can't extend it.
*
* @since 2.7
* @access protected
* @var mixed
*/
protected $post_author = 0;
protected $post_date = '0000-00-00 00:00:00';
protected $post_date_gmt = '0000-00-00 00:00:00';
protected $post_content = '';
protected $post_title = '';
protected $post_excerpt = '';
protected $post_status = 'publish';
protected $comment_status = 'open';
protected $ping_status = 'open';
protected $post_password = '';
protected $post_name = '';
protected $to_ping = '';
protected $pinged = '';
protected $post_modified = '0000-00-00 00:00:00';
protected $post_modified_gmt = '0000-00-00 00:00:00';
protected $post_content_filtered = '';
protected $post_parent = 0;
protected $guid = '';
protected $menu_order = 0;
protected $post_mime_type = '';
protected $comment_count = 0;
protected $filter;
protected $post_type;
/**
* Constructor.
*
* @since 2.7
* @access protected
*
* @param mixed int|string $_id_or_code_or_name Discount id/code/name.
* @param bool $by_code Whether identifier passed was a discount code.
* @param bool $by_name Whether identifier passed was a discount name.
*/
public function __construct( $_id_or_code_or_name = false, $by_code = false, $by_name = false ) {
if ( empty( $_id_or_code_or_name ) ) {
return false;
}
if ( $by_code ) {
$discount = $this->find_by_code( $_id_or_code_or_name );
} elseif ( $by_name ) {
$discount = $this->find_by_name( $_id_or_code_or_name );
} else {
$_id_or_code_or_name = absint( $_id_or_code_or_name );
$discount = WP_Post::get_instance( $_id_or_code_or_name );
}
if ( $discount ) {
$this->setup_discount( $discount );
} else {
return false;
}
}
/**
* Magic __get method to dispatch a call to retrieve a protected property.
*
* @since 2.7
*
* @param mixed $key
* @return mixed
*/
public function __get( $key ) {
$key = sanitize_key( $key );
if ( method_exists( $this, 'get_' . $key ) ) {
return call_user_func( array( $this, 'get_' . $key ) );
} else if ( property_exists( $this, $key ) ) {
return $this->{$key};
} else {
return new WP_Error( 'edd-discount-invalid-property', sprintf( __( 'Can\'t get property %s', 'easy-digital-downloads' ), $key ) );
}
}
/**
* Magic __set method to dispatch a call to update a protected property.
*
* @since 2.7
*
* @see set()
*
* @param string $key Property name.
* @param mixed $value Property value.
*/
public function __set( $key, $value ) {
$key = sanitize_key( $key );
// Only real properties can be saved.
$keys = array_keys( get_class_vars( get_called_class() ) );
if ( ! in_array( $key, $keys ) ) {
return false;
}
$this->pending[ $key ] = $value;
// Dispatch to setter method if value needs to be sanitized
if ( method_exists( $this, 'set_' . $key ) ) {
return call_user_func( array( $this, 'set_' . $key ), $key, $value );
} else {
$this->{$key} = $value;
}
}
/**
* Magic __isset method to allow empty checks on protected elements
*
* @since 2.7
*
* @param string $key The attribute to get
* @return boolean If the item is set or not
*/
public function __isset( $key ) {
if ( property_exists( $this, $key ) ) {
return false === empty( $this->{$key} );
} else {
return null;
}
}
/**
* Converts the instance of the EDD_Discount object into an array for special cases.
*
* @since 2.7
*
* @return array EDD_Discount object as an array.
*/
public function array_convert() {
return get_object_vars( $this );
}
/**
* Find a discount in the database with the code supplied.
*
* @since 2.7
* @access private
*
* @param string $code Discount code.
* @return WP_Post Post instance of the discount.
*/
private function find_by_code( $code = '' ) {
if ( empty( $code ) || ! is_string( $code ) ) {
return false;
}
$discounts = edd_get_discounts(
array(
'meta_key' => '_edd_discount_code',
'meta_value' => $code,
'posts_per_page' => 1,
'post_status' => 'any',
'fields' => 'ids'
)
);
if ( ! is_array( $discounts ) || array() === $discounts ) {
return false;
}
if ( $discounts ) {
$discount = $discounts[0];
}
return WP_Post::get_instance( $discount );
}
/**
* Find a discount in the database with the name supplied.
*
* @since 2.7
* @access private
*
* @param string $name Discount name.
* @return WP_Post Post instance of the discount.
*/
private function find_by_name( $name = '' ) {
if ( empty( $name ) || ! is_string( $name ) ) {
return false;
}
$discounts = edd_get_discounts(
array(
'post_type' => 'edd_discount',
'name' => $name,
'posts_per_page' => 1,
'post_status' => 'any',
'fields' => 'ids'
)
);
if ( ! is_array( $discounts ) || array() === $discounts ) {
return false;
}
if ( $discounts ) {
$discount = $discounts[0];
}
return WP_Post::get_instance( $discount );
}
/**
* Setup object vars with discount WP_Post object.
*
* @since 2.7
* @access private
*
* @param WP_Post $discount WP_Post instance of the discount.
* @return bool Object var initialisation successful or not.
*/
private function setup_discount( $discount = null ) {
$this->pending = array();
if ( null == $discount ) {
return false;
}
if ( ! is_object( $discount ) ) {
return false;
}
if ( is_wp_error( $discount ) ) {
return false;
}
if ( ! $discount instanceof WP_Post ) {
return false;
}
if ( 'edd_discount' !== $discount->post_type ) {
return false;
}
/**
* Fires before the instance of the EDD_Discount object is set up.
*
* @since 2.7
*
* @param object EDD_Discount EDD_Discount instance of the discount object.
* @param object WP_Post $discount WP_Post instance of the discount object.
*/
do_action( 'edd_pre_setup_discount', $this, $discount );
/**
* Setup all object variables
*/
$this->ID = absint( $discount->ID );
$this->name = $this->setup_name();
$this->code = $this->setup_code();
$this->status = $this->setup_status();
$this->type = $this->setup_type();
$this->amount = $this->setup_amount();
$this->product_reqs = $this->setup_product_requirements();
$this->excluded_products = $this->setup_excluded_products();
$this->start = $this->setup_start();
$this->expiration = $this->setup_expiration();
$this->uses = $this->setup_uses();
$this->max_uses = $this->setup_max_uses();
$this->min_price = $this->setup_min_price();
$this->is_single_use = $this->setup_is_single_use();
$this->is_not_global = $this->setup_is_not_global();
$this->product_condition = $this->setup_product_condition();
/**
* Setup discount object vars with WP_Post vars
*/
foreach ( get_object_vars( $discount ) as $key => $value ) {
$this->{$key} = $value;
}
/**
* Fires after the instance of the EDD_Discount object is set up. Allows extensions to add items to this object via hook.
*
* @since 2.7
*
* @param object EDD_Discount EDD_Discount instance of the discount object.
* @param object WP_Post $discount WP_Post instance of the discount object.
*/
do_action( 'edd_setup_discount', $this, $discount );
return true;
}
/**
* Setup Functions
*/
/**
* Setup the name of the discount.
*
* @since 2.7
* @access private
*
* @return string Name of the discount.
*/
private function setup_name() {
$title = get_the_title( $this->ID );
return $title;
}
/**
* Setup the discount code.
*
* @since 2.7
* @access private
*
* @return string Discount code.
*/
private function setup_code() {
$code = $this->get_meta( 'code', true );
return $code;
}
/**
* Setup the discount status.
*
* @since 2.7
* @access private
*
* @return string Discount status.
*/
private function setup_status() {
$status = $this->get_meta( 'status', true );
return $status;
}
/**
* Setup the discount type.
*
* @since 2.7
* @access private
*
* @return string Discount type.
*/
private function setup_type() {
$type = $this->get_meta( 'type', true );
return $type;
}
/**
* Setup the discount amount.
*
* @since 2.7
* @access private
*
* @return string Discount amount.
*/
private function setup_amount() {
$amount = $this->get_meta( 'amount', true );
return $amount;
}
/**
* Setup the product requirements.
*
* @since 2.7
* @access private
*
* @return array Download requirements.
*/
private function setup_product_requirements() {
$requirements = $this->get_meta( 'product_reqs', true );
return (array) $requirements;
}
/**
* Setup the excluded products.
*
* @since 2.7
* @access private
*
* @return array Excluded products.
*/
private function setup_excluded_products() {
$excluded = $this->get_meta( 'excluded_products', true );
return (array) $excluded;
}
/**
* Setup the start date.
*
* @since 2.7
* @access private
*
* @return string Discount start date.
*/
private function setup_start() {
$start = $this->get_meta( 'start', true );
return $start;
}
/**
* Setup the expiration date.
*
* @since 2.7
* @access private
*
* @return array Discount expiration date.
*/
private function setup_expiration() {
$expration = $this->get_meta( 'expiration', true );
return $expration;
}
/**
* Setup the uses.
*
* @since 2.7
* @access private
*
* @return int Discount uses.
*/
private function setup_uses() {
$uses = $this->get_meta( 'uses', true );
return $uses;
}
/**
* Setup the max uses.
*
* @since 2.7
* @access private
*
* @return int Maximum uses.
*/
private function setup_max_uses() {
$max_uses = $this->get_meta( 'max_uses', true );
return $max_uses;
}
/**
* Setup the min price.
*
* @since 2.7
* @access private
*
* @return int Minimum price.
*/
private function setup_min_price() {
$max_uses = $this->get_meta( 'min_price', true );
return $max_uses;
}
/**
* Setup if the discount is single use or not.
*
* @since 2.7
* @access private
*
* @return bool Is single use.
*/
private function setup_is_single_use() {
$is_single_use = $this->get_meta( 'is_single_use', true );
return (bool) $is_single_use;
}
/**
* Setup if the discount is not global.
*
* @since 2.7
* @access private
*
* @return bool Is not global.
*/
private function setup_is_not_global() {
$is_not_global = $this->get_meta( 'is_not_global', true );
return (bool) $is_not_global;
}
/**
* Setup if the discount is not global.
*
* @since 2.7
* @access private
*
* @return bool Is not global.
*/
private function setup_product_condition() {
$condition = $this->get_meta( 'product_condition', true );
return $condition;
}
/**
* Helper method to retrieve meta data associated with the discount.
*
* @since 2.7
*
* @param string $key Meta key.
* @param bool $single Return single item or array.
*/
public function get_meta( $key = '', $single = true ) {
$meta = get_post_meta( $this->ID, '_edd_discount_' . $key, $single );
return $meta;
}
/**
* Helper method to update post meta associated with the discount.
*
* @since 2.7
*
* @param string $key Meta key.
* @param string $value Meta value.
* @param string $prev_value Previous meta value.
* @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
*/
public function update_meta( $key = '', $value = '', $prev_value = '' ) {
if ( empty( $key ) || '' == $key ) {
return false;
}
$key = '_edd_discount_' . $key;
$value = apply_filters( 'edd_update_discount_meta_' . $key, $value, $this->ID );
return update_post_meta( $this->ID, $key, $value, $prev_value );
}
/**
* Retrieve the ID of the WP_Post object.
*
* @since 2.7
*
* @return int Discount ID.
*/
public function get_ID() {
return $this->ID;
}
/**
* Retrieve the name of the discount.
*
* @since 2.7
*
* @return string Name of the discount.
*/
public function get_name() {
return $this->name;
}
/**
* Retrieve the code used to apply the discount.
*
* @since 2.7
*
* @return string Discount code.
*/
public function get_code() {
/**
* Filters the discount code.
*
* @since 2.7
*
* @param string $code Discount code.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_get_discount_code', $this->code, $this->ID );
}
/**
* Retrieve the status of the discount
*
* @since 2.7
*
* @return string Discount code status (active/inactive).
*/
public function get_status() {
/**
* Filters the discount status.
*
* @since 2.7
*
* @param string $code Discount status (active or inactive).
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_get_discount_status', $this->status, $this->ID );
}
/**
* Retrieves the status label of the discount.
*
* @since 2.9
*
* @return string Status label for the current discount.
*/
public function get_status_label() {
switch( $this->status ) {
case 'expired' :
$label = __( 'Expired', 'easy-digital-downloads' );
break;
case 'inactive' :
$label = __( 'Inactive', 'easy-digital-downloads' );
break;
case 'active' :
default :
$label = __( 'Active', 'easy-digital-downloads' );
break;
}
/**
* Filters the discount status.
*
* @since 2.9
*
* @param string $label Discount status label.
* @param string $status Discount status (active or inactive).
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_get_discount_status_label', $label, $this->status, $this->ID );
}
/**
* Retrieve the type of discount.
*
* @since 2.7
*
* @return string Discount type (percent or flat amount).
*/
public function get_type() {
/**
* Filters the discount type.
*
* @since 2.7
*
* @param string $code Discount type (percent or flat amount).
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_get_discount_type', $this->type, $this->ID );
}
/**
* Retrieve the discount amount.
*
* @since 2.7
*
* @return mixed float Discount amount.
*/
public function get_amount() {
/**
* Filters the discount amount.
*
* @since 2.7
*
* @param float $amount Discount amount.
* @param int $ID Discount ID.
*/
return (float) apply_filters( 'edd_get_discount_amount', $this->amount, $this->ID );
}
/**
* Retrieve the discount requirements for the discount to be satisfied.
*
* @since 2.7
*
* @return array IDs of required downloads.
*/
public function get_product_reqs() {
if ( empty( $this->product_reqs ) || ! is_array( $this->product_reqs ) ) {
$this->product_reqs = array();
}
/**
* Filters the download requirements.
*
* @since 2.7
*
* @param array $product_reqs IDs of required products.
* @param int $ID Discount ID.
*/
return (array) apply_filters( 'edd_get_discount_product_reqs', $this->product_reqs, $this->ID );
}
/**
* Retrieve the downloads that are excluded from having this discount code applied.
*
* @since 2.7
*
* @return array IDs of excluded downloads.
*/
public function get_excluded_products() {
if ( empty( $this->excluded_products ) || ! is_array( $this->excluded_products ) ) {
$this->excluded_products = array();
}
/**
* Filters the excluded downloads.
*
* @since 2.7
*
* @param array $excluded_products IDs of excluded products.
* @param int $ID Discount ID.
*/
return (array) apply_filters( 'edd_get_discount_excluded_products', $this->excluded_products, $this->ID );
}
/**
* Retrieve the start date.
*
* @since 2.7
*
* @return string Start date.
*/
public function get_start() {
/**
* Filters the start date.
*
* @since 2.7
*
* @param string $start Discount start date.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_get_discount_start', $this->start, $this->ID );
}
/**
* Retrieve the end date.
*
* @since 2.7
*
* @return string End date.
*/
public function get_expiration() {
/**
* Filters the end date.
*
* @since 2.7
*
* @param string $expiration Discount expiration date.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_get_discount_expiration', $this->expiration, $this->ID );
}
/**
* Retrieve the uses for the discount code.
*
* @since 2.7
*
* @return int Uses.
*/
public function get_uses() {
/**
* Filters the maximum uses.
*
* @since 2.7
*
* @param int $max_uses Maximum uses.
* @param int $ID Discount ID.
*/
return (int) apply_filters( 'edd_get_discount_uses', $this->uses, $this->ID );
}
/**
* Retrieve the maximum uses for the discount code.
*
* @since 2.7
*
* @return int Maximum uses.
*/
public function get_max_uses() {
/**
* Filters the maximum uses.
*
* @since 2.7
*
* @param int $max_uses Maximum uses.
* @param int $ID Discount ID.
*/
return (int) apply_filters( 'edd_get_discount_max_uses', $this->max_uses, $this->ID );
}
/**
* Retrieve the minimum spend required for the discount to be satisfied.
*
* @since 2.7
*
* @return mixed float Minimum spend.
*/
public function get_min_price() {
/**
* Filters the minimum price.
*
* @since 2.7
*
* @param float $min_price Minimum price.
* @param int $ID Discount ID.
*/
return (float) apply_filters( 'edd_get_discount_min_price', $this->min_price, $this->ID );
}
/**
* Retrieve the usage limit per limit (if the discount can only be used once per customer).
*
* @since 2.7
*
* @return bool Once use per customer?
*/
public function get_is_single_use() {
/**
* Filters the single use meta value.
*
* @since 2.7
*
* @param bool $is_single_use Is the discount only allowed to be used once per customer.
* @param int $ID Discount ID.
*/
return (bool) apply_filters( 'edd_is_discount_single_use', $this->is_single_use, $this->ID );
}
/**
* Retrieve the property determining if a discount is not global.
*
* @since 2.7
*
* @return bool Whether or not the discount code is global.
*/
public function get_is_not_global() {
/**
* Filters if the discount is global or not.
*
* @since 2.7
*
* @param bool $is_not_global Is the discount global or not.
* @param int $ID Discount ID.
*/
return (bool) apply_filters( 'edd_discount_is_not_global', $this->is_not_global, $this->ID );
}
/**
* Retrieve the product condition.
*
* @since 2.7
*
* @return string Product condition
*/
public function get_product_condition() {
/**
* Filters the product condition.
*
* @since 2.7
*
* @param string $product_condition Product condition.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_discount_product_condition', $this->product_condition, $this->ID );
}
/**
* Check if a discount exists.
*
* @since 2.7
*
* @return bool Discount exists.
*/
public function exists() {
if ( ! $this->ID > 0 ) {
return false;
}
return true;
}
/**
* Build the base of the discount.
*
* @since 2.7
* @access private
*
* @return int|bool Discount ID or false on error.
*/
private function insert_discount() {
$discount_data = array(
'code' => isset( $this->code ) ? $this->code : '',
'name' => isset( $this->name ) ? $this->name : '',
'status' => isset( $this->status ) ? $this->status : 'active',
'uses' => isset( $this->uses ) ? $this->uses : '',
'max_uses' => isset( $this->max_uses ) ? $this->max_uses : '',
'amount' => isset( $this->amount ) ? $this->amount : '',
'start' => isset( $this->start ) ? $this->start : '',
'expiration' => isset( $this->expiration ) ? $this->expiration : '',
'type' => isset( $this->type ) ? $this->type : '',
'min_price' => isset( $this->min_price ) ? $this->min_price : '',
'product_reqs' => isset( $this->product_reqs ) ? $this->product_reqs : array(),
'product_condition' => isset( $this->product_condition ) ? $this->product_condition : '',
'excluded_products' => isset( $this->excluded_products ) ? $this->excluded_products : array(),
'is_not_global' => isset( $this->is_not_global ) ? $this->is_not_global : false,
'is_single_use' => isset( $this->is_single_use ) ? $this->is_single_use : false,
);
$start_timestamp = strtotime( $discount_data['start'] );
if ( ! empty( $discount_data['start'] ) ) {
$discount_data['start'] = date( 'm/d/Y H:i:s', $start_timestamp );
}
if ( ! empty( $discount_data['expiration'] ) ) {
$discount_data['expiration'] = date( 'm/d/Y H:i:s', strtotime( date( 'm/d/Y', strtotime( $discount_data['expiration'] ) ) . ' 23:59:59' ) );
$end_timestamp = strtotime( $discount_data['expiration'] );
if ( ! empty( $discount_data['start'] ) && $start_timestamp > $end_timestamp ) {
// Set the expiration date to the start date if start is later than expiration
$discount_data['expiration'] = $discount_data['start'];
}
}
if ( ! empty( $discount_data['excluded_products'] ) ) {
foreach ( $discount_data['excluded_products'] as $key => $product ) {
if ( 0 === intval( $product ) ) {
unset( $discount_data['excluded_products'][ $key ] );
}
}
}
$args = apply_filters( 'edd_insert_discount_args', array(
'post_title' => $this->name,
'post_status' => $discount_data['status'],
'post_type' => 'edd_discount',
'post_date' => ! empty( $this->date ) ? $this->date : null,
'post_date_gmt' => ! empty( $this->date ) ? get_gmt_from_date( $this->date ) : null
), $discount_data );
// Create a blank edd_discount post
$discount_id = wp_insert_post( $args );
if ( ! empty( $discount_id ) ) {
$this->ID = $discount_id;
foreach ( $discount_data as $key => $value ) {
if( ! empty( $value ) ) {
$this->update_meta( $key, $value );
}
}
}
return $this->ID;
}
/**
* Once object variables has been set, an update is needed to persist them to the database.
*
* @since 2.7
*
* @return bool True if the save was successful, false if it failed or wasn't needed.
*/
public function save() {
$saved = false;
if ( empty( $this->ID ) ) {
$discount_id = $this->insert_discount();
if ( false === $discount_id ) {
$saved = false;
} else {
$this->ID = $discount_id;
}
}
/**
* Save all the object variables that have been updated to the databse.
*/
if ( ! empty( $this->pending ) ) {
foreach ( $this->pending as $key => $value ) {
$this->update_meta( $key, $value );
if ( 'status' == $key ) {
$this->update_status( $value );
}
if ( 'name' == $key ) {
wp_update_post( array(
'ID' => $this->ID,
'post_title' => $value
) );
}
}
$saved = true;
}
if ( true == $saved ) {
global $edd_get_discounts_cache;
$edd_get_discounts_cache = array();
$this->setup_discount( WP_Post::get_instance( $this->ID ) );
/**
* Fires after each meta update allowing developers to hook their own items saved in $pending.
*
* @since 2.7
*
* @param object Instance of EDD_Discount object.
* @param string $key Meta key.
*/
do_action( 'edd_discount_save', $this->ID, $this );
}
return $saved;
}
/**
* Create a new discount. If the discount already exists in the database, update it.
*
* @since 2.7
*
* @param array $args Discount details.
* @return mixed bool|int false if data isn't passed and class not instantiated for creation, or post ID for the new discount.
*/
public function add( $args ) {
// If no code is provided, return early with false
if ( empty( $args['code'] ) ) {
return false;
}
$meta = $this->build_meta( $args );
if ( ! empty( $this->ID ) && $this->exists() ) {
return $this->update( $args );
} else {
/**
* Add a new discount to the database.
*/
/**
* Filters the metadata before being inserted into the database.
*
* @since 2.7
*
* @param array $meta Discount meta.
* @param int $ID Discount ID.
*/
$meta = apply_filters( 'edd_insert_discount', $meta );
/**
* Fires before the discount has been added to the database.
*
* @since 2.7
*
* @param array $meta Discount meta.
*/
do_action( 'edd_pre_insert_discount', $meta );
$this->ID = wp_insert_post( array(
'post_type' => 'edd_discount',
'post_title' => $meta['name'],
'post_status' => 'active'
) );
foreach ( $meta as $key => $value ) {
$this->update_meta( $key, $value );
}
/**
* Fires after the discount code is inserted.
*
* @param array $meta {
* The discount details.
*
* @type string $code The discount code.
* @type string $name The name of the discount.
* @type string $status The discount status. Defaults to active.
* @type int $uses The current number of uses.
* @type int $max_uses The max number of uses.
* @type string $start The start date.
* @type int $min_price The minimum price required to use the discount code.
* @type array $product_reqs The product IDs required to use the discount code.
* @type string $product_condition The conditions in which a product(s) must meet to use the discount code.
* @type array $excluded_products Product IDs excluded from this discount code.
* @type bool $is_not_global If the discount code is not globally applied to all products. Defaults to false.
* @type bool $is_single_use If the code cannot be used more than once per customer. Defaults to false.
* }
* @param int $ID The ID of the discount that was inserted.
*/
do_action( 'edd_post_insert_discount', $meta, $this->ID );
$this->setup_discount( WP_Post::get_instance( $this->ID ) );
// Discount code created
return $this->ID;
}
}
/**
* Update an existing discount in the database.
*
* @since 2.7
*
* @param array $args Discount details.
* @return mixed bool|int false if data isn't passed and class not instantiated for creation, or post ID for the new discount.
*/
public function update( $args ) {
$meta = $this->build_meta( $args );
/**
* Filter the data being updated
*
* @since 2.7
*
* @param array $meta Discount meta.
* @param int $ID Discount ID.
*/
$meta = apply_filters( 'edd_update_discount', $meta, $this->ID );
/**
* Fires before the discount has been updated in the database.
*
* @since 2.7
*
* @param array $meta Discount meta.
* @param int $ID Discount ID.
*/
do_action( 'edd_pre_update_discount', $meta, $this->ID );
wp_update_post( array(
'ID' => $this->ID,
'post_title' => $meta['name'],
'post_status' => $meta['status']
) );
foreach ( $meta as $key => $value ) {
$this->update_meta( $key, $value );
}
$this->setup_discount( WP_Post::get_instance( $this->ID ) );
/**
* Fires after the discount has been updated in the database.
*
* @since 2.7
*
* @param array $meta Discount meta.
* @param int $ID Discount ID.
*/
do_action( 'edd_post_update_discount', $meta, $this->ID );
return $this->ID;
}
/**
* Build Discount Meta Array.
*
* @since 2.7
* @access private
*
* @param array $args Discount meta.
* @return array Filtered and sanitized discount args.
*/
private function build_meta( $args = array() ) {
if ( ! is_array( $args ) || array() === $args ) {
return false;
}
$meta = array(
'code' => isset( $args['code'] ) ? $args['code'] : '',
'name' => isset( $args['name'] ) ? $args['name'] : '',
'status' => isset( $args['status'] ) ? $args['status'] : 'active',
'uses' => isset( $args['uses'] ) ? $args['uses'] : '',
'max_uses' => isset( $args['max'] ) ? $args['max'] : '',
'amount' => isset( $args['amount'] ) ? $args['amount'] : '',
'start' => isset( $args['start'] ) ? $args['start'] : '',
'expiration' => isset( $args['expiration'] ) ? $args['expiration'] : '',
'type' => isset( $args['type'] ) ? $args['type'] : '',
'min_price' => isset( $args['min_price'] ) ? $args['min_price'] : '',
'product_reqs' => isset( $args['products'] ) ? $args['products'] : array(),
'product_condition' => isset( $args['product_condition'] )? $args['product_condition'] : '',
'excluded_products' => isset( $args['excluded-products'] )? $args['excluded-products'] : array(),
'is_not_global' => isset( $args['not_global'] ) ? $args['not_global'] : false,
'is_single_use' => isset( $args['use_once'] ) ? $args['use_once'] : false,
);
$start_timestamp = strtotime( $meta['start'] );
if ( ! empty( $meta['start'] ) ) {
$meta['start'] = date( 'm/d/Y H:i:s', $start_timestamp );
}
if ( ! empty( $meta['expiration'] ) ) {
$meta['expiration'] = date( 'm/d/Y H:i:s', strtotime( date( 'm/d/Y', strtotime( $meta['expiration'] ) ) . ' 23:59:59' ) );
$end_timestamp = strtotime( $meta['expiration'] );
if ( ! empty( $meta['start'] ) && $start_timestamp > $end_timestamp ) {
// Set the expiration date to the start date if start is later than expiration
$meta['expiration'] = $meta['start'];
}
}
if ( ! empty( $meta['excluded_products'] ) ) {
foreach ( $meta['excluded_products'] as $key => $product ) {
if ( 0 === intval( $product ) ) {
unset( $meta['excluded_products'][ $key ] );
}
}
}
return $meta;
}
/**
* Update the status of the discount.
*
* @since 2.7
*
* @param string $new_status New status (default: active)
* @return bool If the status been updated or not.
*/
public function update_status( $new_status = 'active' ) {
/**
* Fires before the status of the discount is updated.
*
* @since 2.7
*
* @param int $ID Discount ID.
* @param string $new_status New status.
* @param string $post_status Post status.
*/
do_action( 'edd_pre_update_discount_status', $this->ID, $new_status, $this->post_status );
$id = wp_update_post(
array(
'ID' => $this->ID,
'post_status' => $new_status
)
);
/**
* Fires after the status of the discount is updated.
*
* @since 2.7
*
* @param int $ID Discount ID.
* @param string $new_status New status.
* @param string $post_status Post status.
*/
do_action( 'edd_post_update_discount_status', $this->ID, $new_status, $this->post_status );
if ( $id == $this->ID ) {
return true;
}
return false;
}
/**
* Check if the discount has started.
*
* @since 2.7
*
* @param bool $set_error Whether an error message be set in session.
* @return bool Is discount started?
*/
public function is_started( $set_error = true ) {
$return = false;
if ( $this->start ) {
$start_date = strtotime( $this->start );
if ( $start_date < time() ) {
// Discount has pased the start date
$return = true;
} elseif( $set_error ) {
edd_set_error( 'edd-discount-error', _x( 'This discount is invalid.', 'error shown when attempting to use a discount before its start date', 'easy-digital-downloads' ) );
}
} else {
// No start date for this discount, so has to be true
$return = true;
}
/**
* Filters if the discount has started or not.
*
* @since 2.7
*
* @param bool $return Has the discount started or not.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_is_discount_started', $return, $this->ID );
}
/**
* Check if the discount has expired.
*
* @since 2.7
*
* @param bool $update Update the discount to expired if an one is found but has an active status
* @return bool Has the discount expired?
*/
public function is_expired( $update = true ) {
$return = false;
if ( empty( $this->expiration ) ) {
return $return;
}
$expiration = strtotime( $this->expiration );
if ( $expiration < time() ) {
if ( $update ) {
$this->update_status( 'inactive' );
$this->update_meta( 'status', 'expired' );
}
$return = true;
}
/**
* Filters if the discount has expired or not.
*
* @since 2.7
*
* @param bool $return Has the discount expired or not.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_is_discount_expired', $return, $this->ID );
}
/**
* Check if the discount has maxed out.
*
* @since 2.7
*
* @param bool $set_error Whether an error message be set in session.
* @return bool Is discount maxed out?
*/
public function is_maxed_out( $set_error = true ) {
$return = false;
if ( $this->uses >= $this->max_uses && ! empty( $this->max_uses ) ) {
if ( $set_error ) {
edd_set_error( 'edd-discount-error', __( 'This discount has reached its maximum usage.', 'easy-digital-downloads' ) );
}
$return = true;
}
/**
* Filters if the discount is maxed out or not.
*
* @since 2.7
*
* @param bool $return Is the discount maxed out or not.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_is_discount_maxed_out', $return, $this->ID );
}
/**
* Check if the minimum cart amount is satisfied for the discount to hold.
*
* @since 2.7
*
* @param bool $set_error Whether an error message be set in session.
* @return bool Is the minimum cart amount met?
*/
public function is_min_price_met( $set_error = true ) {
$return = false;
$cart_amount = edd_get_cart_discountable_subtotal( $this->ID );
if ( (float) $cart_amount >= (float) $this->min_price ) {
$return = true;
} elseif( $set_error ) {
edd_set_error( 'edd-discount-error', sprintf( __( 'Minimum order of %s not met.', 'easy-digital-downloads' ), edd_currency_filter( edd_format_amount( $this->min_price ) ) ) );
}
/**
* Filters if the minimum cart amount has been met to satisify the discount.
*
* @since 2.7
*
* @param bool $return Is the minimum cart amount met or not.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_is_discount_min_met', $return, $this->ID );
}
/**
* Is the discount single use or not?
*
* @since 2.7
*
* @return bool Is the discount single use or not?
*/
public function is_single_use() {
/**
* Filters if the discount is single use or not.
*
* @since 2.7
*
* @param bool $single_use Is the discount is single use or not.
* @param int $ID Discount ID.
*/
return (bool) apply_filters( 'edd_is_discount_single_use', $this->is_single_use, $this->ID );
}
/**
* Are the product requirements met for the discount to hold.
*
* @since 2.7
*
* @param bool $set_error Whether an error message be set in session.
* @return bool Are required products in the cart?
*/
public function is_product_requirements_met( $set_error = true ) {
$product_reqs = $this->product_reqs;
$excluded_ps = $this->excluded_products;
$cart_items = edd_get_cart_contents();
$cart_ids = $cart_items ? wp_list_pluck( $cart_items, 'id' ) : null;
$return = false;
if ( empty( $product_reqs ) && empty( $excluded_ps ) ) {
$return = true;
}
/**
* Normalize our data for product requirements, exclusions and cart data.
*/
// First absint the items, then sort, and reset the array keys
$product_reqs = array_map( 'absint', $product_reqs );
asort( $product_reqs );
$product_reqs = array_filter( array_values( $product_reqs ) );
$excluded_ps = array_map( 'absint', $excluded_ps );
asort( $excluded_ps );
$excluded_ps = array_filter( array_values( $excluded_ps ) );
$cart_ids = array_map( 'absint', $cart_ids );
asort( $cart_ids );
$cart_ids = array_values( $cart_ids );
// Ensure we have requirements before proceeding
if ( ! $return && ! empty( $product_reqs ) ) {
switch ( $this->product_condition ) {
case 'all' :
// Default back to true
$return = true;
foreach ( $product_reqs as $download_id ) {
if ( empty( $download_id ) ) {
continue;
}
if ( ! edd_item_in_cart( $download_id ) ) {
if ( $set_error ) {
edd_set_error( 'edd-discount-error', __( 'The product requirements for this discount are not met.', 'easy-digital-downloads' ) );
}
$return = false;
break;
}
}
break;
default :
foreach ( $product_reqs as $download_id ) {
if ( empty( $download_id ) ) {
continue;
}
if ( edd_item_in_cart( $download_id ) ) {
$return = true;
break;
}
}
if ( ! $return && $set_error ) {
edd_set_error( 'edd-discount-error', __( 'The product requirements for this discount are not met.', 'easy-digital-downloads' ) );
}
break;
}
} else {
$return = true;
}
if ( ! empty( $excluded_ps ) ) {
if ( count( array_intersect( $cart_ids, $excluded_ps ) ) == count( $cart_ids ) ) {
$return = false;
if ( $set_error ) {
edd_set_error( 'edd-discount-error', __( 'This discount is not valid for the cart contents.', 'easy-digital-downloads' ) );
}
}
}
/**
* Filters whether the product requirements are met for the discount to hold.
*
* @since 2.7
*
* @param bool $return Are the product requirements met or not.
* @param int $ID Discount ID.
* @param string $product_condition Product condition.
*/
return (bool) apply_filters( 'edd_is_discount_products_req_met', $return, $this->ID, $this->product_condition );
}
/**
* Has the discount code been used.
*
* @since 2.7
*
* @param string $user User info.
* @param bool $set_error Whether an error message be set in session.
*/
public function is_used( $user = '', $set_error = true ) {
$return = false;
if ( $this->is_single_use ) {
$payments = array();
if ( EDD()->customers->installed() ) {
$by_user_id = is_email( $user ) ? false : true;
$customer = new EDD_Customer( $user, $by_user_id );
$payments = explode( ',', $customer->payment_ids );
} else {
$user_found = false;
if ( is_email( $user ) ) {
$user_found = true; // All we need is the email
$key = '_edd_payment_user_email';
$value = $user;
} else {
$user_data = get_user_by( 'login', $user );
if ( $user_data ) {
$user_found = true;
$key = '_edd_payment_user_id';
$value = $user_data->ID;
}
}
if ( $user_found ) {
$query_args = array(
'post_type' => 'edd_payment',
'meta_query' => array(
array(
'key' => $key,
'value' => $value,
'compare' => '='
)
),
'fields' => 'ids'
);
$payments = get_posts( $query_args ); // Get all payments with matching email
}
}
if ( $payments ) {
foreach ( $payments as $payment ) {
$payment = new EDD_Payment( $payment );
if ( empty( $payment->discounts ) ) {
continue;
}
if ( in_array( $payment->status, array( 'abandoned', 'failed', 'pending' ) ) ) {
continue;
}
$discounts = explode( ',', $payment->discounts );
if ( is_array( $discounts ) ) {
$discounts = array_map( 'strtoupper', $discounts );
$key = array_search( strtoupper( $this->code ), $discounts );
if ( false !== $key ) {
if ( $set_error ) {
edd_set_error( 'edd-discount-error', __( 'This discount has already been redeemed.', 'easy-digital-downloads' ) );
}
$return = true;
break;
}
}
}
}
}
/**
* Filters if the discount is used or not.
*
* @since 2.7
*
* @param bool $return If the discount is used or not.
* @param int $ID Discount ID.
* @param string $user User info.
*/
return apply_filters( 'edd_is_discount_used', $return, $this->ID, $user );
}
/**
* Checks whether a discount holds at the time of purchase.
*
* @since 2.7
*
* @param string $user User info.
* @param bool $set_error Whether an error message be set in session.
* @return bool Is the discount valid or not?
*/
public function is_valid( $user = '', $set_error = true ) {
$return = false;
$user = trim( $user );
if ( edd_get_cart_contents() && $this->ID ) {
if (
$this->is_active( true, $set_error ) &&
$this->is_started( $set_error ) &&
! $this->is_maxed_out( $set_error ) &&
! $this->is_used( $user, $set_error ) &&
$this->is_product_requirements_met( $set_error ) &&
$this->is_min_price_met( $set_error )
) {
$return = true;
}
} elseif( $set_error ) {
edd_set_error( 'edd-discount-error', _x( 'This discount is invalid.', 'error for when a discount is invalid based on its configuration' , 'easy-digital-downloads' ) );
}
/**
* Filters whether the discount is valid or not.
*
* @since 2.7
*
* @param bool $return If the discount is used or not.
* @param int $ID Discount ID.
* @param string $code Discount code.
* @param string $user User info.
*/
return apply_filters( 'edd_is_discount_valid', $return, $this->ID, $this->code, $user );
}
/**
* Checks if a discount code is active.
*
* @since 2.7
*
* @param bool $update Update the discount to expired if an one is found but has an active status.
* @param bool $set_error Whether an error message be set in session.
* @return bool If the discount is active or not.
*/
public function is_active( $update = true, $set_error = true ) {
$return = false;
if ( $this->exists() ) {
if ( $this->is_expired( $update ) ) {
if ( defined( 'DOING_AJAX' ) && $set_error ) {
edd_set_error( 'edd-discount-error', __( 'This discount is expired.', 'easy-digital-downloads' ) );
}
} elseif ( $this->post_status == 'active' ) {
$return = true;
} elseif( defined( 'DOING_AJAX' ) && $set_error ) {
edd_set_error( 'edd-discount-error', __( 'This discount is not active.', 'easy-digital-downloads' ) );
}
}
/**
* Filters if the discount is active or not.
*
* @since 2.7
*
* @param bool $return Is the discount active or not.
* @param int $ID Discount ID.
*/
return apply_filters( 'edd_is_discount_active', $return, $this->ID );
}
/**
* Get Discounted Amount.
*
* @since 2.7
*
* @param string|int $base_price Price before discount.
* @return float $discounted_price Amount after discount.
*/
public function get_discounted_amount( $base_price ) {
// Start off setting the amount as the base price.
$amount = $base_price;
if ( 'flat' == $this->type ) {
$amount = $base_price - $this->amount;
if ( $amount < 0 ) {
$amount = 0;
}
} else {
// Percentage discount
$amount = $base_price - ( $base_price * ( $this->amount / 100 ) );
}
/**
* Filter the discounted amount calculated.
*
* @since 2.7
*
* @param float $amount Calculated discounted amount.
*/
return apply_filters( 'edd_discounted_amount', $amount );
}
/**
* Increment the usage of the discount.
*
* @since 2.7
*
* @return int New discount usage.
*/
public function increase_usage() {
if ( $this->uses ) {
$this->uses++;
} else {
$this->uses = 1;
}
$this->update_meta( 'uses', $this->uses );
if ( $this->max_uses == $this->uses ) {
$this->update_status( 'inactive' );
$this->update_meta( 'status', 'inactive' );
}
/**
* Fires after the usage count has been increased.
*
* @since 2.7
*
* @param int $uses Discount usage.
* @param int $ID Discount ID.
* @param string $code Discount code.
*/
do_action( 'edd_discount_increase_use_count', $this->uses, $this->ID, $this->code );
return $this->uses;
}
/**
* Decrement the usage of the discount.
*
* @since 2.7
*
* @return int New discount usage.
*/
public function decrease_usage() {
if ( $this->uses ) {
$this->uses--;
}
if ( $this->uses < 0 ) {
$uses = 0;
}
$this->update_meta( 'uses', $this->uses );
if ( $this->max_uses > $this->uses ) {
$this->update_status( 'active' );
$this->update_meta( 'status', 'active' );
}
/**
* Fires after the usage count has been decreased.
*
* @since 2.7
*
* @param int $uses Discount usage.
* @param int $ID Discount ID.
* @param string $code Discount code.
*/
do_action( 'edd_discount_decrease_use_count', $this->uses, $this->ID, $this->code );
return $this->uses;
}
/**
* Edit Discount Link.
*
* @since 2.7
*
* @return string Link to the `Edit Discount` page.
*/
public function edit_url() {
return esc_url( add_query_arg( array( 'edd-action' => 'edit_discount', 'discount' => $this->ID ), admin_url( 'edit.php?post_type=download&page=edd-discounts' ) ) );
}
}
Changelog Changelog
| Version | Description |
|---|---|
| 2.7 | Introduced. |
Methods Methods
- __construct — Constructor.
- __get — Magic __get method to dispatch a call to retrieve a protected property.
- __isset — Magic __isset method to allow empty checks on protected elements
- __set — Magic __set method to dispatch a call to update a protected property.
- add — Create a new discount. If the discount already exists in the database, update it.
- array_convert — Converts the instance of the EDD_Discount object into an array for special cases.
- build_meta — Build Discount Meta Array.
- decrease_usage — Decrement the usage of the discount.
- edit_url — Edit Discount Link.
- exists — Check if a discount exists.
- find_by_code — Find a discount in the database with the code supplied.
- find_by_name — Find a discount in the database with the name supplied.
- get_amount — Retrieve the discount amount.
- get_code — Retrieve the code used to apply the discount.
- get_discounted_amount — Get Discounted Amount.
- get_excluded_products — Retrieve the downloads that are excluded from having this discount code applied.
- get_expiration — Retrieve the end date.
- get_ID — Retrieve the ID of the WP_Post object.
- get_is_not_global — Retrieve the property determining if a discount is not global.
- get_is_single_use — Retrieve the usage limit per limit (if the discount can only be used once per customer).
- get_max_uses — Retrieve the maximum uses for the discount code.
- get_meta — Helper method to retrieve meta data associated with the discount.
- get_min_price — Retrieve the minimum spend required for the discount to be satisfied.
- get_name — Retrieve the name of the discount.
- get_product_condition — Retrieve the product condition.
- get_product_reqs — Retrieve the discount requirements for the discount to be satisfied.
- get_start — Retrieve the start date.
- get_status — Retrieve the status of the discount
- get_status_label — Retrieves the status label of the discount.
- get_type — Retrieve the type of discount.
- get_uses — Retrieve the uses for the discount code.
- increase_usage — Increment the usage of the discount.
- insert_discount — Build the base of the discount.
- is_active — Checks if a discount code is active.
- is_expired — Check if the discount has expired.
- is_maxed_out — Check if the discount has maxed out.
- is_min_price_met — Check if the minimum cart amount is satisfied for the discount to hold.
- is_product_requirements_met — Are the product requirements met for the discount to hold.
- is_single_use — Is the discount single use or not?
- is_started — Check if the discount has started.
- is_used — Has the discount code been used.
- is_valid — Checks whether a discount holds at the time of purchase.
- save — Once object variables has been set, an update is needed to persist them to the database.
- setup_amount — Setup the discount amount.
- setup_code — Setup the discount code.
- setup_discount — Setup object vars with discount WP_Post object.
- setup_excluded_products — Setup the excluded products.
- setup_expiration — Setup the expiration date.
- setup_is_not_global — Setup if the discount is not global.
- setup_is_single_use — Setup if the discount is single use or not.
- setup_max_uses — Setup the max uses.
- setup_min_price — Setup the min price.
- setup_name — Setup the name of the discount.
- setup_product_condition — Setup if the discount is not global.
- setup_product_requirements — Setup the product requirements.
- setup_start — Setup the start date.
- setup_status — Setup the discount status.
- setup_type — Setup the discount type.
- setup_uses — Setup the uses.
- update — Update an existing discount in the database.
- update_meta — Helper method to update post meta associated with the discount.
- update_status — Update the status of the discount.