diff --git a/composer.json b/composer.json index 9a1f7903..063821be 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,7 @@ "license": "proprietary", "require": { "php": ">=8.1", + "ext-simplexml": "*", "spryker/acl-merchant-portal-extension": "^1.0.0", "spryker/decimal-object": "^1.0.0", "spryker/error-handler": "^2.1.0", diff --git a/src/Spryker/Shared/Oms/OmsConstants.php b/src/Spryker/Shared/Oms/OmsConstants.php index 0858d8bf..52bc9ac8 100644 --- a/src/Spryker/Shared/Oms/OmsConstants.php +++ b/src/Spryker/Shared/Oms/OmsConstants.php @@ -41,4 +41,14 @@ interface OmsConstants * @var string */ public const ENABLE_OMS_TRANSITION_LOG = 'OMS:ENABLE_OMS_TRANSITION_LOG'; + + /** + * Specification: + * - Defines where to store cached processes. + * + * @api + * + * @var string + */ + public const PROCESS_CACHE_PATH = 'OMS:PROCESS_CACHE_PATH'; } diff --git a/src/Spryker/Zed/Oms/Business/OmsBusinessFactory.php b/src/Spryker/Zed/Oms/Business/OmsBusinessFactory.php index 8dbd57d5..7483b7f4 100644 --- a/src/Spryker/Zed/Oms/Business/OmsBusinessFactory.php +++ b/src/Spryker/Zed/Oms/Business/OmsBusinessFactory.php @@ -38,6 +38,8 @@ use Spryker\Zed\Oms\Business\Process\Process; use Spryker\Zed\Oms\Business\Process\State; use Spryker\Zed\Oms\Business\Process\Transition; +use Spryker\Zed\Oms\Business\Reader\ProcessCacheReader; +use Spryker\Zed\Oms\Business\Reader\ProcessCacheReaderInterface; use Spryker\Zed\Oms\Business\Reader\ReservationReader; use Spryker\Zed\Oms\Business\Reader\ReservationReaderInterface; use Spryker\Zed\Oms\Business\Reader\StateMachineReader; @@ -54,6 +56,8 @@ use Spryker\Zed\Oms\Business\Util\TimeoutProcessorCollection; use Spryker\Zed\Oms\Business\Util\TimeoutProcessorCollectionInterface; use Spryker\Zed\Oms\Business\Util\TransitionLog; +use Spryker\Zed\Oms\Business\Writer\ProcessCacheWriter; +use Spryker\Zed\Oms\Business\Writer\ProcessCacheWriterInterface; use Spryker\Zed\Oms\OmsDependencyProvider; /** @@ -128,6 +132,8 @@ public function createOrderStateMachineBuilder() $this->createProcessProcess(), $this->getConfig()->getProcessDefinitionLocation(), $this->getConfig()->getSubProcessPrefixDelimiter(), + $this->createProcessCacheReader(), + $this->createProcessCacheWriter(), ); } @@ -598,4 +604,20 @@ public function getOmsEventTriggeredListenerPlugins(): array { return $this->getProvidedDependency(OmsDependencyProvider::PLUGINS_OMS_EVENT_TRIGGERED_LISTENER); } + + /** + * @return \Spryker\Zed\Oms\Business\Reader\ProcessCacheReaderInterface + */ + public function createProcessCacheReader(): ProcessCacheReaderInterface + { + return new ProcessCacheReader($this->getConfig()); + } + + /** + * @return \Spryker\Zed\Oms\Business\Writer\ProcessCacheWriterInterface + */ + public function createProcessCacheWriter(): ProcessCacheWriterInterface + { + return new ProcessCacheWriter($this->getConfig()); + } } diff --git a/src/Spryker/Zed/Oms/Business/OrderStateMachine/Builder.php b/src/Spryker/Zed/Oms/Business/OrderStateMachine/Builder.php index 5329bd9a..4faa338f 100644 --- a/src/Spryker/Zed/Oms/Business/OrderStateMachine/Builder.php +++ b/src/Spryker/Zed/Oms/Business/OrderStateMachine/Builder.php @@ -14,6 +14,8 @@ use Spryker\Zed\Oms\Business\Process\ProcessInterface; use Spryker\Zed\Oms\Business\Process\StateInterface; use Spryker\Zed\Oms\Business\Process\TransitionInterface; +use Spryker\Zed\Oms\Business\Reader\ProcessCacheReaderInterface; +use Spryker\Zed\Oms\Business\Writer\ProcessCacheWriterInterface; use Symfony\Component\Finder\Finder as SymfonyFinder; class Builder implements BuilderInterface @@ -58,6 +60,10 @@ class Builder implements BuilderInterface */ protected $subProcessPrefixDelimiter; + protected ?ProcessCacheReaderInterface $processCacheReader; + + protected ?ProcessCacheWriterInterface $processCacheWriter; + /** * @param \Spryker\Zed\Oms\Business\Process\EventInterface $event * @param \Spryker\Zed\Oms\Business\Process\StateInterface $state @@ -65,6 +71,8 @@ class Builder implements BuilderInterface * @param \Spryker\Zed\Oms\Business\Process\ProcessInterface $process * @param array|string $processDefinitionLocation * @param string $subProcessPrefixDelimiter + * @param \Spryker\Zed\Oms\Business\Reader\ProcessCacheReaderInterface|null $processCacheReader + * @param \Spryker\Zed\Oms\Business\Writer\ProcessCacheWriterInterface|null $processCacheWriter */ public function __construct( EventInterface $event, @@ -72,13 +80,17 @@ public function __construct( TransitionInterface $transition, ProcessInterface $process, $processDefinitionLocation, - $subProcessPrefixDelimiter = ' - ' + $subProcessPrefixDelimiter = ' - ', + ?ProcessCacheReaderInterface $processCacheReader = null, + ?ProcessCacheWriterInterface $processCacheWriter = null ) { $this->event = $event; $this->state = $state; $this->transition = $transition; $this->process = $process; $this->subProcessPrefixDelimiter = $subProcessPrefixDelimiter; + $this->processCacheReader = $processCacheReader; + $this->processCacheWriter = $processCacheWriter; $this->setProcessDefinitionLocation($processDefinitionLocation); } @@ -90,28 +102,54 @@ public function __construct( */ public function createProcess($processName) { - if (!isset(static::$processBuffer[$processName])) { - $this->rootElement = $this->loadXmlFromProcessName($processName); + if (isset(static::$processBuffer[$processName])) { + return static::$processBuffer[$processName]; + } + + if ($this->processCacheReader && $this->processCacheReader->hasProcess($processName)) { + static::$processBuffer[$processName] = $this->processCacheReader->getProcess($processName); - $this->mergeSubProcessFiles(); + return static::$processBuffer[$processName]; + } - /** @var array<\Spryker\Zed\Oms\Business\Process\ProcessInterface> $processMap */ - $processMap = []; + $mainProcess = $this->createMainProcess($processName); - [$processMap, $mainProcess] = $this->createSubProcess($processMap); + static::$processBuffer[$processName] = $mainProcess; - $stateToProcessMap = $this->createStates($processMap); + if ($this->processCacheWriter) { + $this->processCacheWriter->cacheProcess($mainProcess, $processName); + } - $this->createSubProcesses($processMap); + return static::$processBuffer[$processName]; + } - $eventMap = $this->createEvents(); + /** + * @param string $processName + * + * @return \Spryker\Zed\Oms\Business\Process\ProcessInterface + */ + protected function createMainProcess(string $processName): ProcessInterface + { + $this->rootElement = $this->loadXmlFromProcessName($processName); - $this->createTransitions($stateToProcessMap, $processMap, $eventMap); + $this->mergeSubProcessFiles(); - static::$processBuffer[$processName] = $mainProcess; - } + /** @var array<\Spryker\Zed\Oms\Business\Process\ProcessInterface> $processMap */ + $processMap = []; - return static::$processBuffer[$processName]; + [$processMap, $mainProcess] = $this->createSubProcess($processMap); + + $stateToProcessMap = $this->createStates($processMap); + + $this->createSubProcesses($processMap); + + $eventMap = $this->createEvents(); + + $this->createTransitions($stateToProcessMap, $processMap, $eventMap); + + $this->precalculateProcess($mainProcess); + + return $mainProcess; } /** @@ -544,4 +582,22 @@ protected function createSubProcessPathPattern($fileName) { return '/\b' . preg_quote(dirname($fileName), '/') . '\b/'; } + + /** + * @param \Spryker\Zed\Oms\Business\Process\ProcessInterface $process + * + * @return void + */ + protected function precalculateProcess(ProcessInterface $process): void + { + $allStates = $process->getAllStates(); + foreach ($allStates as $state) { + $process->getStateFromAllProcesses($state->getName()); + } + $process->getAllReservedStates(); + $process->getAllTransitions(); + $process->getAllTransitionsWithoutEvent(); + $process->getManualEvents(); + $process->getManualEventsBySource(); + } } diff --git a/src/Spryker/Zed/Oms/Business/Process/Process.php b/src/Spryker/Zed/Oms/Business/Process/Process.php index 591767c7..f08535c5 100644 --- a/src/Spryker/Zed/Oms/Business/Process/Process.php +++ b/src/Spryker/Zed/Oms/Business/Process/Process.php @@ -47,6 +47,41 @@ class Process implements ProcessInterface */ protected $subProcesses = []; + /** + * @var array + */ + protected mixed $processStates = []; + + /** + * @var array<\Spryker\Zed\Oms\Business\Process\StateInterface> + */ + protected mixed $allStates = []; + + /** + * @var array<\Spryker\Zed\Oms\Business\Process\StateInterface> + */ + protected array $allReservedStates = []; + + /** + * @var array<\Spryker\Zed\Oms\Business\Process\TransitionInterface> + */ + protected array $allTransitions = []; + + /** + * @var array<\Spryker\Zed\Oms\Business\Process\TransitionInterface> + */ + protected array $allTransitionsWithoutEvent = []; + + /** + * @var array<\Spryker\Zed\Oms\Business\Process\EventInterface> + */ + protected array $manualEvents = []; + + /** + * @var array> + */ + protected array $manualEventsBySource = []; + /** * @param \Spryker\Zed\Oms\Business\Util\DrawerInterface $drawer */ @@ -188,10 +223,16 @@ public function hasState($stateId) */ public function getStateFromAllProcesses($stateId) { + if (isset($this->processStates[$stateId])) { + return $this->processStates[$stateId]; + } + $processes = $this->getAllProcesses(); foreach ($processes as $process) { if ($process->hasState($stateId)) { - return $process->getState($stateId); + $this->processStates[$stateId] = $process->getState($stateId); + + return $this->processStates[$stateId]; } } @@ -255,6 +296,10 @@ public function hasTransitions() */ public function getAllStates() { + if ($this->allStates) { + return $this->allStates; + } + $states = []; if ($this->hasStates()) { $states = $this->getStates(); @@ -267,7 +312,7 @@ public function getAllStates() } } - return $states; + return $this->allStates = $states; } /** @@ -275,6 +320,10 @@ public function getAllStates() */ public function getAllReservedStates() { + if ($this->allReservedStates) { + return $this->allReservedStates; + } + $reservedStates = []; $states = $this->getAllStates(); foreach ($states as $state) { @@ -283,7 +332,7 @@ public function getAllReservedStates() } } - return $reservedStates; + return $this->allReservedStates = $reservedStates; } /** @@ -291,6 +340,10 @@ public function getAllReservedStates() */ public function getAllTransitions() { + if ($this->allTransitions) { + return $this->allTransitions; + } + $transitions = []; if ($this->hasTransitions()) { $transitions = $this->getTransitions(); @@ -301,7 +354,7 @@ public function getAllTransitions() } } - return $transitions; + return $this->allTransitions = $transitions; } /** @@ -309,6 +362,10 @@ public function getAllTransitions() */ public function getAllTransitionsWithoutEvent() { + if ($this->allTransitionsWithoutEvent) { + return $this->allTransitionsWithoutEvent; + } + $transitions = []; $allTransitions = $this->getAllTransitions(); foreach ($allTransitions as $transition) { @@ -317,7 +374,7 @@ public function getAllTransitionsWithoutEvent() } } - return $transitions; + return $this->allTransitionsWithoutEvent = $transitions; } /** @@ -327,6 +384,10 @@ public function getAllTransitionsWithoutEvent() */ public function getManualEvents() { + if ($this->manualEvents) { + return $this->manualEvents; + } + $manuallyExecutableEventList = []; $transitions = $this->getAllTransitions(); foreach ($transitions as $transition) { @@ -338,7 +399,7 @@ public function getManualEvents() } } - return $manuallyExecutableEventList; + return $this->manualEvents = $manuallyExecutableEventList; } /** @@ -346,6 +407,10 @@ public function getManualEvents() */ public function getManualEventsBySource() { + if ($this->manualEventsBySource) { + return $this->manualEventsBySource; + } + $events = $this->getManualEvents(); $eventsBySource = []; @@ -362,7 +427,7 @@ public function getManualEventsBySource() } } - return $eventsBySource; + return $this->manualEventsBySource = $eventsBySource; } /** diff --git a/src/Spryker/Zed/Oms/Business/Reader/ProcessCacheReader.php b/src/Spryker/Zed/Oms/Business/Reader/ProcessCacheReader.php new file mode 100644 index 00000000..23c4ffd9 --- /dev/null +++ b/src/Spryker/Zed/Oms/Business/Reader/ProcessCacheReader.php @@ -0,0 +1,57 @@ +omsConfig = $omsConfig; + } + + /** + * @param string $processName + * + * @return bool + */ + public function hasProcess(string $processName): bool + { + return file_exists($this->getFullFilename($processName)); + } + + /** + * @param string $processName + * + * @return \Spryker\Zed\Oms\Business\Process\ProcessInterface + */ + public function getProcess(string $processName): ProcessInterface + { + /** @var \Spryker\Zed\Oms\Business\Process\ProcessInterface $process */ + $process = unserialize(file_get_contents($this->getFullFilename($processName))); + + return $process; + } + + /** + * @param string $processName + * + * @return string + */ + protected function getFullFilename(string $processName): string + { + return $this->omsConfig->getProcessCachePath() . $processName; + } +} diff --git a/src/Spryker/Zed/Oms/Business/Reader/ProcessCacheReaderInterface.php b/src/Spryker/Zed/Oms/Business/Reader/ProcessCacheReaderInterface.php new file mode 100644 index 00000000..2e473da9 --- /dev/null +++ b/src/Spryker/Zed/Oms/Business/Reader/ProcessCacheReaderInterface.php @@ -0,0 +1,27 @@ +omsConfig = $omsConfig; + } + + /** + * @param string $processName + * + * @return bool + */ + public function hasProcess(string $processName): bool + { + return file_exists($this->getFullFilename($processName)); + } + + /** + * @param \Spryker\Zed\Oms\Business\Process\ProcessInterface $process + * @param string|null $processName + * + * @return string + */ + public function cacheProcess(ProcessInterface $process, ?string $processName = null): string + { + $this->createCacheDirectory(); + + if (!$processName) { + $processName = $process->getName(); + } + + file_put_contents($this->getFullFilename($processName), serialize($process)); + + return $this->getFullFilename($processName); + } + + /** + * @param string $processName + * + * @return string + */ + protected function getFullFilename(string $processName): string + { + return $this->omsConfig->getProcessCachePath() . $processName; + } + + /** + * @return void + */ + protected function createCacheDirectory(): void + { + if (file_exists($this->omsConfig->getProcessCachePath())) { + return; + } + + mkdir($this->omsConfig->getProcessCachePath(), 0777, true); + } +} diff --git a/src/Spryker/Zed/Oms/Business/Writer/ProcessCacheWriterInterface.php b/src/Spryker/Zed/Oms/Business/Writer/ProcessCacheWriterInterface.php new file mode 100644 index 00000000..a80c2a2a --- /dev/null +++ b/src/Spryker/Zed/Oms/Business/Writer/ProcessCacheWriterInterface.php @@ -0,0 +1,28 @@ +setName(static::COMMAND_NAME) + ->setDescription(static::COMMAND_DESCRIPTION); + + parent::configure(); + } + + /** + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->getFacade()->getProcesses(); + + return static::CODE_SUCCESS; + } +} diff --git a/src/Spryker/Zed/Oms/OmsConfig.php b/src/Spryker/Zed/Oms/OmsConfig.php index da268d7e..068408db 100644 --- a/src/Spryker/Zed/Oms/OmsConfig.php +++ b/src/Spryker/Zed/Oms/OmsConfig.php @@ -171,4 +171,20 @@ public function getOrderFieldsForOrderStatusChangedMessage(): array OrderTransfer::ORDER_REFERENCE => OrderStatusChangedTransfer::ORDER_REFERENCE, ]; } + + /** + * Specification: + * - Defines where to store cached processes. + * + * @api + * + * @return string + */ + public function getProcessCachePath(): string + { + return $this->get( + OmsConstants::PROCESS_CACHE_PATH, + APPLICATION_ROOT_DIR . '/data/cache/oms/process/', + ); + } }