/** * @param SimpleXMLElement $xml * @param StateMachine $sm * @return static */ public static function fromXml(SimpleXMLElement $xml, StateMachine $sm) { $rVal = new static(); // Attach the statemachine $rVal->sm = $sm; // Value and Label $rVal->value = (string) $xml->attributes()->value; $rVal->label = (string) $xml->attributes()->label; // Load events foreach ($xml->event as $eventXml) { $rVal->events[] = Event::fromXml($eventXml, $sm, $rVal); } // Load timeouts foreach ($xml->timeout as $timeoutXml) { $rVal->timeouts[] = Timeout::fromXml($timeoutXml, $sm, $rVal); } // Enter Commands XML fragments $enterXml = !empty($xml->enter) ? $xml->enter[0]->command : []; foreach ($enterXml as $item) { $rVal->enter[] = $item; } // Exit Commands XML fragments $exitXml = !empty($xml->exit) ? $xml->exit[0]->command : []; foreach ($exitXml as $item) { $rVal->exit[] = $item; } $rVal->isInitial = $sm->getInitialStateValue() === $rVal->value; $rVal->isFinal = empty($rVal->events); return $rVal; }
/** * @param StateMachineEvent $event * @param StateMachineContext $context * @return bool * @throws Exception * @throws \yii\db\Exception */ public function transition(StateMachineEvent $event, StateMachineContext $context) { // Let the context know which event triggered the transition $context->setEvent($event); /** @var yii\db\Transaction|false $txn */ $txn = $this->useTransactions ? $context->getModel()->getDb()->beginTransaction() : false; try { // Leaving current state... foreach ($event->getState()->getExitCommands() as $command) { if (!$command->execute($context)) { $txn && $txn->rollBack(); return false; } } // Cleanup old timeouts Timeout::cleanUp($context); // Entering new state... $context->getModel()->{$context->getAttr()} = $event->getTarget(); foreach ($event->getTargetState()->getEnterCommands() as $command) { if (!$command->execute($context)) { $txn && $txn->rollBack(); return false; } } // Register the new timeouts foreach ($event->getTargetState()->getTimeOuts() as $timeout) { $timeout->register($context); } // Persist the context's model data $context->getModel()->save(); // Update Journal - if applicable if ($this->modelJournal) { $journal = $this->modelJournal; /** @var StateMachineJournal $journal */ $journal::nu($context, $event); } $txn && $txn->commit(); // transition completed successfully return true; } catch (Exception $e) { $txn && $txn->rollBack(); $context->attachException($e); throw $e; } }