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

Reply via email to