vendor/shopware/core/Checkout/Customer/Subscriber/CustomerMetaFieldSubscriber.php line 39

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Checkout\Customer\Subscriber;
  3. use Doctrine\DBAL\ArrayParameterType;
  4. use Doctrine\DBAL\Connection;
  5. use Shopware\Core\Checkout\Order\OrderDefinition;
  6. use Shopware\Core\Checkout\Order\OrderStates;
  7. use Shopware\Core\Defaults;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Doctrine\RetryableQuery;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\DeleteCommand;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
  11. use Shopware\Core\Framework\Log\Package;
  12. use Shopware\Core\Framework\Uuid\Uuid;
  13. use Shopware\Core\System\StateMachine\Event\StateMachineTransitionEvent;
  14. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  15. /**
  16.  * @internal
  17.  */
  18. #[Package('customer-order')]
  19. class CustomerMetaFieldSubscriber implements EventSubscriberInterface
  20. {
  21.     /**
  22.      * @internal
  23.      */
  24.     public function __construct(private readonly Connection $connection)
  25.     {
  26.     }
  27.     public static function getSubscribedEvents(): array
  28.     {
  29.         return [
  30.             StateMachineTransitionEvent::class => 'fillCustomerMetaDataFields',
  31.             PreWriteValidationEvent::class => 'deleteOrder',
  32.         ];
  33.     }
  34.     public function fillCustomerMetaDataFields(StateMachineTransitionEvent $event): void
  35.     {
  36.         if ($event->getContext()->getVersionId() !== Defaults::LIVE_VERSION) {
  37.             return;
  38.         }
  39.         if ($event->getEntityName() !== 'order') {
  40.             return;
  41.         }
  42.         if ($event->getToPlace()->getTechnicalName() !== OrderStates::STATE_COMPLETED && $event->getFromPlace()->getTechnicalName() !== OrderStates::STATE_COMPLETED) {
  43.             return;
  44.         }
  45.         $this->updateCustomer([$event->getEntityId()]);
  46.     }
  47.     public function deleteOrder(PreWriteValidationEvent $event): void
  48.     {
  49.         if ($event->getContext()->getVersionId() !== Defaults::LIVE_VERSION) {
  50.             return;
  51.         }
  52.         $orderIds = [];
  53.         foreach ($event->getCommands() as $command) {
  54.             if ($command->getDefinition()->getClass() === OrderDefinition::class
  55.                 && $command instanceof DeleteCommand
  56.             ) {
  57.                 $orderIds[] = Uuid::fromBytesToHex($command->getPrimaryKey()['id']);
  58.             }
  59.         }
  60.         $this->updateCustomer($orderIdstrue);
  61.     }
  62.     /**
  63.      * @param array<string> $orderIds
  64.      */
  65.     private function updateCustomer(array $orderIdsbool $isDelete false): void
  66.     {
  67.         if (empty($orderIds)) {
  68.             return;
  69.         }
  70.         $customerIds $this->connection->fetchFirstColumn(
  71.             'SELECT DISTINCT LOWER(HEX(customer_id)) FROM `order_customer` WHERE order_id IN (:ids) AND order_version_id = :version AND customer_id IS NOT NULL',
  72.             ['ids' => Uuid::fromHexToBytesList($orderIds), 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  73.             ['ids' => ArrayParameterType::STRING]
  74.         );
  75.         if (empty($customerIds)) {
  76.             return;
  77.         }
  78.         $parameters = [
  79.             'customerIds' => Uuid::fromHexToBytesList($customerIds),
  80.             'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION),
  81.             'state' => OrderStates::STATE_COMPLETED,
  82.         ];
  83.         $types = [
  84.             'customerIds' => ArrayParameterType::STRING,
  85.         ];
  86.         $whereOrder '';
  87.         if ($isDelete) {
  88.             $whereOrder 'AND `order`.id NOT IN (:exceptOrderIds)';
  89.             $parameters['exceptOrderIds'] = Uuid::fromHexToBytesList($orderIds);
  90.             $types['exceptOrderIds'] = ArrayParameterType::STRING;
  91.         }
  92.         $select '
  93.             SELECT `order_customer`.customer_id as id,
  94.                    COUNT(`order`.id) as order_count,
  95.                    SUM(`order`.amount_total) as order_total_amount,
  96.                    MAX(`order`.order_date_time) as last_order_date
  97.             FROM `order_customer`
  98.             INNER JOIN `order`
  99.                 ON `order`.id = `order_customer`.order_id
  100.                 AND `order`.version_id = `order_customer`.order_version_id
  101.                 AND `order`.version_id = :version
  102.                 ' $whereOrder '
  103.             INNER JOIN `state_machine_state`
  104.                 ON `state_machine_state`.id = `order`.state_id
  105.                 AND `state_machine_state`.technical_name = :state
  106.             WHERE `order_customer`.customer_id IN (:customerIds)
  107.             GROUP BY `order_customer`.customer_id
  108.         ';
  109.         $data $this->connection->fetchAllAssociative($select$parameters$types);
  110.         if (empty($data)) {
  111.             foreach ($customerIds as $customerId) {
  112.                 $data[] = [
  113.                     'id' => Uuid::fromHexToBytes($customerId),
  114.                     'order_count' => 0,
  115.                     'order_total_amount' => 0,
  116.                     'last_order_date' => null,
  117.                 ];
  118.             }
  119.         }
  120.         $update = new RetryableQuery(
  121.             $this->connection,
  122.             $this->connection->prepare('UPDATE `customer` SET order_count = :order_count, order_total_amount = :order_total_amount, last_order_date = :last_order_date WHERE id = :id')
  123.         );
  124.         foreach ($data as $record) {
  125.             $update->execute($record);
  126.         }
  127.     }
  128. }