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

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