vendor/shopware/core/Content/Flow/Dispatching/Action/SetOrderStateAction.php line 89

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Flow\Dispatching\Action;
  3. use Doctrine\DBAL\Connection;
  4. use Psr\Log\LoggerInterface;
  5. use Shopware\Core\Checkout\Order\SalesChannel\OrderService;
  6. use Shopware\Core\Content\Flow\Dispatching\DelayableAction;
  7. use Shopware\Core\Content\Flow\Dispatching\StorableFlow;
  8. use Shopware\Core\Defaults;
  9. use Shopware\Core\Framework\Context;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Dbal\EntityDefinitionQueryHelper;
  11. use Shopware\Core\Framework\Event\FlowEvent;
  12. use Shopware\Core\Framework\Event\OrderAware;
  13. use Shopware\Core\Framework\Feature;
  14. use Shopware\Core\Framework\Log\Package;
  15. use Shopware\Core\Framework\ShopwareHttpException;
  16. use Shopware\Core\Framework\Uuid\Uuid;
  17. use Shopware\Core\System\StateMachine\Exception\IllegalTransitionException;
  18. use Shopware\Core\System\StateMachine\Exception\StateMachineNotFoundException;
  19. use Symfony\Component\HttpFoundation\ParameterBag;
  20. /**
  21.  * @deprecated tag:v6.5.0 - reason:remove-subscriber - FlowActions won't be executed over the event system anymore,
  22.  * therefore the actions won't implement the EventSubscriberInterface anymore.
  23.  */
  24. #[Package('business-ops')]
  25. class SetOrderStateAction extends FlowAction implements DelayableAction
  26. {
  27.     public const FORCE_TRANSITION = 'force_transition';
  28.     private const ORDER = 'order';
  29.     private const ORDER_DELIVERY = 'order_delivery';
  30.     private const ORDER_TRANSACTION = 'order_transaction';
  31.     private Connection $connection;
  32.     private LoggerInterface $logger;
  33.     private OrderService $orderService;
  34.     /**
  35.      * @internal
  36.      */
  37.     public function __construct(
  38.         Connection $connection,
  39.         LoggerInterface $logger,
  40.         OrderService $orderService
  41.     ) {
  42.         $this->connection = $connection;
  43.         $this->logger = $logger;
  44.         $this->orderService = $orderService;
  45.     }
  46.     public static function getName(): string
  47.     {
  48.         return 'action.set.order.state';
  49.     }
  50.     /**
  51.      * @deprecated tag:v6.5.0 - reason:remove-subscriber - Will be removed
  52.      *
  53.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  54.      */
  55.     public static function getSubscribedEvents()
  56.     {
  57.         if (Feature::isActive('v6.5.0.0')) {
  58.             return [];
  59.         }
  60.         return [
  61.             self::getName() => 'handle',
  62.         ];
  63.     }
  64.     /**
  65.      * @return array<int, string>
  66.      */
  67.     public function requirements(): array
  68.     {
  69.         return [OrderAware::class];
  70.     }
  71.     /**
  72.      * @deprecated tag:v6.5.0 Will be removed, implement handleFlow instead
  73.      */
  74.     public function handle(FlowEvent $event): void
  75.     {
  76.         Feature::triggerDeprecationOrThrow(
  77.             'v6.5.0.0',
  78.             Feature::deprecatedMethodMessage(__CLASS__, __METHOD__, 'v6.5.0.0')
  79.         );
  80.         $baseEvent = $event->getEvent();
  81.         if (!$baseEvent instanceof OrderAware) {
  82.             return;
  83.         }
  84.         $this->update($baseEvent->getContext(), $event->getConfig(), $baseEvent->getOrderId());
  85.     }
  86.     public function handleFlow(StorableFlow $flow): void
  87.     {
  88.         if (!$flow->hasStore(OrderAware::ORDER_ID)) {
  89.             return;
  90.         }
  91.         $this->update($flow->getContext(), $flow->getConfig(), $flow->getStore(OrderAware::ORDER_ID));
  92.     }
  93.     /**
  94.      * @param array<string, mixed> $config
  95.      */
  96.     private function update(Context $context, array $config, string $orderId): void
  97.     {
  98.         if (empty($config)) {
  99.             return;
  100.         }
  101.         if ($config[self::FORCE_TRANSITION] ?? false) {
  102.             $context->addState(self::FORCE_TRANSITION);
  103.         }
  104.         $this->connection->beginTransaction();
  105.         try {
  106.             $transitions = array_filter([
  107.                 self::ORDER => $config[self::ORDER] ?? null,
  108.                 self::ORDER_DELIVERY => $config[self::ORDER_DELIVERY] ?? null,
  109.                 self::ORDER_TRANSACTION => $config[self::ORDER_TRANSACTION] ?? null,
  110.             ]);
  111.             foreach ($transitions as $machine => $toPlace) {
  112.                 $this->transitState((string) $machine, $orderId, (string) $toPlace, $context);
  113.             }
  114.             $this->connection->commit();
  115.         } catch (ShopwareHttpException $e) {
  116.             $this->connection->rollBack();
  117.             $this->logger->error($e->getMessage());
  118.         } finally {
  119.             $context->removeState(self::FORCE_TRANSITION);
  120.         }
  121.     }
  122.     /**
  123.      * @throws IllegalTransitionException
  124.      * @throws StateMachineNotFoundException
  125.      */
  126.     private function transitState(string $machine, string $orderId, string $toPlace, Context $context): void
  127.     {
  128.         if (!$toPlace) {
  129.             return;
  130.         }
  131.         $data = new ParameterBag();
  132.         $machineId = $machine === self::ORDER ? $orderId : $this->getMachineId($machine, $orderId);
  133.         if (!$machineId) {
  134.             throw new StateMachineNotFoundException($machine);
  135.         }
  136.         $actionName = $this->getAvailableActionName($machine, $machineId, $toPlace);
  137.         if (!$actionName) {
  138.             $actionName = $toPlace;
  139.         }
  140.         switch ($machine) {
  141.             case self::ORDER:
  142.                 $this->orderService->orderStateTransition($orderId, $actionName, $data, $context);
  143.                 return;
  144.             case self::ORDER_DELIVERY:
  145.                 $this->orderService->orderDeliveryStateTransition($machineId, $actionName, $data, $context);
  146.                 return;
  147.             case self::ORDER_TRANSACTION:
  148.                 $this->orderService->orderTransactionStateTransition($machineId, $actionName, $data, $context);
  149.                 return;
  150.             default:
  151.                 throw new StateMachineNotFoundException($machine);
  152.         }
  153.     }
  154.     private function getMachineId(string $machine, string $orderId): ?string
  155.     {
  156.         return $this->connection->fetchOne(
  157.             'SELECT LOWER(HEX(id)) FROM ' . $machine . ' WHERE order_id = :id AND version_id = :version ORDER BY created_at DESC',
  158.             [
  159.                 'id' => Uuid::fromHexToBytes($orderId),
  160.                 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION),
  161.             ]
  162.         ) ?: null;
  163.     }
  164.     private function getAvailableActionName(string $machine, string $machineId, string $toPlace): ?string
  165.     {
  166.         $actionName = $this->connection->fetchOne(
  167.             'SELECT action_name FROM state_machine_transition WHERE from_state_id = :fromStateId AND to_state_id = :toPlaceId',
  168.             [
  169.                 'fromStateId' => $this->getFromPlaceId($machine, $machineId),
  170.                 'toPlaceId' => $this->getToPlaceId($toPlace, $machine),
  171.             ]
  172.         );
  173.         return $actionName ?: null;
  174.     }
  175.     private function getToPlaceId(string $toPlace, string $machine): ?string
  176.     {
  177.         $id = $this->connection->fetchOne(
  178.             'SELECT id FROM state_machine_state WHERE technical_name = :toPlace AND state_machine_id = :stateMachineId',
  179.             [
  180.                 'toPlace' => $toPlace,
  181.                 'stateMachineId' => $this->getStateMachineId($machine),
  182.             ]
  183.         );
  184.         return $id ?: null;
  185.     }
  186.     private function getFromPlaceId(string $machine, string $machineId): ?string
  187.     {
  188.         $escaped = EntityDefinitionQueryHelper::escape($machine);
  189.         $id = $this->connection->fetchOne(
  190.             'SELECT state_id FROM ' . $escaped . 'WHERE id = :id',
  191.             [
  192.                 'id' => Uuid::fromHexToBytes($machineId),
  193.             ]
  194.         );
  195.         return $id ?: null;
  196.     }
  197.     private function getStateMachineId(string $machine): ?string
  198.     {
  199.         $id = $this->connection->fetchOne(
  200.             'SELECT id FROM state_machine WHERE technical_name = :technicalName',
  201.             [
  202.                 'technicalName' => $machine . '.state',
  203.             ]
  204.         );
  205.         return $id ?: null;
  206.     }
  207. }