vendor/shopware/core/Checkout/Promotion/DataAbstractionLayer/PromotionRedemptionUpdater.php line 87

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Checkout\Promotion\DataAbstractionLayer;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
  5. use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
  6. use Shopware\Core\Checkout\Promotion\Cart\PromotionProcessor;
  7. use Shopware\Core\Defaults;
  8. use Shopware\Core\Framework\Context;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\RetryableQuery;
  10. use Shopware\Core\Framework\Uuid\Uuid;
  11. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  12. class PromotionRedemptionUpdater implements EventSubscriberInterface
  13. {
  14.     /**
  15.      * @var Connection
  16.      */
  17.     private $connection;
  18.     public function __construct(Connection $connection)
  19.     {
  20.         $this->connection $connection;
  21.     }
  22.     /**
  23.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  24.      */
  25.     public static function getSubscribedEvents()
  26.     {
  27.         return [
  28.             CheckoutOrderPlacedEvent::class => 'orderPlaced',
  29.         ];
  30.     }
  31.     public function update(array $idsContext $context): void
  32.     {
  33.         $ids array_unique(array_filter($ids));
  34.         if (empty($ids) || $context->getVersionId() !== Defaults::LIVE_VERSION) {
  35.             return;
  36.         }
  37.         $sql = <<<'SQL'
  38.                 SELECT LOWER(HEX(order_line_item.promotion_id)) as promotion_id,
  39.                        COUNT(DISTINCT order_line_item.id) as total,
  40.                        LOWER(HEX(order_customer.customer_id)) as customer_id
  41.                 FROM order_line_item
  42.                 INNER JOIN order_customer
  43.                     ON order_customer.order_id = order_line_item.order_id
  44.                     AND order_customer.version_id = order_line_item.version_id
  45.                 WHERE order_line_item.type = :type
  46.                 AND order_line_item.promotion_id IN (:ids)
  47.                 AND order_line_item.version_id = :versionId
  48.                 GROUP BY order_line_item.promotion_id, order_customer.customer_id
  49. SQL;
  50.         $promotions $this->connection->fetchAll(
  51.             $sql,
  52.             ['type' => PromotionProcessor::LINE_ITEM_TYPE'ids' => Uuid::fromHexToBytesList($ids), 'versionId' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  53.             ['ids' => Connection::PARAM_STR_ARRAY]
  54.         );
  55.         if (empty($promotions)) {
  56.             return;
  57.         }
  58.         $update = new RetryableQuery(
  59.             $this->connection,
  60.             $this->connection->prepare('UPDATE promotion SET order_count = :count, orders_per_customer_count = :customerCount WHERE id = :id')
  61.         );
  62.         // group the promotions to update each promotion with a single update statement
  63.         $promotions $this->groupByPromotion($promotions);
  64.         foreach ($promotions as $id => $totals) {
  65.             $total array_sum($totals);
  66.             $update->execute([
  67.                 'id' => Uuid::fromHexToBytes($id),
  68.                 'count' => (int) $total,
  69.                 'customerCount' => json_encode($totals),
  70.             ]);
  71.         }
  72.     }
  73.     public function orderPlaced(CheckoutOrderPlacedEvent $event): void
  74.     {
  75.         $lineItems $event->getOrder()->getLineItems();
  76.         $customer $event->getOrder()->getOrderCustomer();
  77.         if (!$lineItems || !$customer) {
  78.             return;
  79.         }
  80.         $promotionIds = [];
  81.         /** @var OrderLineItemEntity $lineItem */
  82.         foreach ($lineItems as $lineItem) {
  83.             if ($lineItem->getType() !== PromotionProcessor::LINE_ITEM_TYPE) {
  84.                 continue;
  85.             }
  86.             $promotionId $lineItem->getPromotionId();
  87.             if ($promotionId === null) {
  88.                 continue;
  89.             }
  90.             $promotionIds[] = $promotionId;
  91.         }
  92.         if (!$promotionIds) {
  93.             return;
  94.         }
  95.         $allCustomerCounts $this->getAllCustomerCounts($promotionIds);
  96.         $update = new RetryableQuery(
  97.             $this->connection,
  98.             $this->connection->prepare('UPDATE promotion SET order_count = order_count + 1, orders_per_customer_count = :customerCount WHERE id = :id')
  99.         );
  100.         foreach ($promotionIds as $promotionId) {
  101.             $customerId $customer->getCustomerId();
  102.             if ($customerId !== null) {
  103.                 $allCustomerCounts[$promotionId][$customerId] = + ($allCustomerCounts[$promotionId][$customerId] ?? 0);
  104.             }
  105.             $update->execute([
  106.                 'id' => Uuid::fromHexToBytes($promotionId),
  107.                 'customerCount' => json_encode($allCustomerCounts[$promotionId]),
  108.             ]);
  109.         }
  110.     }
  111.     private function groupByPromotion(array $promotions): array
  112.     {
  113.         $grouped = [];
  114.         foreach ($promotions as $promotion) {
  115.             $id $promotion['promotion_id'];
  116.             $customerId $promotion['customer_id'];
  117.             $grouped[$id][$customerId] = (int) $promotion['total'];
  118.         }
  119.         return $grouped;
  120.     }
  121.     private function getAllCustomerCounts(array $promotionIds): array
  122.     {
  123.         $allCustomerCounts = [];
  124.         $countResult $this->connection->fetchAllAssociative(
  125.             'SELECT `id`, `orders_per_customer_count` FROM `promotion` WHERE `id` IN (:ids)',
  126.             ['ids' => Uuid::fromHexToBytesList($promotionIds)],
  127.             ['ids' => Connection::PARAM_STR_ARRAY]
  128.         );
  129.         foreach ($countResult as $row) {
  130.             if (!\is_string($row['orders_per_customer_count'])) {
  131.                 $allCustomerCounts[Uuid::fromBytesToHex($row['id'])] = [];
  132.                 continue;
  133.             }
  134.             $customerCount json_decode($row['orders_per_customer_count'], true);
  135.             if ($customerCount === false) {
  136.                 $allCustomerCounts[Uuid::fromBytesToHex($row['id'])] = [];
  137.                 continue;
  138.             }
  139.             $allCustomerCounts[Uuid::fromBytesToHex($row['id'])] = $customerCount;
  140.         }
  141.         return $allCustomerCounts;
  142.     }
  143. }