A Workflow Based Multi-Form Approach for Zend Framework
Zend Framework (ZF) is a great application framework to build your website. Regardless of what site you are building – simple or complex – ZF will be your friend. However, ZF is not perfect and there are still some things that I really miss, for instance, an easy method to build workflow based multi-forms.
Warning! I need your input!
Please be warned: currently, I am thinking about the design of such a workflow based multiform approach. I didn’t start implementing yet! Everything that I post in this article is only a proposal or draft version and most of all it is a RFC (Requests for Comments)!
Hence, I really would appreciate your input. Please let me know, what you think about this approach…
What and Why?
What the heck do I mean with “Workflow Based Multi-Form Approach for Zend Framework”? Well, actually, I would like to have an easy method to define a set of forms and how they are linked up regarding their control- and data flow. Let me give you a simple example. Imagine a web shop where users can buy [insert your favorite gadget here]. After they selected whatever they want to buy, they click on a “buy now” button.
As soon as they clicked on this button, a simple workflow starts: first, the user has to enter some personal data (name, address, etc). After that, another form will popup where she can select the method of payment (credit card or bank transfer). In case she selects credit card, the next form will collect all the necessary credit card data; if she selects bank transfer the next form will collect all the necessary bank account details. The last step will show all the details and ask here to confirm the purchase.
In this simple example, we would need five different forms plus a method to define the logic. Of course, you could simple use the actions of your controller to implement the logic – and in this simple example, everything else would probably be an overhead. However, if you’ll have to deal with a whole bunch of such workflows there should be a more convenient (and stable) way to deal with this.
How?
The basic idea is to specify all your workflows using an XML file. The most important requirements are:
- The system has to be able to deal with several workflows
- Each workflow consists of a setup section and one or more forms
- Forms will not be specified in the XML file, instead you should be able to (re)use your Zend_Form classes
- Each form may consist of several submit buttons
- For each submit button you may specify:
- An action which will be triggered if the user clicks on the button. Each specified “on-submit-action” may consist of a condition, which will allow you to define something like: if user entered value A then trigger action X
- A form, which will be the next form in this workflow. Each specified “on-submit-form” may consist of a condition, which will allow you to define something like: if user entered value A then trigger action X
The XSD
If you would like to have a look at the XSD you may download the XML Schema Definition here… (please, right click on the link and select “Save Target As…”)
An XML Example
The XML corresponding to our example described above, would look like this:
<?xml version="1.0"?>
<mffConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.web-punk.com/mff.xsd">
<workflows>
<workflow id="firstwf">
<setup>
<!-- Path where your forms can be found. -->
<!-- If not set, default will be used: -->
<!-- APPLICATION_PATH/application/models/Form -->
<formpath>P:\Path\to\your\Forms</formpath>
<!-- DEBUG: not used yet -->
<debug>true</debug>
</setup>
<forms>
<form id="personalData">
<!-- class name of the form. If not set, -->
<!-- default = Model_Form_[id] will be used -->
<className>Model_Form_persData</className>
<!-- Validate form on leave? Default = true -->
<validateOnLeave>true</validateOnLeave>
<buttons>
<button id="next">
<!-- if user clicks next button, goto form payMethod -->
<toForm>payMethod</toForm>
</button>
</buttons>
</form>
<form id="payMethod">
<className>Model_Form_payMethod</className>
<buttons>
<button id="back">
<toForm>personalData</toForm>
</button>
<button id="next">
<!-- if user clicks next button and $value='creditcard', goto form ccData -->
<toForm condition="$value = 'creditcard'">ccData</toForm>
<!-- if user clicks next button and $value='banktransfer', goto form baData -->
<toForm condition="$value = 'banktransfer'">baData</toForm>
</button>
</buttons>
</form>
<form id="ccData">
<className>Model_Form_ccDetails</className>
<buttons>
<button id="back">
<toForm>payMethod</toForm>
</button>
<button id="next">
<toForm>confForm</toForm>
</button>
</buttons>
</form>
<form id="baData">
<className>Model_Form_baDetails</className>
<buttons>
<button id="back">
<toForm>payMethod</toForm>
</button>
<button id="next">
<toForm>confForm</toForm>
</button>
</buttons>
</form>
<form id="confForm">
<className>Model_Form_form2</className>
<buttons>
<button id="back">
<!-- if user clicks back button and $value = 'creditcard', goto form ccData -->
<toForm condition="$value = 'creditcard'">ccData</toForm>
<!-- if user clicks back button and $value = 'banktransfer', goto form baData -->
<toForm condition="$value = 'banktransfer'">baData</toForm>
</button>
<button id="next">
<toAction>finishOrder</toAction>
</button>
</buttons>
</form>
</forms>
</workflow>
</workflows>
</mffConfig>
How to use this stuff?
The last question that I would like to briefly discuss is how you would use this approach in your ZF application.
The first step would definitely be to build you forms as usual using Zend_Form, i.e. create a class that basically looks like this:
class Model_Form_form1 extends Zend_Form
{
public function init()
{
$userEmail = new Zend_Form_Element_Text('user_email', array(
'label' => 'Your email address',
'required' => false
));
$btnSubmit = new Zend_Form_Element_Button('submit', array(
'label' => 'Next',
'type' => 'submit'
));
// Add filters and validators...
// Add elements to form
$this->addElements(array(
$userEmail, $btnSubmit
));
// Add decorators...
}
}
After you created all your forms, you could use an action to start the workflow:
class IndexController extends Zend_Controller_Action
{
/**
* your index action...
*
* @return void
*/
public function indexAction()
{
// init MFF
$mff = new MFF_MultiFormFlow();
// start form with ID personalData in workflow with ID exampleWorkflow
$mff->run('exampleWorkflow', 'personalData');
// is data valid in all forms?
if ($mff->isValid()) {
dosomething($mff->getData());
}
}
}
As stated above, this is only an idea of how you could use this approach. I haven’t started implementing yet, so your input is welcome and valuable

CSS Zend Garden
KingCrunchs kleine Welt
My first reaction is that you shouldn’t define you’re own XML format when you could just as easily use Zend_Config. This will give your users the flexibility to configure the component with ini, xml, or php. Zend_Form operates in the same manor via the constructor:
public function __construct($options = null)
{
if (is_array($options)) {
$this->setOptions($options);
} elseif ($options instanceof Zend_Config) {
$this->setConfig($options);
}
…
I am dealing with a multi-step workflow right now. I was going to use the subforms approach detailed at the Advanced Zend Form Usage page of the manual (http://framework.zend.com/manual/en/zend.form.advanced.html#zend.form.advanced.multiPage).
But I chickened out, primarily because the app is a legacy app without MVC (pity me!). Also, I anticipate wanting to go move back and forth between steps via GET and the solution there struck me as geared towards a one-way flow.
In the end, I am going with a collection of distinct pages (actions), each dealing with its own form, each saving to session, And then at the end, I’ll gather it all up and do all my db writing and emailing. Kind of a hack, but I can’t fight with the architecture right now; customer is already getting antzy on the delay. ;-(
I’d like to see something like you describe – perhaps Zend_Form_Wizard – which would aggregate multiple steps into a single configured component. I suspect that the workflow given at the above manual page is a good start for implementation ideas.
Hi Grayson,
thank you for your valuable input. In fact, the first version of my designed used Zend_Config ;-). However, I discarded this idea because of two drawbacks: 1) XSD enables to easily define a language and 2) you may easily check whether or not your XML file is valid in the context of this language.
In other words, from the perspective of the developer of this framework, XSD allows you to easily check if all used XML files have a valid syntax. From the perspective of the end-user, i.e. the developer who is using this framework, you may use standard tools to very easily create valid XML files, easily check if the syntax is valid, easily check the definition of the syntax, and so on.
On the other hand, I fully agree with you that using XSD does not answer one important question: why should the user (developer of the web app) use two different XML files for his configuration? Maybe the answer to this question is that logically separated parts should also be separated in different XML files. But maybe this answer is too simple…
Bye
Christian
Hi David,
thank you too for your input! When I started to code websites I used pure php without all the nice gadgets of ZF like for instance great support for MVC. However, after I finished my first bigger website (and started maintaining this site) I immediately found out that MVC is not a “nice to have” but simply a “must have” ;-).
Your comment however reminds me of the fact that until now I focused only on the control- and data flow within such a system. I didn’t put any attention on how to integrate this framework into a MVC stack.
As the model (until now) separates forms from actions, you would need a way to define something like “use view script personaldata.phtml for form personaldata”. Hmmmm…. more work to do. ;-)
Bye
Christian
Well, comprehensible approach. It reminds me on Java Server Faces – but not just because of all the XML syntax. ;)
At its heart your workflow engine will be something like event dispatching mechanism. Aren’t there already PHP frameworks implementing this? Maybe PRADO?
@Grayson is right: The configuration should look and feel as Zend-like as possible. This gives way for programmatically configure the workflow as well.
What do you think about a FrontController plug-in to enable event-driven request dispatching in Zend at all? Would not this work out for all kinds of actions as well as for form posting?
And by the way: Please consider your configuration to be modularized! This will help the operations department a lot, for sure…
Hi Ralf,
thanks for the positive feedback. I also had an interesting dicsussion about this approach on the German Zend Framework forum (sorry, but this discussion is available in German only ;-)).
Basically, the whole discussion was about the same concern that you and Grayson mentioned: why use XSD? Actually, I am still not sure which approach I am going to use: XSD or Zend_Config. I see one big pro for using Zend_Config: the look and feel is more ZF-like (as you mentioned). On the other hand, there is a big pro for XSD: it is very easy for the user of the framework to check the validity of his XML.
But maybe this isn’t a “this OR that” question at all? Maybe I am going to use both, i.e. use Zend_Config and provide a XSD for the user?
Bye
Christian
great blog thank you