Files
2026-09-15 10:35:56 +08:00

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 '';
}
}