クラス
Cart
ソース ソース
ファイル: src/API/Cart.php
class Cart { /** * Used during cart initialization and processing * * @var null|array */ public $payload = null; /** * Registers hooks * * @author Evan D Shaw <evandanielshaw@gmail.com> * @return void */ public function init() { add_action('usces_action_lastprocessing', [$this, 'unsetSessionVars']); add_action('usces_main', [$this, 'swapSession']); add_action('usces_action_after_inCart', [$this, 'saveGroupAndItemLabelsInGroupItemsCart'], 10, 1); add_filter('usces_filter_up_serialize', [$this, 'filterComboSetUpSerialize'], 10, 2); add_filter('usces_filter_realprice', [$this, 'filterComboSetRealPrice'], 10, 2); } /** * Unsets `$_SESSION` variables * * @author Evan D Shaw <evandanielshaw@gmail.com> * @return void */ public function unsetSessionVars() { unset($_SESSION['wcexicsComboSetId']); unset($_SESSION['wcexicsGroupToItemMap']); unset($_SESSION['wcexicsGroupItemOption']); unset($_SESSION['wcexicsSingleItem']); } /** * Swaps `$_SESSION['usces_singleitem']` with `$_SESSION['wcexicsSingleItem']` * * Since we also use `$usces->incart_check()` for checking combo-set group items, `$_SESSION['usces_singleitem']`s * `itemOption` and `quant` keys get overriden if any checks fail. This causes all item option selections * and the quantity selection to be lost which is why we need to temporarily set them in `wcexicsSingleItem` * and then revert `usces_singleitem` to its correct state. * * @author Evan D Shaw <evandanielshaw@gmail.com> * @return void */ public function swapSession() { if (isset($_SESSION['wcexicsSingleItem']['itemOption'])) { $_SESSION['usces_singleitem']['itemOption'] = $_SESSION['wcexicsSingleItem']['itemOption']; } if (isset($_SESSION['wcexicsSingleItem']['quant'])) { $_SESSION['usces_singleitem']['quant'] = $_SESSION['wcexicsSingleItem']['quant']; } unset($_SESSION['wcexicsSingleItem']); } /** * Adds combo-set ID and group items cart to serialized representation of the combo-set cart item. * * This allows the buyer to purchase multiple combo-set items with different selections * in the same purchase. We also use this serialized group items cart later on in purchase * processing. * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param array $sels * @return array */ public function filterComboSetInSerialize($sels) { if (empty($this->payload)) { return $sels; } $sels['comboSetId'] = $this->payload['comboSetId']; $sels['comboSetItems'] = $this->payload['groupItemsCart']; return $sels; } /** * Adds combo-set ID and group items to serialized array for `up_serialize` * * This makes cart page quantity update and item deletion possible * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param array $sels * @param int $index * @return array */ public function filterComboSetUpSerialize($sels, $index) { if (!isset($_POST['comboSetId'][$index]) || !isset($_POST['comboSetItems'][$index])) { return $sels; } $sels['comboSetId'] = (int)$_POST['comboSetId'][$index]; $sels['comboSetItems'] = unserialize($_POST['comboSetItems'][$index]); return $sels; } /** * Adds fixed values (price modifier) to the serialized array * * Price modifier is used later on when calculating the group item price * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param array $sels * @return array */ public function filterGroupItemInSerialize($sels) { $sels['itemId'] = (int)$_POST['itemId']; $sels['groupId'] = (int)$_POST['groupId']; $sels['comboSetId'] = (int)$_POST['comboSetId']; $sels['priceModifier'] = (int)$_POST['priceModifier']; return $sels; } /** * Save maps of group and item labels in combo-set cart * * We need to save the labels in case a group or item is deleted or its label is * changed during checkout, otherwise it will disappear from the combo-set cart HTML * * We can't save these values directly in the group items cart because it may cause identical * cart items to be duplicated if the buyer adds the same combo-set after a label has been * changed from the admin console * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param string $serial * @return void */ public function saveGroupAndItemLabelsInGroupItemsCart($serial) { $maps = self::getGroupAndItemLabelsFromSerial($serial); if (empty($maps)) { return; } $_SESSION['usces_cart'][$serial]['grouplabelmap'] = $maps['grouplabelmap']; $_SESSION['usces_cart'][$serial]['itemlabelmap'] = $maps['itemlabelmap']; } /** * Gets group and group item labels from serialized combo-set cart string * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param string $serial * @return array|null */ public static function getGroupAndItemLabelsFromSerial($serial) { $sels = unserialize($serial); if (empty($sels['comboSetId']) || empty($sels['comboSetItems'])) { return null; } $grouplabelmap = []; $itemlabelmap = []; foreach ($sels['comboSetItems'] as $giserial => $gitem) { $gisels = unserialize($giserial); $group = ComboGroup::getComboGroupById($gisels['groupId']); $item = GroupItem::getGroupItemById($gisels['itemId']); $grouplabelmap[$group->getId()] = $group->getLabel(); $itemlabelmap[$item->getId()] = $item->getItemLabel(); } return [ 'grouplabelmap' => $grouplabelmap, 'itemlabelmap' => $itemlabelmap, ]; } /** * Process the price modifiers of all selected group items * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param int $price * @param string $serial * @return int */ public function filterComboSetRealPrice($price, $serial) { $sels = unserialize($serial); if (empty($sels['comboSetId']) || empty($sels['comboSetItems'])) { // not a combo-set return $price; } // add modifiers to combo-set total amount foreach ($sels['comboSetItems'] as $serial => $item) { $price += (int)$item['price']; } return $price; } /** * Sets the group item price with its price modifier * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param int $price * @param string $serial * @return int */ public function filterGroupItemRealPrice($price, $serial) { $sels = unserialize($serial); $price = (int)$sels['priceModifier']; return $price; } /** * Adds a combo-set item to the cart using `$_POST` data * * @author Evan D Shaw <evandanielshaw@gmail.com> * @global \WP_Query $wp_query * @global \usc_e_shop $usces * @return void */ public function postComboSetToCart() { global $wp_query, $usces; // is $wp_query really necessary? who knows... // set custom key to `inCart` so we can use Welcart's cart class $_POST['inCart'] = $_POST['wcexicsAddToCart']; unset($_POST['wcexicsAddToCart']); $originalpost = $_POST; $ids = array_keys($_POST['inCart']); $post_id = (int)$ids[0]; $skus = array_keys($_POST['inCart'][$post_id]); $skucode = urldecode($skus[0]); // we need to make sure the item is a combo-set item $comboset = ComboSet::getComboSetFromSkuCode($post_id, $skucode); if ($comboset instanceof GenericError) { PurchaseSanityChecks::exitAddToCartWithError($comboset->message); } $pvals = self::getAddToCartPostValues($comboset->getId()); $groupids = $pvals['groupids']; $itemoptmap = $pvals['itemoptmap']; // save selections in $_SESSION so that they aren't lost in case of an error redirect self::setAddToCartSessionVars($comboset->getId(), $groupids, $itemoptmap); // check that all group IDs and item IDs exist. Check that required groups have selections $error = PurchaseSanityChecks::comboSetSelectionsAreValid($comboset->getId(), $groupids); if ($error instanceof GenericError) { // abort early since other errors don't matter in this case PurchaseSanityChecks::exitAddToCartWithError($error->message); } // check that the combo-set item division is valid for the group item selections $error = PurchaseSanityChecks::comboSetItemDivisionIsValid($comboset->getId(), $groupids); if ($error instanceof GenericError) { PurchaseSanityChecks::exitAddToCartWithError($error->message); } // check that item charge types are valid $error = PurchaseSanityChecks::comboSetItemChargeTypeIsValid($comboset->getId(), $groupids); if ($error instanceof GenericError) { PurchaseSanityChecks::exitAddToCartWithError($error->message); } // get post data for constructing a cart out of the selected group items $postdata = self::getInCartPostData($comboset, $groupids, $itemoptmap); // use `$usces->incart_check()` to check the combo-set SKU and all selected group items self::inCartCheckComboSet($postdata); // unset $_SESSION selections since checks passed ($_SESSION['usces_singleitem'] is handled by Welcart) $this->unsetSessionVars(); // Now we need to construct a cart out of all the group items $groupitemscart = $this->getGroupItemsCart($postdata)['rawCart']; // set $this->payload for use in processing the combo-set item $this->payload = []; $this->payload['comboSetId'] = $comboset->getId(); $this->payload['groupItemsCart'] = $groupitemscart; // Add the combo-set item to the cart add_filter('usces_filter_in_serialize', [$this, 'filterComboSetInSerialize'], 10); $_POST = $originalpost; $usces->page = 'cart'; $usces->cart->inCart(); remove_filter('usces_filter_in_serialize', [$this, 'filterComboSetInSerialize']); // let Welcart handle the rest add_action('the_post', [$usces, 'action_cartFilter']); add_filter('yoast-ga-push-after-pageview', 'usces_trackPageview_cart'); add_action('template_redirect', [$usces, 'template_redirect']); } /** * Returns key-value array of `$_POST` values sent to the add to cart API * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param int $comboSetId * @return (int|array)[] */ public static function getAddToCartPostValues($comboSetId) { $groupids = !empty($_POST['wcexicsGroups'][$comboSetId]) ? (array)$_POST['wcexicsGroups'][$comboSetId] : []; $itemoptmap = !empty($_POST['wcexicsGroupItemOption']) ? (array)$_POST['wcexicsGroupItemOption'] : []; return [ 'groupids' => $groupids, 'itemoptmap' => $itemoptmap, ]; } /** * Saves POST data from the add to cart API in `$_SESSION` * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param int $comboSetId * @param array $grouptoitemmap * @param array $itemoptmap * @return void */ public static function setAddToCartSessionVars($comboSetId, $grouptoitemmap, $itemoptmap) { $_SESSION['wcexicsComboSetId'] = $comboSetId; $_SESSION['wcexicsGroupToItemMap'] = $grouptoitemmap; $_SESSION['wcexicsGroupItemOption'] = $itemoptmap; $_SESSION['wcexicsSingleItem']['itemOption'] = isset($_POST['itemOption']) ? $_POST['itemOption'] : []; $_SESSION['wcexicsSingleItem']['quant'] = isset($_POST['quant']) ? $_POST['quant'] : []; } /** * Checks stock and item options for the combo-set * * @author Evan D Shaw <evandanielshaw@gmail.com> * @global \usc_e_shop $usces * @param array $postdata * @return void */ public static function inCartCheckComboSet($postdata) { global $usces; /** * Check stock and item options for the combo-set item itself * * Note that at this point we haven't checked options or stock for group items so there may be a case * where the following `$usces->incart_check()` exits before we can construct group item error messages. * * However, by default, Welcart will use JavaScript to check that all required options are selected * for the item. Therefore, `$usces->incart_check()` will only exit with an error if there * is no stock. If there is no stock then it doesn't matter whether group items are valid * since the item can't be purchased anyways which is why we make the call here. */ $usces->incart_check(); // will exit to the item page if any checks fail // back up the $_POST object $backuppost = $_POST; foreach ($postdata as $post) { // overwrite $_POST since `$usces->incart_check()` takes no parameters... $_POST = array_merge($_POST, $post); // Check stock, options, etc for each item. $usces->incart_check(); // exits to the cart page if any checks fail } // restore the $_POST object $_POST = $backuppost; } /** * Get group items cart data * * @author Evan D Shaw <evandanielshaw@gmail.com> * @global \usc_e_shop $usces * @param array $postdata * @return array */ public function getGroupItemsCart($postdata) { global $usces; // There is no way to pass parameters to `->inCart()` so we have to overwrite $_SESSION['usces_cart']... $currentcart = $_SESSION['usces_cart']; unset($_SESSION['usces_cart']); add_filter('usces_filter_realprice', [$this, 'filterGroupItemRealPrice'], 10, 2); add_filter('usces_filter_in_serialize', [$this, 'filterGroupItemInSerialize'], 10); foreach ($postdata as $post) { // overwrite $_POST since `->inCart()` takes no parameters... $_POST = array_merge($_POST, $post); $usces->cart->inCart(); } remove_filter('usces_filter_realprice', [$this, 'filterGroupItemRealPrice']); remove_filter('usces_filter_in_serialize', [$this, 'filterGroupItemInSerialize']); // get the group items cart we just created $rawcart = $_SESSION['usces_cart']; $builtcart = $usces->cart->get_cart(); // restore the actual cart to its original state $_SESSION['usces_cart'] = $currentcart; return [ 'rawCart' => $rawcart, 'builtCart' => $builtcart, ]; } /** * Sanitizes and sorts all selections for a combo-set selections array * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param array $grouptoitemmap * @return array */ public static function sanitizeComboSetSelections($grouptoitemmap) { // sort for serialization ksort($grouptoitemmap); foreach ($grouptoitemmap as $groupid => $itemids) { $itemids = !empty($itemids) ? (array)$itemids : []; // sort for serialization sort($itemids); $cleanselections = []; // strip all -1 values since they represent no selection. All values outside of -1 are // processed as explicit selections foreach ($itemids as $selitemid) { if ((int)$selitemid !== -1) { $cleanselections[] = (int)$selitemid; } } // sanitize request map for easier processing $grouptoitemmap[$groupid] = $cleanselections; } return $grouptoitemmap; } /** * Gets post data for each group item. * * Checks for the existence of each group and item. Also checks that required groups have * at least one selection. * * Aborts early if any of the above checks fail. If all groups * and items were found, an array of `$_POST` data which can be used directly * by `$usces->incart_check()` or `$usces->cart->inCart()` is returned. * * @author Evan D Shaw <evandanielshaw@gmail.com> * @param ComboSetType $comboset * @param array $grouptoitemmap * @param array $itemoptmap * @return GenericError|array */ public static function getInCartPostData(ComboSetType $comboset, $grouptoitemmap, $itemoptmap = []) { $postbuilder = []; $grouptoitemmap = self::sanitizeComboSetSelections($grouptoitemmap); foreach ($grouptoitemmap as $groupid => $itemids) { $group = ComboGroup::getComboGroupById($groupid); // if the group does not exist OR it is not associated with the combo-set, abort if ($group === null || $group->getComboSetId() !== $comboset->getId()) { return Master::getErrorStore()->getErrorResponse( ErrorStore::COMBO_GROUP_NOT_FOUND, [$groupid], [], [$groupid] ); } foreach ($itemids as $itemid) { $item = GroupItem::getGroupItemById($itemid); // if the item does not exist OR it is not associated with the group, abort if ($item === null || $item->getGroupId() !== $group->getId()) { return Master::getErrorStore()->getErrorResponse( ErrorStore::GROUP_ITEM_NOT_FOUND, [$itemid], [], [$itemid] ); } $itemskudata = $item->getSkuData(); $itempostid = $item->getPostId(); $itemskucode = urlencode($itemskudata['code']); $postdata = []; $postdata['inCart'][$itempostid][$itemskucode] = 'dummy'; $postdata['quant'][$itempostid][$itemskucode] = $item->getItemQuantity(); $postdata['zaikonum'][$itempostid][$itemskucode] = $itemskudata['stocknum']; $postdata['zaiko'][$itempostid][$itemskucode] = $itemskudata['stock']; $postdata['gptekiyo'][$itempostid][$itemskucode] = $itemskudata['gp']; $postdata['skuPrice'][$itempostid][$itemskucode] = 0; if (!empty($itemoptmap[$item->getId()])) { $postdata['itemOption'][$itempostid][$itemskucode] = $itemoptmap[$item->getId()]; } $postdata['itemId'] = $item->getId(); $postdata['groupId'] = $group->getId(); $postdata['comboSetId'] = $comboset->getId(); $postdata['priceModifier'] = $item->getPriceModifier(); $postbuilder[] = $postdata; } } return $postbuilder; } }
- filterComboSetInSerialize — Adds combo-set ID and group items cart to serialized representation of the combo-set cart item.
- filterComboSetRealPrice — Process the price modifiers of all selected group items
- filterComboSetUpSerialize — Adds combo-set ID and group items to serialized array for `up_serialize`
- filterGroupItemInSerialize — Adds fixed values (price modifier) to the serialized array
- filterGroupItemRealPrice — Sets the group item price with its price modifier
- getAddToCartPostValues — Returns key-value array of `$_POST` values sent to the add to cart API
- getGroupAndItemLabelsFromSerial — Gets group and group item labels from serialized combo-set cart string
- getGroupItemsCart — Get group items cart data
- getInCartPostData — Gets post data for each group item.
- inCartCheckComboSet — Checks stock and item options for the combo-set
- init — Registers hooks
- postComboSetToCart — Adds a combo-set item to the cart using `$_POST` data
- sanitizeComboSetSelections — Sanitizes and sorts all selections for a combo-set selections array
- saveGroupAndItemLabelsInGroupItemsCart — Save maps of group and item labels in combo-set cart
- setAddToCartSessionVars — Saves POST data from the add to cart API in `$_SESSION`
- swapSession — Swaps `$_SESSION['usces_singleitem']` with `$_SESSION['wcexicsSingleItem']`
- unsetSessionVars — Unsets `$_SESSION` variables