t = count( $coupon_items ); $num_updated = 0; $num_deleted = 0; foreach ( $coupon_items as $coupon_item ) { $coupon_id = self::get_coupon_id( $coupon_item ); unset( $existing_items[ $coupon_id ] ); if ( ! $coupon_id ) { // Insert a unique, but obviously invalid ID for this deleted coupon. $num_deleted++; $coupon_id = -1 * $num_deleted; } $result = $wpdb->replace( self::get_db_table_name(), array( 'order_id' => $order_id, 'coupon_id' => $coupon_id, 'discount_amount' => $coupon_item->get_discount(), 'date_created' => $order->get_date_created( 'edit' )->date( TimeInterval::$sql_datetime_format ), ), array( '%d', '%d', '%f', '%s', ) ); /** * Fires when coupon's reports are updated. * * @param int $coupon_id Coupon ID. * @param int $order_id Order ID. */ do_action( 'woocommerce_analytics_update_coupon', $coupon_id, $order_id ); // Sum the rows affected. Using REPLACE can affect 2 rows if the row already exists. $num_updated += 2 === intval( $result ) ? 1 : intval( $result ); } if ( ! empty( $existing_items ) ) { $existing_items = array_flip( $existing_items ); $format = array_fill( 0, count( $existing_items ), '%d' ); $format = implode( ',', $format ); array_unshift( $existing_items, $order_id ); $wpdb->query( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "DELETE FROM {$table_name} WHERE order_id = %d AND coupon_id in ({$format})", $existing_items ) ); } return ( $coupon_items_count === $num_updated ); } /** * Clean coupons data when an order is deleted. * * @param int $order_id Order ID. */ public static function sync_on_order_delete( $order_id ) { global $wpdb; $wpdb->delete( self::get_db_table_name(), array( 'order_id' => $order_id ) ); /** * Fires when coupon's reports are removed from database. * * @param int $coupon_id Coupon ID. * @param int $order_id Order ID. */ do_action( 'woocommerce_analytics_delete_coupon', 0, $order_id ); ReportsCache::invalidate(); } /** * Gets coupons based on the provided arguments. * * @todo Upon core merge, including this in core's `class-wc-coupon-data-store-cpt.php` might make more sense. * @param array $args Array of args to filter the query by. Supports `include`. * @return array Array of results. */ public function get_coupons( $args ) { global $wpdb; $query = "SELECT ID, post_title FROM {$wpdb->posts} WHERE post_type='shop_coupon'"; $included_coupons = $this->get_included_coupons( $args, 'include' ); if ( ! empty( $included_coupons ) ) { $query .= " AND ID IN ({$included_coupons})"; } return $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared } /** * Initialize query objects. */ protected function initialize_queries() { $this->clear_all_clauses(); $this->subquery = new SqlQuery( $this->context . '_subquery' ); $this->subquery->add_sql_clause( 'from', self::get_db_table_name() ); $this->subquery->add_sql_clause( 'group_by', 'coupon_id' ); } }