クラス
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