185 lines
6.8 KiB
PHP
185 lines
6.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\Cart\Plugin\CustomerData;
|
|
|
|
use Esun\Cart\Model\MilestoneProvider;
|
|
use Esun\Cart\Model\ShippingEstimate;
|
|
use Magento\Catalog\Helper\Image as ImageHelper;
|
|
use Magento\CatalogInventory\Api\StockRegistryInterface;
|
|
use Magento\Checkout\CustomerData\Cart;
|
|
use Magento\Checkout\Helper\Data as CheckoutHelper;
|
|
use Magento\Checkout\Model\Session as CheckoutSession;
|
|
use Magento\Quote\Api\CartRepositoryInterface;
|
|
use Magento\Quote\Model\Quote;
|
|
|
|
class CartPlugin
|
|
{
|
|
public function __construct(
|
|
private readonly CheckoutSession $checkoutSession,
|
|
private readonly CheckoutHelper $checkoutHelper,
|
|
private readonly MilestoneProvider $milestoneProvider,
|
|
private readonly ShippingEstimate $shippingEstimate,
|
|
private readonly ImageHelper $imageHelper,
|
|
private readonly StockRegistryInterface $stockRegistry,
|
|
private readonly CartRepositoryInterface $quoteRepository
|
|
) {
|
|
}
|
|
|
|
public function afterGetSectionData(Cart $subject, array $result): array
|
|
{
|
|
$quote = $this->checkoutSession->getQuote();
|
|
$removedOrphan = $this->removeOrphanedChildItems($quote);
|
|
$quote->setTotalsCollectedFlag(false)->collectTotals();
|
|
if ($removedOrphan) {
|
|
$this->quoteRepository->save($quote);
|
|
}
|
|
|
|
$result['items'] = $this->addItemPrices($result['items'] ?? [], $quote);
|
|
$result['drawer_totals'] = $this->getTotalsData($quote);
|
|
$result['purchase_progress'] = $this->milestoneProvider->getData($quote);
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function removeOrphanedChildItems(Quote $quote): bool
|
|
{
|
|
$removed = false;
|
|
foreach ($quote->getAllItems() as $item) {
|
|
$parentItemId = (int)$item->getParentItemId();
|
|
if ($parentItemId > 0 && !$quote->getItemById($parentItemId)) {
|
|
$quote->removeItem((int)$item->getId());
|
|
$removed = true;
|
|
}
|
|
}
|
|
|
|
return $removed;
|
|
}
|
|
|
|
private function getTotalsData(Quote $quote): array
|
|
{
|
|
$address = $quote->getShippingAddress();
|
|
$subtotal = (float)$quote->getSubtotal();
|
|
$discount = (float)$address->getDiscountAmount();
|
|
$estimate = $this->shippingEstimate->getData($quote);
|
|
$shipping = (float)$estimate['amount'];
|
|
$grandTotal = (float)$quote->getGrandTotal()
|
|
- (float)$address->getShippingAmount()
|
|
+ $shipping;
|
|
|
|
return [
|
|
'subtotal' => $this->checkoutHelper->formatPrice($subtotal),
|
|
'discount' => $this->checkoutHelper->formatPrice($discount),
|
|
'shipping' => $this->checkoutHelper->formatPrice($shipping),
|
|
'grand_total' => $this->checkoutHelper->formatPrice($grandTotal),
|
|
'has_shipping_estimate' => (bool)$estimate['available'],
|
|
'shipping_description' => (string)$estimate['description'],
|
|
'coupon_code' => (string)$quote->getCouponCode(),
|
|
];
|
|
}
|
|
|
|
private function addItemPrices(array $items, Quote $quote): array
|
|
{
|
|
$quoteItems = [];
|
|
foreach ($quote->getAllVisibleItems() as $quoteItem) {
|
|
$quoteItems[(int)$quoteItem->getId()] = $quoteItem;
|
|
}
|
|
|
|
foreach ($items as &$item) {
|
|
$itemId = (int)($item['item_id'] ?? 0);
|
|
if (!isset($quoteItems[$itemId])) {
|
|
continue;
|
|
}
|
|
|
|
$quoteItem = $quoteItems[$itemId];
|
|
$simpleProductOption = $quoteItem->getOptionByCode('simple_product');
|
|
$childProduct = $simpleProductOption?->getProduct();
|
|
$item['parent_product_id'] = (int)$quoteItem->getProductId();
|
|
$item['child_product_id'] = $childProduct && (int)$childProduct->getId() > 0
|
|
? (int)$childProduct->getId()
|
|
: (int)$quoteItem->getProductId();
|
|
$this->replaceWithChildProductImage($item, $quoteItem);
|
|
$item['min_sale_qty'] = $this->getMinSaleQty($quoteItem, $quote);
|
|
$currentPrice = (float)$quoteItem->getCalculationPrice();
|
|
$originalPrice = (float)$quoteItem->getOriginalPrice();
|
|
$item['line_total'] = $this->checkoutHelper->formatPrice(
|
|
$currentPrice * (float)$quoteItem->getQty()
|
|
);
|
|
$item['original_price'] = $originalPrice > $currentPrice + 0.0001
|
|
? $this->checkoutHelper->formatPrice($originalPrice)
|
|
: '';
|
|
}
|
|
unset($item);
|
|
|
|
return $items;
|
|
}
|
|
|
|
private function getMinSaleQty(\Magento\Quote\Model\Quote\Item $quoteItem, Quote $quote): float
|
|
{
|
|
$productIds = [(int)$quoteItem->getProductId()];
|
|
$simpleProductOption = $quoteItem->getOptionByCode('simple_product');
|
|
$childProduct = $simpleProductOption?->getProduct();
|
|
|
|
if ($childProduct && (int)$childProduct->getId() > 0) {
|
|
$productIds[] = (int)$childProduct->getId();
|
|
}
|
|
|
|
$minimumQty = 1.0;
|
|
foreach (array_unique(array_filter($productIds)) as $productId) {
|
|
try {
|
|
$stockItem = $this->stockRegistry->getStockItem(
|
|
$productId,
|
|
(int)$quote->getStore()->getWebsiteId()
|
|
);
|
|
|
|
if (method_exists($stockItem, 'setCustomerGroupId')) {
|
|
$stockItem->setCustomerGroupId((int)$quote->getCustomerGroupId());
|
|
}
|
|
|
|
$minimumQty = max($minimumQty, (float)$stockItem->getMinSaleQty());
|
|
} catch (\Magento\Framework\Exception\LocalizedException) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return $minimumQty;
|
|
}
|
|
|
|
private function replaceWithChildProductImage(array &$item, \Magento\Quote\Model\Quote\Item $quoteItem): void
|
|
{
|
|
$simpleProductOption = $quoteItem->getOptionByCode('simple_product');
|
|
$childProduct = $simpleProductOption?->getProduct();
|
|
|
|
if (!$childProduct || !(int)$childProduct->getId()) {
|
|
return;
|
|
}
|
|
|
|
$imageFile = $this->getChildImageFile($childProduct);
|
|
if ($imageFile === '') {
|
|
return;
|
|
}
|
|
|
|
$image = $this->imageHelper
|
|
->init($childProduct, 'mini_cart_product_thumbnail')
|
|
->setImageFile($imageFile);
|
|
$item['product_image'] = [
|
|
'src' => $image->getUrl(),
|
|
'alt' => $image->getLabel() ?: (string)$childProduct->getName(),
|
|
'width' => $image->getWidth(),
|
|
'height' => $image->getHeight(),
|
|
];
|
|
}
|
|
|
|
private function getChildImageFile(\Magento\Catalog\Model\Product $childProduct): string
|
|
{
|
|
foreach (['thumbnail', 'small_image', 'image'] as $imageAttribute) {
|
|
$imageFile = (string)$childProduct->getData($imageAttribute);
|
|
if ($imageFile !== '' && $imageFile !== 'no_selection') {
|
|
return $imageFile;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
}
|