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

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