Hi Bob, hi Sam,
Sam, perhaps this mail is usefull for you as well. I try to explain, how
defining a workflow and integrating in your application could work.
If you have some more questions, I ask you to subscribe to the
components list (if you didn't subscribed till now). Then others can
profit of the discussion as well.
> On Mi, 2007-07-18 at 02:05 -0700, bob stef wrote:
> > Hi,
> > my name is Bogdan Stefanescu and I'm doing an internship at eZ
> > Systems France in Lyon. I try to implement a Pet Store using eZ
> > Components and I have some problems using Workflow component. I try
> > to implement the workflow component in the paiement process. I have
> > the following steps:
> > 1. Validation of the personal data (order data);
> > 2. Validation of the checkbox with the "terms and
> > conditions";
> > 3. Validation of the credit card data
> > 4. Confirmation email
> > I understood the workflow patterns but i don't know how to
> > associate my steps and the action to the nodes.
> > If you can help me I will appreciate this very much.
Ok, here's how I would define this. This is no valid XML workflow
definition but only pseudo code. As I do not have the XML schema in my
head and as it is late, I'm lazy. Also, I do not quote attribute
values. Feel free to work with it and send the result - or if you have
problems, intermediary versions - to the components list as example :-)
<workflow>
<node type=start id=1>
<outNode id=3 />
</node>
<node type=end id=2 />
<!-- step 1: Validation of the personal data (order data) -->
<node type=simpleMerge id=3>
<outNode id=4 />
</node>
<node type=variableset id=4>
<variable name=executionTemplate>
<string>templateNameOfStep1.tpl</string>
</variable>
<outNode id=5 />
</node>
<node type=input id=5>
<variable name=var1>
<condition ... />
</variable>
...
<outNode id=6 />
</node>
<node type=action serviceObjectName="someServiceObject" id=6>
<!--
the service object can do some additional checking or
perform some business logic like sending mail, checking
credit card, ...
parameters and return value cf. notes below
let's assume the service object sets a return variable
called 'soResult'
-->
<outNode id=7>
</node>
<node type=exclusiveChoice id=7>
<condition type=variable name=soResult>
<conditon type=IsFalse>
<outNode id=4>
</condtion>
<condition type=variable name=soResult>
<conditon type=IsTrue>
<outNode id=8>
</condtion>
</node>
<!-- step 2 -->
<node type=simpleMerge>
<outNode id=9 />
</node>
<node type=variableSet>
<variable name=executionTemplate>
<string>templateNameOfTemplateForStep2</string>
</variable>
</utNode id=10 />
</node>
...
<!-- the other steps are more or less the same schema -->
<!-- ending -->
<node type=variableSet id=100>
<variable name=executionTemplate>
<string>templateNameOfFinishingSuccessfully</string>
</variable>
<outNode id=2 />
</node>
</workflow>
Here some notes:
- conditions for input variables:
We wrote our own XML reader extending the definition_storage/xml.php.
There is only one additional thing: an condition that takes some
parameters and does the validation against our global input filter
implementation. Our input filter condition expects something like a
callback (and it's parameters). This is much more flexible than the
conditions provided via the component. And it enables to use some
existing input filter or very specific tests (like e.g. uniqueness of a
login in the database).
- action nodes and service objects
We tried to keep the service objects independent of the workflow
execution state variables. Therefore the parameters of a service object
often look like (I guess it is not called array but somehow
<arguments> - I'm not sure at the moment.)
<array>
<element key=soInternalVariableName1>
<string>$executionVarName</string>
<element>
<element key=soInternalVariableName2>
<string>fixedValue</string>
<element>
<element key=soInternalVariableName3>
<string>$object->variable</string>
<element>
<element key=soInternalVariableName4>
<string>$object->getVariable()</string>
<element>
...
<element key=return>
<string>returnVariableName</string>
</element>
</array>
In the service object you can assign the internal variables via the
appropriate values, like:
$assigned1 = this->configuration['soInternalVariableName1'];
if ( '$' === substr( $assigned1, 0, 1 ) )
{
$internal1 = $execution->getVariable( substr( $assigned1, 1 ) );
}
else
{
$internal1 = $assigned1;
}
The first variable would be replaced via the execution
variable 'executionVarName' and the second one gets the fixed
value 'fixedValue'. The variables 3 and 4 could implement some variable
or method access to an object that's in the execution variables (not
shown in the PHP code above).
Of course you don't want to write this for each variable but you put
this in a general method...
The result of a service object (success or failure) could be transfered
back to the execution state variables like:
$execution->setVariable( $this->configuration['return'], $result );
If necessary, this can be evaluated in the workflow as shown in the
branching...
- ending the workflow (node 100 above):
Sometimes it's usefull if the workflow takes care itself (via a service
object) to store the important data/workflow execution state variables
in (e.g.) the database. This allowes the controller to know nothing
about the workflow. (This is not shown above.) For general cases we
implemented a service object that has a mapping like this:
<array>
<element key=tableName1>
<array>
<element key=colName1>
<string>executionVarName1</string>
</element>
...
</array>
</element>
<elemet key=tablesName2>
...
</element>
</array>
- Integration in controller:
The controller checks if an execution id for the user and the
appropriate workflow exists. If not, it starts a new workflow (what
will resume the workflow in the first input node), in the other case it
triggers the workflow execution with some input data. After
resuming/ending, the controller can fetch the template name:
$tplName = $execution->getVariable( 'executionTemplate' );
and display it to the user. So the controller knows nothing: no
information about input validatio, no information about templates to
show, ...
Of course oure setup is a little bit more complex/advanced - I only
wanted to show the basic ideas. E.g. we have a Utils class that starts
or triggers the workflow. The triggering also prepares some error
messages for the controller if validation fails. Additionally we added
cancelling of workflows, timing out of nodes and going back to an
earlier input node to the workflow engine (this is: cancelling,
timeouts and going to an earlier step in the execution is not possible
with Sebastian's implementation).
I hope, this gave a short impression, how you can work with the workflow
component. If you have questions, if I did some errors (it's late and I
didn't check what I've written again) or if you do not know how to
translate my pseudo code to valid XML definition - just ask!
> > I thinck there is an error in the example given in the tutorial:
> > "creating a simple workflow programmatically" instead of $true and
> > $false I thinck it should be $trueNode and $falseNode.
> > 46. $merge->addInNode( $true );
> > 47. $merge->addInNode( $false );
At the moment I do not have the time to check the docs. I guess this is
Sebastian's part. :-)
But in general, I prefer defining a workflow via XML - I find it much
simpler to keep track of the whole thing. It is more "chronological"
when defining. E.g. if you want to define a conditional branch via PHP,
you first have to define the outgoing nodes as you need to pass them to
the branching node (via conditional out-nodes). In XML you only need to
keep track of the node ids. For me, that's much faster and I had less
errors. (But there may be situations when generating a workflow
definition via PHP is more appropriate, e.g. inside a workflow editor.)
Have a nice day
Thomas
--
Components mailing list
[email protected]
http://lists.ez.no/mailman/listinfo/components