New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to start state machine with normal state coming from database? #114
Comments
I use an event listener which will store transition information in a history table, something like this
This will be run before every transition. State machine will always start with the state your object has when the machine is initialized. Just make sure your object has the appropriate state when you set it in your state machine. |
@realshadow : What is $repository Object? Is there any simple method or logic by that we can start with normal state easily? Like setCurrentState('StateName') method? |
@somnathmuluk I do not perfectly understand your request. What are you trying to do ? Persisting your state & transition graph in database ? Or just the inital state ? |
@yohang I just want to start State Machine with Normal State rather than initial state. And in database I am saving current state (i.e Normal state). So I wanted to check if any other state can be transited or not from normal state. |
@somnathmuluk Oh, I see. This is the "normal" use of Finite. If your stateful object has the state set before the state machine initialization, it'll work ! The common use of Finite is with Doctrine Entities / Document, which does this transparently at entity retrieving. |
Yohan, but tell me please, can I somehow specify the state with which I can initialize FSM without saving the whole object in the database, but only the name of the state? The object is very large in my opinion to be constantly transmitted over the network, but the name of the state is not. It would be ideal, as wrote @somnathmuluk here such method as setCurrentState ('StateName'). Thanks in advance. |
now I make a class extend the StateMachine;
now |
The current state is a string, so if you can save the string to your database, then when you set up your state machine you first load up the value from the database, then use the $initialBookingState = 'proposed'; // load this from the database
$document = new Document();
$loader = new \Finite\Loader\ArrayLoader([
'class' => 'Document',
'states' => [
'draft' => [
'type' => \Finite\State\StateInterface::TYPE_INITIAL,
'properties' => ['deletable' => true, 'editable' => true],
],
'proposed' => [
'type' => \Finite\State\StateInterface::TYPE_NORMAL,
'properties' => [],
],
'accepted' => [
'type' => \Finite\State\StateInterface::TYPE_FINAL,
'properties' => ['printable' => true],
],
],
'transitions' => [
'propose' => ['from' => ['draft'], 'to' => 'proposed'],
'accept' => ['from' => ['proposed'], 'to' => 'accepted'],
'reject' => ['from' => ['proposed'], 'to' => 'draft'],
],
]);
$document->setFiniteState($initialBookingState); // set our initial state here
$stateMachine = new \Finite\StateMachine\StateMachine($document);
$loader->load($stateMachine);
$stateMachine->initialize();
// Current state
var_dump($stateMachine->getCurrentState()->getName()); // proposed |
@BurningDog How do you update the document during transitions? The loading of the state is only a half of the answer. (Very nice example, btw.) |
@jkufner I wrap the state machine transition in a service, and call the service to do the transition. If the transition succeeds, then I update the database by calling StateMachineService::applyTransitionIfPossible($myEntity, 'my custom transition');
class StateMachineService
{
/**
* Attempts to apply the transition. If we can't, don't fail but return null.
*/
public static function applyTransitionIfPossible(MyEntity $myEntity, string $transition)
{
$stateMachine = $myEntity->getStateMachine();
if ($stateMachine->can($transition)) {
$stateMachine->apply($transition);
$myEntity->setBookingState($stateMachine->getCurrentState()->getName());
}
}
}
class MyEntity
{
// This entity is the one I care about knowing the state of by using the state machine.
// It saves the state in the database
private ?string $state = null;
private ?StateMachine $stateMachine = null;
// All sorts of other code
// ...
public function getState(): ?string
{
return $this->state;
}
/**
* Sets the new State. If it doesn't match the current state of the state machine, throw an error.
*
* @throws Exception
*/
public function setState(string $newState): self
{
if ($newState !== $this->getStateMachine()->getCurrentState()->getName()) {
throw new \Exception('The new state does not match the current state machine state');
}
$this->state = $newState;
return $this;
}
public function getStateMachine(): StateMachine
{
if (!$this->stateMachine) {
$this->stateMachine = StateMachineService::createStateMachine($this, $this->state);
}
return $this->stateMachine;
}
} In |
@BurningDog Thank you. So if I understand correctly, there is no encapsulation of the entity and the state machine to enforce the modelled behavior and deny any other modifications. |
@jkufner not quite. When saving the state to the database, you can implement that however you want to, but it's independent of Finite. In my implementation in Symfony 4.4 I've needed an entity (for database persistence) and a service (to link the entity state with the state machine state, and a few other helper functions). Other state machine libraries have wrappers for a particular framework. For instance, https://github.com/winzou/state-machine can be used in Laravel with https://github.com/sebdesign/laravel-state-machine and https://github.com/iben12/laravel-statable |
Is there any example which shows state machine starting with normal state?
Transitions can be affected by different users. I need to save all transitions in database. And state machine can be started with normal state (mid-level state). Any example how to save state in database and start with same state and then apply transition?
The text was updated successfully, but these errors were encountered: