stb0

class-wc-name-your-price-cart.php

Sep 10th, 2020
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 15.09 KB | None | 0 0
  1. <?php
  2. /**
  3.  * Interact with WooCommerce cart
  4.  *
  5.  * @class   WC_Name_Your_Price_Cart
  6.  * @package WooCommerce Name Your Price/Classes
  7.  * @since   1.0.0
  8.  * @version  3.1.2
  9.  */
  10.  
  11. if ( ! defined( 'ABSPATH' ) ) {
  12.     exit;
  13. }
  14.  
  15. /**
  16.  * WC_Name_Your_Price_Cart class.
  17.  */
  18. class WC_Name_Your_Price_Cart {
  19.  
  20.     /**
  21.      * The single instance of the class.
  22.      *
  23.      * @var WC_Name_Your_Price_Cart
  24.      *
  25.      * @since 3.0.0
  26.      */
  27.     protected static $instance = null;
  28.  
  29.     /**
  30.      * Main class instance. Ensures only one instance of class is loaded or can be loaded.
  31.      *
  32.      * @static
  33.      * @return WC_Name_Your_Price_Cart
  34.      * @since  3.0.0
  35.      */
  36.     public static function instance() {
  37.         if ( is_null( self::$instance ) ) {
  38.             self::$instance = new self();
  39.         }
  40.         return self::$instance;
  41.     }
  42.  
  43.     /**
  44.      * Cloning is forbidden.
  45.      *
  46.      * @since 3.0.0
  47.      */
  48.     public function __clone() {
  49.         _doing_it_wrong( __FUNCTION__, esc_html__( 'Cloning this object is forbidden.', 'wc_name_your_price' ), '3.0.0' );
  50.     }
  51.  
  52.     /**
  53.      * Unserializing instances of this class is forbidden.
  54.      *
  55.      * @since 3.0.0
  56.      */
  57.     public function __wakeup() {
  58.         _doing_it_wrong( __FUNCTION__, esc_html__( 'Unserializing instances of this class is forbidden.', 'wc_name_your_price' ), '3.0.0' );
  59.     }
  60.  
  61.     /**
  62.      * __construct function.
  63.      *
  64.      * @return void
  65.      */
  66.     public function __construct() {
  67.  
  68.         // Functions for cart actions - ensure they have a priority before addons (10).
  69.         add_filter( 'woocommerce_is_purchasable', array( $this, 'is_purchasable' ), 5, 2 );
  70.         add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_cart_item_data' ), 5, 3 );
  71.         add_filter( 'woocommerce_get_cart_item_from_session', array( $this, 'get_cart_item_from_session' ), 11, 2 );
  72.         add_filter( 'woocommerce_add_cart_item', array( $this, 'set_cart_item' ), 11, 1 );
  73.         add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'validate_add_cart_item' ), 5, 6 );
  74.  
  75.         // Re-validate prices in cart.
  76.         add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_items' ) );
  77.  
  78.     }
  79.  
  80.     /**
  81.      * ---------------------------------------------------------------------------------
  82.      * Cart Filters
  83.      * ---------------------------------------------------------------------------------
  84.      */
  85.  
  86.     /**
  87.      * Override woo's is_purchasable in cases of nyp products.
  88.      *
  89.      * @since 1.0
  90.      *
  91.      * @param   boolean     $is_purchasable
  92.      * @param   WC_Product  $product
  93.      * @return  boolean
  94.      */
  95.     public function is_purchasable( $is_purchasable, $product ) {
  96.         if ( ( $product->is_type( WC_Name_Your_Price_Helpers::get_simple_supported_types() ) && WC_Name_Your_Price_Helpers::is_nyp( $product ) )
  97.             || ( $product->is_type( WC_Name_Your_Price_Helpers::get_variable_supported_types() ) && WC_Name_Your_Price_Helpers::has_nyp( $product ) )
  98.         ) {
  99.             $is_purchasable = true;
  100.         }
  101.         return $is_purchasable;
  102.     }
  103.  
  104.     /**
  105.      * Redirect to the cart when editing a price "in-cart".
  106.      *
  107.      * @since   3.0.0
  108.      * @param  string $url
  109.      * @return string
  110.      */
  111.     public function edit_in_cart_redirect( $url ) {
  112.         return wc_get_cart_url();
  113.     }
  114.  
  115.  
  116.     /**
  117.      * Filter the displayed notice after redirecting to the cart when editing a price "in-cart".
  118.      *
  119.      * @since   3.0.0
  120.      * @param  string $url
  121.      * @return string
  122.      */
  123.     public function edit_in_cart_redirect_message( $message ) {
  124.         return __( 'Cart updated.', 'wc_name_your_price' );
  125.     }
  126.  
  127.     /**
  128.      * Add cart session data.
  129.      *
  130.      * @param array $cart_item_data extra cart item data we want to pass into the item.
  131.      * @param int   $product_id contains the id of the product to add to the cart.
  132.      * @param int   $variation_id ID of the variation being added to the cart.
  133.      * @since 1.0
  134.      */
  135.     public function add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
  136.  
  137.         // phpcs:disable WordPress.Security.NonceVerification
  138.  
  139.         // An NYP item can either be a product or variation.
  140.         $nyp_id = $variation_id ? $variation_id : $product_id;
  141.  
  142.         $suffix  = WC_Name_Your_Price_Helpers::get_suffix( $nyp_id );
  143.         $product = WC_Name_Your_Price_Helpers::maybe_get_product_instance( $nyp_id );
  144.  
  145.         // get_posted_price() removes the thousands separators.
  146.         $posted_price = WC_Name_Your_Price_Helpers::get_posted_price( $product, $suffix );
  147.  
  148.         // Is this an NYP item?
  149.         if ( WC_Name_Your_Price_Helpers::is_nyp( $nyp_id ) && $posted_price ) {
  150.  
  151.             // Updating container in cart?
  152.             if ( isset( $_POST['update-price'] ) && isset( $_POST['_nypnonce'] ) && wp_verify_nonce( sanitize_key( $_POST['_nypnonce'] ), 'nyp-nonce' ) ) {
  153.  
  154.                 $updating_cart_key = wc_clean( wp_unslash( $_POST['update-price'] ) );
  155.  
  156.                 if ( WC()->cart->find_product_in_cart( $updating_cart_key ) ) {
  157.  
  158.                     // Remove.
  159.                     WC()->cart->remove_cart_item( $updating_cart_key );
  160.  
  161.                     // Redirect to cart.
  162.                     add_filter( 'woocommerce_add_to_cart_redirect', array( $this, 'edit_in_cart_redirect' ) );
  163.  
  164.                     // Edit notice.
  165.                     add_filter( 'wc_add_to_cart_message_html', array( $this, 'edit_in_cart_redirect_message' ) );
  166.                 }
  167.             }
  168.  
  169.             // No need to check is_nyp b/c this has already been validated by validate_add_cart_item().
  170.             $cart_item_data['nyp'] = (float) $posted_price;
  171.         }
  172.  
  173.         // Add the subscription billing period (the input name is nyp-period).
  174.         $posted_period = WC_Name_Your_Price_Helpers::get_posted_period( $product, $suffix );
  175.  
  176.         if ( WC_Name_Your_Price_Helpers::is_subscription( $nyp_id ) && WC_Name_Your_Price_Helpers::is_billing_period_variable( $nyp_id ) && $posted_period && array_key_exists( $posted_period, WC_Name_Your_Price_Helpers::get_subscription_period_strings() ) ) {
  177.             $cart_item_data['nyp_period'] = $posted_period;
  178.         }
  179.  
  180.         return $cart_item_data;
  181.     }
  182.  
  183.     /**
  184.      * Adjust the product based on cart session data.
  185.      *
  186.      * @param  array $cart_item $cart_item['data'] is product object in session
  187.      * @param  array $values cart item array
  188.      * @since 1.0
  189.      */
  190.     public function get_cart_item_from_session( $cart_item, $values ) {
  191.  
  192.         // No need to check is_nyp b/c this has already been validated by validate_add_cart_item().
  193.         if ( isset( $values['nyp'] ) ) {
  194.             $cart_item['nyp'] = $values['nyp'];
  195.  
  196.             // Add the subscription billing period.
  197.             if ( WC_Name_Your_Price_Helpers::is_subscription( $cart_item['data'] ) && isset( $values['nyp_period'] ) && array_key_exists( $values['nyp_period'], WC_Name_Your_Price_Helpers::get_subscription_period_strings() ) ) {
  198.                 $cart_item['nyp_period'] = $values['nyp_period'];
  199.             }
  200.  
  201.             $cart_item = $this->set_cart_item( $cart_item );
  202.         }
  203.  
  204.         return $cart_item;
  205.     }
  206.  
  207.     /**
  208.      * Change the price of the item in the cart.
  209.      *
  210.      * @since 3.0
  211.      *
  212.      * @param  array $cart_item
  213.      * @return  array
  214.      */
  215.     public function set_cart_item( $cart_item ) {
  216.  
  217.         // Adjust price in cart if nyp is set.
  218.         if ( isset( $cart_item['nyp'] ) && isset( $cart_item['data'] ) ) {
  219.  
  220.             $product = $cart_item['data'];
  221.  
  222.             $product->set_price( $cart_item['nyp'] );
  223.             $product->set_sale_price( $cart_item['nyp'] );
  224.             $product->set_regular_price( $cart_item['nyp'] );
  225.  
  226.             // Subscription-specific price and variable billing period.
  227.             if ( $product->is_type( array( 'subscription', 'subscription_variation' ) ) ) {
  228.  
  229.                 $product->update_meta_data( '_subscription_price', $cart_item['nyp'] );
  230.  
  231.                 if ( WC_Name_Your_Price_Helpers::is_billing_period_variable( $product ) && isset( $cart_item['nyp_period'] ) ) {
  232.  
  233.                     // Length may need to be re-calculated. Hopefully no one is using the length but who knows.
  234.                     // v3.1.3 disables the length selector when in variable billing mode.
  235.                     $original_period = WC_Subscriptions_Product::get_period( $product );
  236.                     $original_length = WC_Subscriptions_Product::get_length( $product );
  237.  
  238.                     if ( $original_length > 0 && $original_period && $cart_item['nyp_period'] !== $original_period ) {
  239.                         $factors    = WC_Name_Your_Price_Helpers::annual_price_factors();
  240.                         $new_length = $original_length * $factors[ $cart_item['nyp_period'] ] / $factors[ $original_period ];
  241.                         $product->update_meta_data( '_subscription_length', floor($new_length) );
  242.                     }
  243.  
  244.                     // Set period to the chosen period.
  245.                     $product->update_meta_data( '_subscription_period', $cart_item['nyp_period'] );
  246.  
  247.                     // Variable billing period is always a "per" interval.
  248.                     $product->update_meta_data( '_subscription_period_interval', 1 );
  249.  
  250.                 }
  251.             }
  252.         }
  253.  
  254.         return $cart_item;
  255.     }
  256.  
  257.     /**
  258.      * Validate an NYP product before adding to cart.
  259.      *
  260.      * @since 1.0
  261.      *
  262.      * @param  int    $product_id     - Contains the ID of the product.
  263.      * @param  int    $quantity       - Contains the quantity of the item.
  264.      * @param  int    $variation_id   - Contains the ID of the variation.
  265.      * @param  array  $variation      - Attribute values.
  266.      * @param  array  $cart_item_data - Extra cart item data we want to pass into the item.
  267.      * @return bool
  268.      */
  269.     public function validate_add_cart_item( $passed, $product_id, $quantity, $variation_id = '', $variations = '', $cart_item_data = array() ) {
  270.  
  271.         $nyp_id  = $variation_id ? $variation_id : $product_id;
  272.         $product = wc_get_product( $nyp_id );
  273.  
  274.         // Skip if not a NYP product - send original status back.
  275.         if ( ! WC_Name_Your_Price_Helpers::is_nyp( $product ) ) {
  276.             return $passed;
  277.         }
  278.  
  279.         $suffix = WC_Name_Your_Price_Helpers::get_suffix( $nyp_id );
  280.  
  281.         // Get_posted_price() runs the price through the standardize_number() helper.
  282.         $price = isset( $cart_item_data['nyp'] ) ? $cart_item_data['nyp'] : WC_Name_Your_Price_Helpers::get_posted_price( $product, $suffix );
  283.  
  284.         // Get the posted billing period.
  285.         $period = isset( $cart_item_data['nyp_period'] ) ? $cart_item_data['nyp_period'] : WC_Name_Your_Price_Helpers::get_posted_period( $product, $suffix );
  286.  
  287.         return $this->validate_price( $product, $quantity, $price, $period );
  288.  
  289.     }
  290.  
  291.     /**
  292.      * Re-validate prices on cart load.
  293.      * Specifically we are looking to prevent smart/quick pay gateway buttons completing an order that is invalid.
  294.      */
  295.     public function check_cart_items() {
  296.  
  297.         foreach ( WC()->cart->cart_contents as $cart_item_key => $cart_item ) {
  298.  
  299.             if ( isset( $cart_item['nyp'] ) ) {
  300.                 $period = isset( $cart_item['nyp_period'] ) ? $cart_item['nyp_period'] : '';
  301.                 $this->validate_price( $cart_item['data'], $cart_item['quantity'], $cart_item['nyp'], $period, 'cart' );
  302.             }
  303.         }
  304.     }
  305.  
  306.     /**
  307.      * Validate an NYP product's price is valid.
  308.      *
  309.      * @since 3.0
  310.      *
  311.      * @param  mixed   $product
  312.      * @param  int     $quantity
  313.      * @param  string  $price
  314.      * @param  string  $period
  315.      * @param  string  $context
  316.      * @param  bool    $return_error - When true returns the string error message.
  317.      * @throws Exception When the entered price is not valid
  318.      * @return boolean|string
  319.      */
  320.     public function validate_price( $product, $quantity, $price, $period = '', $context = 'add-to-cart', $return_error = false ) {
  321.         $is_configuration_valid = true;
  322.  
  323.         try {
  324.  
  325.             // Sanity check.
  326.             $product = WC_Name_Your_Price_Helpers::maybe_get_product_instance( $product );
  327.  
  328.             if ( ! ( $product instanceof WC_Product ) ) {
  329.                 $notice = WC_Name_Your_Price_Helpers::error_message( 'invalid-product' );
  330.                 throw new Exception( $notice );
  331.             }
  332.  
  333.             $product_id    = $product->get_id();
  334.             $product_title = $product->get_title();
  335.  
  336.             // Get minimum price.
  337.             $minimum = WC_Name_Your_Price_Helpers::get_minimum_price( $product );
  338.  
  339.             // Get maximum price.
  340.             $maximum = WC_Name_Your_Price_Helpers::get_maximum_price( $product );
  341.  
  342.             // Minimum error template.
  343.             $error_template = WC_Name_Your_Price_Helpers::is_minimum_hidden( $product ) ? 'hide_minimum' : 'minimum';
  344.  
  345.             // Check that it is a positive numeric value.
  346.             if ( ! is_numeric( $price ) || is_infinite( $price ) || floatval( $price ) < 0 ) {
  347.  
  348.                 $notice = WC_Name_Your_Price_Helpers::error_message(
  349.                     'invalid',
  350.                     array( '%%TITLE%%' => $product_title ),
  351.                     $product,
  352.                     $context
  353.                 );
  354.  
  355.                 throw new Exception( $notice );
  356.  
  357.                 // Check that it is greater than minimum price for variable billing subscriptions.
  358.             } elseif ( $minimum && $period && WC_Name_Your_Price_Helpers::is_subscription( $product ) && WC_Name_Your_Price_Helpers::is_billing_period_variable( $product ) ) {
  359.  
  360.                 // Minimum billing period.
  361.                 $minimum_period = WC_Name_Your_Price_Helpers::get_minimum_billing_period( $product );
  362.  
  363.                 // Annual minimum.
  364.                 $minimum_annual = WC_Name_Your_Price_Helpers::annualize_price( $minimum, $minimum_period );
  365.  
  366.                 // Annual price.
  367.                 $annual_price = WC_Name_Your_Price_Helpers::annualize_price( $price, $period );
  368.  
  369.                 // By standardizing the prices over the course of a year we can safely compare them.
  370.                 if ( $annual_price < $minimum_annual ) {
  371.  
  372.                     $factors = WC_Name_Your_Price_Helpers::annual_price_factors();
  373.  
  374.                     // If set period is in the $factors array we can calc the min price shown in the error according to entered period.
  375.                     if ( isset( $factors[ $period ] ) ) {
  376.                         $error_price  = $minimum_annual / $factors[ $period ];
  377.                         $error_period = $period;
  378.                         // Otherwise, just show the saved minimum price and period.
  379.                     } else {
  380.                         $error_price  = $minimum;
  381.                         $error_period = $minimum_period;
  382.                     }
  383.  
  384.                     // The minimum is a combo of price and period.
  385.                     $minimum_error = wc_price( $error_price ) . ' / ' . $error_period;
  386.  
  387.                     $notice = WC_Name_Your_Price_Helpers::error_message(
  388.                         $error_template,
  389.                         array(
  390.                             '%%TITLE%%'   => $product_title,
  391.                             '%%MINIMUM%%' => $minimum_error,
  392.                         ),
  393.                         $product,
  394.                         $context
  395.                     );
  396.  
  397.                     throw new Exception( $notice );
  398.  
  399.                 }
  400.                 // Check that it is greater than minimum price.
  401.             } elseif ( $minimum && floatval( $price ) < floatval( $minimum ) ) {
  402.  
  403.                 $notice = WC_Name_Your_Price_Helpers::error_message(
  404.                     $error_template,
  405.                     array(
  406.                         '%%TITLE%%'   => $product_title,
  407.                         '%%MINIMUM%%' => wc_price( $minimum ),
  408.                     ),
  409.                     $product,
  410.                     $context
  411.                 );
  412.  
  413.                 throw new Exception( $notice );
  414.  
  415.                 // Check that it is less than maximum price.
  416.             } elseif ( $maximum && floatval( $price ) > floatval( $maximum ) ) {
  417.  
  418.                 $error_template = '' !== $context ? 'maximum-' . $context : 'maximum';
  419.                 $notice         = WC_Name_Your_Price_Helpers::error_message(
  420.                     'error_template',
  421.                     array(
  422.                         '%%TITLE%%'   => $product_title,
  423.                         '%%MAXIMUM%%' => wc_price( $maximum ),
  424.                     ),
  425.                     $product,
  426.                     $context
  427.                 );
  428.  
  429.                 throw new Exception( $notice );
  430.  
  431.             }
  432.         } catch ( Exception $e ) {
  433.  
  434.             $notice = $e->getMessage();
  435.  
  436.             if ( $notice ) {
  437.  
  438.                 // Return the error message. Typically for use by headless applications.
  439.                 if ( $return_error ) {
  440.                     return $notice;
  441.                 }
  442.  
  443.                 wc_add_notice( $notice, 'error' );
  444.             }
  445.  
  446.             $is_configuration_valid = false;
  447.  
  448.         } finally {
  449.             return $is_configuration_valid;
  450.         }
  451.  
  452.     }
  453.  
  454.     /**
  455.      * ---------------------------------------------------------------------------------
  456.      * Deprecated Functions
  457.      * ---------------------------------------------------------------------------------
  458.      */
  459.  
  460.     /**
  461.      * Change the price of the item in the cart.
  462.      *
  463.      * @since 1.0
  464.      * @deprecated 3.0
  465.      */
  466.     public function add_cart_item( $cart_item ) {
  467.         wc_deprecated_function( 'add_cart_item', '3.0', 'Renamed to set_cart_item()' );
  468.         return $this->set_cart_item( $cart_item );
  469.     }
  470.  
  471. } // End class.
  472.  
Advertisement
Add Comment
Please, Sign In to add comment