vendor/shopware/core/Checkout/Order/Listener/OrderStateChangeEventListener.php line 48

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Checkout\Order\Listener;
  3. use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity;
  4. use Shopware\Core\Checkout\Order\Event\OrderStateChangeCriteriaEvent;
  5. use Shopware\Core\Checkout\Order\Event\OrderStateMachineStateChangeEvent;
  6. use Shopware\Core\Checkout\Order\OrderEntity;
  7. use Shopware\Core\Checkout\Order\OrderException;
  8. use Shopware\Core\Framework\Context;
  9. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Pricing\CashRoundingConfig;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  12. use Shopware\Core\Framework\Event\BusinessEventCollector;
  13. use Shopware\Core\Framework\Event\BusinessEventCollectorEvent;
  14. use Shopware\Core\Framework\Log\Package;
  15. use Shopware\Core\System\StateMachine\Aggregation\StateMachineState\StateMachineStateEntity;
  16. use Shopware\Core\System\StateMachine\Event\StateMachineStateChangeEvent;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  19. /**
  20.  * @internal
  21.  */
  22. #[Package('customer-order')]
  23. class OrderStateChangeEventListener implements EventSubscriberInterface
  24. {
  25.     /**
  26.      * @internal
  27.      */
  28.     public function __construct(private readonly EntityRepository $orderRepository, private readonly EntityRepository $transactionRepository, private readonly EntityRepository $deliveryRepository, private readonly EventDispatcherInterface $eventDispatcher, private readonly BusinessEventCollector $businessEventCollector, private readonly EntityRepository $stateRepository)
  29.     {
  30.     }
  31.     public static function getSubscribedEvents(): array
  32.     {
  33.         return [
  34.             'state_machine.order.state_changed' => 'onOrderStateChange',
  35.             'state_machine.order_delivery.state_changed' => 'onOrderDeliveryStateChange',
  36.             'state_machine.order_transaction.state_changed' => 'onOrderTransactionStateChange',
  37.             BusinessEventCollectorEvent::NAME => 'onAddStateEvents',
  38.         ];
  39.     }
  40.     /**
  41.      * @throws OrderException
  42.      */
  43.     public function onOrderDeliveryStateChange(StateMachineStateChangeEvent $event): void
  44.     {
  45.         $orderDeliveryId $event->getTransition()->getEntityId();
  46.         $criteria = new Criteria([$orderDeliveryId]);
  47.         $criteria->addAssociation('order.orderCustomer');
  48.         $criteria->addAssociation('order.transactions');
  49.         /** @var OrderDeliveryEntity|null $orderDelivery */
  50.         $orderDelivery $this->deliveryRepository
  51.             ->search($criteria$event->getContext())
  52.             ->first();
  53.         if ($orderDelivery === null) {
  54.             throw OrderException::orderDeliveryNotFound($orderDeliveryId);
  55.         }
  56.         if ($orderDelivery->getOrder() === null) {
  57.             throw OrderException::orderDeliveryNotFound($orderDeliveryId);
  58.         }
  59.         $context $this->getContext($orderDelivery->getOrderId(), $event->getContext());
  60.         $order $this->getOrder($orderDelivery->getOrderId(), $context);
  61.         $this->dispatchEvent($event->getStateEventName(), $order$context);
  62.     }
  63.     /**
  64.      * @throws OrderException
  65.      */
  66.     public function onOrderTransactionStateChange(StateMachineStateChangeEvent $event): void
  67.     {
  68.         $orderTransactionId $event->getTransition()->getEntityId();
  69.         $criteria = new Criteria([$orderTransactionId]);
  70.         $criteria->addAssociation('paymentMethod');
  71.         $criteria->addAssociation('order.orderCustomer');
  72.         $criteria->addAssociation('order.transactions');
  73.         $orderTransaction $this->transactionRepository
  74.             ->search($criteria$event->getContext())
  75.             ->first();
  76.         if ($orderTransaction === null) {
  77.             throw OrderException::orderTransactionNotFound($orderTransactionId);
  78.         }
  79.         if ($orderTransaction->getPaymentMethod() === null) {
  80.             throw OrderException::orderTransactionNotFound($orderTransactionId);
  81.         }
  82.         if ($orderTransaction->getOrder() === null) {
  83.             throw OrderException::orderTransactionNotFound($orderTransactionId);
  84.         }
  85.         $context $this->getContext($orderTransaction->getOrderId(), $event->getContext());
  86.         $order $this->getOrder($orderTransaction->getOrderId(), $context);
  87.         $this->dispatchEvent($event->getStateEventName(), $order$context);
  88.     }
  89.     public function onOrderStateChange(StateMachineStateChangeEvent $event): void
  90.     {
  91.         $orderId $event->getTransition()->getEntityId();
  92.         $context $this->getContext($orderId$event->getContext());
  93.         $order $this->getOrder($orderId$context);
  94.         $this->dispatchEvent($event->getStateEventName(), $order$context);
  95.     }
  96.     public function onAddStateEvents(BusinessEventCollectorEvent $event): void
  97.     {
  98.         $context $event->getContext();
  99.         $collection $event->getCollection();
  100.         $criteria = new Criteria();
  101.         $criteria->addAssociation('stateMachine');
  102.         $states $this->stateRepository->search($criteria$context);
  103.         $sides = [
  104.             StateMachineStateChangeEvent::STATE_MACHINE_TRANSITION_SIDE_ENTER,
  105.             StateMachineStateChangeEvent::STATE_MACHINE_TRANSITION_SIDE_LEAVE,
  106.         ];
  107.         /** @var StateMachineStateEntity $state */
  108.         foreach ($states as $state) {
  109.             foreach ($sides as $side) {
  110.                 $machine $state->getStateMachine();
  111.                 if (!$machine) {
  112.                     continue;
  113.                 }
  114.                 $name implode('.', [
  115.                     $side,
  116.                     $machine->getTechnicalName(),
  117.                     $state->getTechnicalName(),
  118.                 ]);
  119.                 $definition $this->businessEventCollector->define(OrderStateMachineStateChangeEvent::class, $name);
  120.                 if (!$definition) {
  121.                     continue;
  122.                 }
  123.                 $collection->set($name$definition);
  124.             }
  125.         }
  126.     }
  127.     /**
  128.      * @throws OrderException
  129.      */
  130.     private function dispatchEvent(string $stateEventNameOrderEntity $orderContext $context): void
  131.     {
  132.         $this->eventDispatcher->dispatch(
  133.             new OrderStateMachineStateChangeEvent($stateEventName$order$context),
  134.             $stateEventName
  135.         );
  136.     }
  137.     private function getContext(string $orderIdContext $context): Context
  138.     {
  139.         $order $this->orderRepository->search(new Criteria([$orderId]), $context)->first();
  140.         if (!$order instanceof OrderEntity) {
  141.             throw OrderException::orderNotFound($orderId);
  142.         }
  143.         /** @var CashRoundingConfig $itemRounding */
  144.         $itemRounding $order->getItemRounding();
  145.         $orderContext = new Context(
  146.             $context->getSource(),
  147.             $order->getRuleIds() ?? [],
  148.             $order->getCurrencyId(),
  149.             array_values(array_unique(array_merge([$order->getLanguageId()], $context->getLanguageIdChain()))),
  150.             $context->getVersionId(),
  151.             $order->getCurrencyFactor(),
  152.             true,
  153.             $order->getTaxStatus(),
  154.             $itemRounding
  155.         );
  156.         $orderContext->addState(...$context->getStates());
  157.         $orderContext->addExtensions($context->getExtensions());
  158.         return $orderContext;
  159.     }
  160.     /**
  161.      * @throws OrderException
  162.      */
  163.     private function getOrder(string $orderIdContext $context): OrderEntity
  164.     {
  165.         $orderCriteria $this->getOrderCriteria($orderId);
  166.         $order $this->orderRepository
  167.             ->search($orderCriteria$context)
  168.             ->first();
  169.         if (!$order instanceof OrderEntity) {
  170.             throw OrderException::orderNotFound($orderId);
  171.         }
  172.         return $order;
  173.     }
  174.     private function getOrderCriteria(string $orderId): Criteria
  175.     {
  176.         $criteria = new Criteria([$orderId]);
  177.         $criteria->addAssociation('orderCustomer.salutation');
  178.         $criteria->addAssociation('orderCustomer.customer');
  179.         $criteria->addAssociation('stateMachineState');
  180.         $criteria->addAssociation('deliveries.shippingMethod');
  181.         $criteria->addAssociation('deliveries.shippingOrderAddress.country');
  182.         $criteria->addAssociation('deliveries.shippingOrderAddress.countryState');
  183.         $criteria->addAssociation('salesChannel');
  184.         $criteria->addAssociation('language.locale');
  185.         $criteria->addAssociation('transactions.paymentMethod');
  186.         $criteria->addAssociation('lineItems');
  187.         $criteria->addAssociation('lineItems.downloads.media');
  188.         $criteria->addAssociation('currency');
  189.         $criteria->addAssociation('addresses.country');
  190.         $criteria->addAssociation('addresses.countryState');
  191.         $criteria->addAssociation('tags');
  192.         $event = new OrderStateChangeCriteriaEvent($orderId$criteria);
  193.         $this->eventDispatcher->dispatch($event);
  194.         return $criteria;
  195.     }
  196. }