resultRedirectFactory->create(); $backUrl = (string)$this->_redirect->getRefererUrl(); if (!$this->formKeyValidator->validate($this->getRequest())) { $this->messageManager->addErrorMessage(__('Your session has expired. Please try again.')); return $redirect->setUrl($backUrl); } try { $parentProduct = $this->getParentProduct(); $items = $this->getRequestedItems(); $this->validateChildren($parentProduct, array_keys($items)); $configurableAttributes = $parentProduct->getTypeInstance() ->getConfigurableAttributes($parentProduct); $itemsToAdd = $items; $isSyncMode = (string)$this->getRequest()->getParam('mix_mode') === 'sync'; if ($isSyncMode) { $updates = []; foreach ($this->cart->getQuote()->getAllVisibleItems() as $quoteItem) { if ((int)$quoteItem->getProductId() !== (int)$parentProduct->getId()) { continue; } $simpleProductOption = $quoteItem->getOptionByCode('simple_product'); $childProductId = (int)($simpleProductOption?->getProduct()?->getId() ?: 0); if ($childProductId > 0 && isset($items[$childProductId])) { $updates[(int)$quoteItem->getId()] = ['qty' => $items[$childProductId]]; unset($itemsToAdd[$childProductId]); } else { $updates[(int)$quoteItem->getId()] = ['remove' => true]; } } if ($updates) { $this->cart->updateItems($updates); } } foreach ($itemsToAdd as $childProductId => $quantity) { $childProduct = $this->productRepository->getById( $childProductId, false, (int)$this->storeManager->getStore()->getId() ); if (!$childProduct->isSaleable()) { throw new LocalizedException( __('%1 is currently unavailable.', $childProduct->getName()) ); } $superAttributes = []; foreach ($configurableAttributes as $configurableAttribute) { $attribute = $configurableAttribute->getProductAttribute(); $superAttributes[(int)$attribute->getId()] = $childProduct->getData( $attribute->getAttributeCode() ); } $this->cart->addProduct(clone $parentProduct, new DataObject([ 'qty' => $quantity, 'super_attribute' => $superAttributes, ])); } $this->cart->save(); $totalQuantity = array_sum($items); $this->messageManager->addSuccessMessage($isSyncMode ? __('You updated %1 variants (%2 items) in your shopping cart.', count($items), $totalQuantity) : __('You added %1 variants (%2 items) to your shopping cart.', count($items), $totalQuantity)); } catch (LocalizedException $exception) { $this->messageManager->addErrorMessage($exception->getMessage()); } catch (\Throwable $exception) { $this->logger->critical($exception); $this->messageManager->addErrorMessage( __('We can\'t add these items to your shopping cart right now.') ); } return $redirect->setUrl($backUrl); } private function getParentProduct(): Product { $productId = (int)$this->getRequest()->getParam('product'); $product = $this->productRepository->getById( $productId, false, (int)$this->storeManager->getStore()->getId() ); if ($product->getTypeId() !== Configurable::TYPE_CODE || !$product->isSaleable()) { throw new LocalizedException(__('This product is not available for mix purchasing.')); } return $product; } /** @return array */ private function getRequestedItems(): array { $requestItems = $this->getRequest()->getParam('mix_items', []); $items = []; if (is_array($requestItems)) { foreach ($requestItems as $childProductId => $quantity) { $childProductId = (int)$childProductId; $quantity = (int)$quantity; if ($childProductId > 0 && $quantity > 0) { $items[$childProductId] = $quantity; } } } if (!$items) { throw new LocalizedException(__('Please choose at least one variant and quantity.')); } return $items; } /** @param int[] $childProductIds */ private function validateChildren(Product $parentProduct, array $childProductIds): void { $allowedIds = array_map( 'intval', $parentProduct->getTypeInstance()->getUsedProductIds($parentProduct) ); if (array_diff($childProductIds, $allowedIds)) { throw new LocalizedException(__('One or more selected variants do not belong to this product.')); } } }