Wizard Behavoir is an extension that simplifies the handling of multi-step forms.
Features ¶
- Data persistence
- Plot-Branching Navigation (PBN)
- Next/Previous or Forward Only navigation
- Optional step timeout
- Invalid step handling
- All steps submit to the same URL (contoller/action); this greatly simplifies routes
- Save and recover wizards between sessions
- Utility methods for use in views to assist navigation
Requirements ¶
Developed using Yii 1.1.6. Should work with all versions
Usage ¶
The Very Brief Version ¶
- Set up the steps array in the Wizard Behavior configuration
- Attach Wizard Behavior to a controller
- Call WizardBehavior's process() method from the desired action
- Handle the events that WizardBehavior raises
The Long Version ¶
Please see the the manual for full details on using Wizard Behavior - it also fully documents the API and contains example code, and/or download the demo application.
Plot-Branching Navigation (PBN) ¶
PBN allows a wizard to change the path depending on a response from a user. A simple example: you ask the user their gender; you can then take a different path through the wizard presenting gender specific forms depending on their response.
Resources ¶
Credits ¶
Wizard Behavior was inspired by the CakePHP Wizard Component: http://github.com/jaredhoyt
Very nice
Very nice extension, I was looking for something like this a while back, and ended up implementing one for myself, but this clearly seems more complete and documented, definitively gonna give it a try. thanks
Great Work!
Hi,
This is very good component.
I have one query like lets say if I have multiple quiz in the database and so I need to pass the quiz id in the actionQuiz($id) action handler, then how come it will passed further to each step because on each step I need to get the random questions for a particular quiz?
Could you please demonstrate this in your quiz or survey demo?
Thanks!
@utkarshpanwar
Hi,
Thanks for the comment. I hope you understand I don't have time to write specific apps (unless you want to hire me :-) ).
The onProcessStep event handler is the place to put the logic for this, and use the WizardBehavior::save() method to save both the question and answer as an array. Something like:
public function wizardProcessStep($event) { $model new QuizQuestions('scenario'); $form = new CForm("path.to.stateful.form.configuration", $model); if ($form->submitted() && $form->validate()) { $event->sender->save(array($this->getPageState('question'), $model->answer)); $event->handled = true; } else { $question = $model->getRandomQuestion(); $this->setPageState('question'); $this->render($event->step, compact('form','question')); } } }
There would need to be checks to ensure that questions are not repeated (you could use the wizardBehavior::read() method to see what questions have been asked), and probably a host of other things I haven't thought about, but I think this should get you going.
Link Error
found link error in the demo. To work properly, need to modify the provided link by:
<a href="<?php echo $this->createUrl('/demo/registration'); ?>">
demo
It's not ever easy to unterstand the code snippets.
Thank's for the demo app!
Problem in demo app
It's a cool extension, after I played around a little bit, I come against a small problem in the demo app:
I run the app on a linux system and it fails whenever I finished the pet survey because the class pet.php cannot be loaded in completed.php. The problem was $model = new $step();
<?php echo CHtml::tag('p', array(), 'Data collected is:'); foreach ($event->data as $step=>$data): $model = new $step();
After I changed the first character to upper case before instantiate the class every thing works fine.
<?php echo CHtml::tag('p', array(), 'Data collected is:'); foreach ($event->data as $step=>$data): $step = ucfirst($step); // upper case first character $model = new $step();
demo as a module
Thanks Yeti nice piece of work
here's the demo as a module
simply copy to /protected/modules folder
and add it into your config
'modules'=>array('wizardBehavior',
access
http://127.0.0.1/yourapp/wizardBehavior/demo
I gusse it would also work using index.php
$event->sender->save() required
I got stuck on this one. This may be a bug: Not only do you have to set $event->handled = true;, but it is also required that you $event->sender->save() at least something. Otherwise the step is not marked as completed.
does it support ajax , client validation ?
in the demo , using CForm class , want to know if it can support CActiveForm ,and does it support client ,ajax validation ? if so , any code example is welcome!
Re: does it support ajax , client validation ?
The wizard is totally agnostic about where the data comes from and how or if it was validated; so to answer the question - the wizard can be used with, but does not itself support, any form class and validation scheme.
All the wizard does is save data from each step, persist it across requests (steps), and make it available on completion. It does not know anything about how the data got there, whether or how it was validated, or what happens to it afterwards; all of those issues are the concern of the various event handlers that deal with the steps and wizard completion.
And for the same reason, while I have not tested it in an Ajax application, there is no reason why the wizard shouldn't work in one.
Hope that helps.
Demo
The demo is working again.
wow! thanks for replying ! yeti great work!
i v accomplished my test ,it do support ajax and client side validations. but not follow your demos. in my onProcessStep event handler method, i rout the request to different method according to the $event->step, then make the different action attribute of forms point to same url (just ignore it ).
one thing to note , don't use Yii::app()->end(); or die(); in the event handler method, return is enough ^-^ ; because these will abort the event propagation ,to get the flowing steps not to execute ;
adding a confirmation step
Hi
I need a confirmation step in my survey, so I put it in the "completed.php" render
but after confirmation I have to process the $event->data
into DB or whatever
so I created an action in thte wizards Controller
but I don't see how to pass or get the $event->data content to the action ?
Re: adding a confirmation step
All - no exceptions - steps must go through the same controller action.
To add a confirmation step do just that - add it as a step and handle the data from the step in the processStep event handler; the data for any step is available in $event->data and the step as $event->step.
confirmation step
I added the step as you describe, but $event->data is null (var dump).
it only contains all the data in the wizardFinished step ?
how can I get the previous steps data in the confirmation one ? is it linked to the save ?
getting all the saved data
the saved content of all steps is here
$event->sender->read()
branches depanding on which button was pressed
I need two branches of my wizard. There should be two buttons (like "Next-Branch1" and "Next-Branch2". First button leads to the end of wizard, saves data to db and afterthen redirects to another form. Second button continues wizard and there would be another steps, after them data should be saved, no redirects.
Is it possible with this wizard?
re: branches depanding on which button was pressed
Absolutely - check out Plot Branching Navigation in the manual; PBN does exactly what you need
branches depanding on which button was pressed - 2
I have found how PBN works and does branching depanding on user input (for example,
if ($model->answer==='Yes') { ... change branche ... } etc.
Howerver, I cannot understand how to change branches depanding on which button was clicked. Say, I have two next buttons.
[yii newbye]
branches depanding on which button was pressed - 2
Buttons are user input. In the step event handler just determine which button was pressed then call $event->sender->branch(branchParams), where branchParams determines which branch is selected and/or which branch(es) are deselected or skipped.
See pages 29-33 of the manual for details on PBN and take a look at the demo code.
Repeat a step
Hi
I need to repeat step several times.
Here's my example:
I've tried this in my processStep handler:
case 'getThingCountForm': // Save a shadow so we can count down and loop $model->thingcountdown = $model->thingcount; $event->sender->save($model->attributes); break; case 'getThingForm': // save the new Thing $thing = new Thing(); $thing->name = $model->thingname; $thing->save(); // Decrement the countdown $stepdata = $event->sender->read(); $stepdata ['getThingCountForm']['thingcountdown']--; $event->sender->save($stepdata ['getThingCountForm'],'getThingCountForm'); if ($stepdata ['getThingCountForm']['thingcountdown']>0) { $event->handled = false; $this->redirect(array('survey','step'=>'getThingForm'),false); } break;
Any ideas how I can make the same step run more than once?
thanks in advance
Russell
repeating steps
I also need repeating steps several times (i need cycling), but in the following way:
1) Step1
2) Step2
3) Step3
4) Ste[4 -> finish or go to step2 again
I need repeating step2-step4 till user decides to finish.
Re: Repeat a step
Currently the wizard saves data using the step name as the key, so reusing a step name will just overwrite the data for that step.
The following might be worth trying: (In WizardBehavior)
public function save($data,$step=null,$iteration=null) { if (is_int($iteration)) $this->_session[$this->_stepsKey][(empty($step)?$this->_currentStep:$step)][$iteration] = $data; else $this->_session[$this->_stepsKey][(empty($step)?$this->_currentStep:$step)] = $data; }
You would need to track which iteration you are on in the process step handler.
When you retrieve the data at the end of the wizard, repeated steps will have the repetitions as an array.
Let me know if it works - if so I'll add it in to the released version.
Repeating the Step?
Thanks Yeti
Good idea about saving into the session using an iteration value. I'll give it a try.
I spent most of this afternoon trying to figure out how to make processStep go back to the same step.
I tried setting WizardBehaviour::handled = false. I tried using $this->process('StepName'). I tried making WizardBehaviour::previousStep() public and using that. I tried $this->redirect()
All failed. Now my head hurts. :-)
Any pointers to how I make it repeat the step?
Thanks
Russell
Repeating the Step?
I should add - I don't know how many iterations I will need beforehand, so I can't just add more of the same steps to WizardBehaviour::steps.
So my last step in the config is 'getThingForm' and I only have one of those, but need to go back to it programmatically based on an interation count entered in a previous step by the user.
Cheers
Russell
Repeating the Step?
Have you tried putting the step you want to repeat inside a branch, then repeatedly selecting it using the branch() method?
Javascript submitting
Hi
I got the whole wizard going it's great , but I have an interface request
where I have to submit the surveys with a link (javascript submit)
but this strangely seems to block the process of going to nextStep in some way I don't really understand ? if by any chance someone got it working , I'd love a use case or a sample
Cheers
Broken link on demo website
Hi,
I just wanted to say I have found a broken link on the demo page:
http://wizard-behavior.pbm-webdev.co.uk/
Download The Wizard Behavior and/or the code for this demo:
http://www.yiiframework.com/extension/wizard-behaviour/ <- wrong one
http://www.yiiframework.com/extension/wizard-behavior/ <- good one
That's it, now I am going to test it because it's just what I need for my forms!! thanks! :)
Infinite loop on Yii 1.1.3 fixed
On Yii 1.1.3 actions declaration in controller don't allow default arguments, and for some reason WizardBehavior component can't identity the current step value and enters an infinite loop:
Original from the demo:
public function actionSurvey($step=null) { $this->pageTitle = 'Survey Wizard'; $this->process($step); }
Edit to make that work:
public function actionSurvey($step=null) { $step=isset($_GET['step']) ? $_GET['step'] : null; $this->pageTitle = 'Survey Wizard'; $this->process($step); }
Maybe it can be fixed from inside the component in a future update.
Hope it helps someone!
Different server displays 'Object id #32' in place of Form
I was working from your demo Registration wizard, and everything worked fine on our development server. When loading the app to our production server we get 'Object id #32' displayed where the Login Form should be displayed. Everything else displays fine.
Here is the generated page snippet:
<div>Step 1 of 4 <h3>User</h3> <div class="form">Object id #32</div> </div>
Tiny note
This is really just a tiny detail but could be annoying if one do not pay attention, in the demo:
$config['class']='application.components.WizardBehavior';
in the manual:
it says the common place to put the file is in the extentionfolder..
I noticed my mistake early so no time wasted.
Bugfix for premature exit (during back-and-forth or looping)
Thanks for the great extension.
If you are going back steps and pruning everything in front of you, or if you implement looping over a single step with no steps in front of it, you are going to want to make a change to isValidStep($step) as follows:
protected function isValidStep($step) { $index = $this->_steps->indexOf($step); if ($index>=0) { + $expectedIndex = $this->_steps->indexOf($this->getExpectedStep()); if ($this->forwardOnly) - return $index===$this->_steps->indexOf($this->getExpectedStep()); - return $index <= $this->_steps->indexOf($this->getExpectedStep()); + return $index===$expectedIndex; + return $expectedIndex>=0 ? $index <= $expectedIndex : $index<=$this->getStepCount(); } return false; }
Reasoning: The next expected step is the next step which does not have data. If there are no more steps, and the current step already has data but is being resent (due to going back or looping), the resend would fail. So we say if no further expected step, step is valid as long as it is within range of total steps.
Error on project setup
Other than editing the path to framework folder is there any other configuration. The zipped demo does not run. Gives Object not found error
Demo?
The demo isnt working at the moment?
Same problem as eclectus
On our dev server it's fine but on the live server in place of the form I get Object id #31.
Anybody have any ideas?
Small error in the demo: here is a fix
If you try to run the demo code, you may encounter an error: "Object not found". It happens when you run a wizard all the way to the end. The completed.php view tries to load the models that it finds in the event steps. But the event step names all start with a lower case character, while the models have an uppercase.
2 possible solutions:
$model = new $step();
with:
$modelName = ucfirst($step); $model = new $modelName();
Docs for wizard behavior
Here is the link with docs for wizard behavior if anyone needs it.
Missing PDF with Manual
This seems to be a great extension, but unfortunately the links are not working and wizard_behavior_manual.pdf can't be found. Is there any way to re-upload it?
There's no other place for docs
to julianm.
Check the link I provided below. It's not pdf but you can save the html page and use it as it is.
Manual
I've found a copy of the manual at
http://www.scribd.com/doc/120297741/Yii-Multi-Page-Form-Wizard-Behavior-Manual
How to save model in BD?
Hi,
Anybody can help me please?...my question is this:
My behavior working ok, but I don´t know, how to save the information content in
$event->data because this variable is an array associative.
thanks for tell me any idea, for this problem.
Reply to @lahiponeja
A crude example, if you don't want to simply use
can be done like this inside your controller where you run the wizard, assuming you have a Player model:
public function wizardFinished($event) { if ($event->step===true) { //save data to database Yii::log(json_encode($event->data),'error'); $player = new Player(); $player->fname = $event->data['playerInfo']['fname']; $player->lname = $event->data['playerInfo']['lname']; $player->addr = $event->data['playerInfo']['addr']; $player->city = $event->data['playerInfo']['city']; $player->state = $event->data['playerInfo']['state']; $player->cntry = $event->data['playerInfo']['cntry']; $player->zip = $event->data['playerInfo']['zip']; $player->phone = $event->data['playerInfo']['phone']; $player->email = $event->data['playerLogin']['email']; $player->pwd = $event->data['playerLogin']['pwd']; $player->save();
Hope this helps.
@eclectus
Thanks, Your information was very helpful. :)
Bugfix for maintaining step order with labelless trees
If you don't use labels, and try to insert a branch, the order gets screwed because the original order-integer is used as a label index at "$parsed[$label] = $step;".
Fix:
private function _parseSteps($steps) { $parsed = array(); foreach ($steps as $label=>$step) { $branch = ''; + if(is_numeric($label)) + $label = $step; if (is_array($step)) { foreach (array_keys($step) as $branchName) { $branchDirective = $this->branchDirective($branchName); if ( ($branchDirective && $branchDirective===self::BRANCH_SELECT) || (empty($branch) && $this->defaultBranch) ) $branch = $branchName; } if (!empty($branch)) { if (is_array($step[$branch])) $parsed = array_merge($parsed, $this->_parseSteps($step[$branch])); else $parsed[$label] = $step[$branch]; } } else $parsed[$label] = $step; } return $parsed; }
How to use TbForm with this extension?
Hello!
Thanks for this great extension.
Can I ask an advice about using Yii Bootstrap (TbForm) forms with the wizard?
This don't works:
public function getForm() { return new TbForm(array( 'elements'=>array( 'title'=>['hint'=>'my hint'], 'last_name'=>['hint'=>'my hint'], 'first_name'=>[], ), 'buttons'=>array( 'submit'=>array( 'type'=>'submit', 'label'=>'Next' ), ), ), $this); }
Thank you!
Wizardbehaviour and TbForm
@heal
"Don't works" is a bit of a vague error message. TbForm should work kind of similar to CForm.
Depending on your error you can try/check:
'elements'=>array( 'test'=>array('htmlOptions'=>array('hint'=>'myhint')), 'type'=>array( 'type'=>'dropdownlist', 'data'=>$this->getPackageList(), ), ),
demo navigation
I have replaced the line, in view/demo.php
<a href="/demo/registration">Registration Wizard »</a>Registration
With
<a href="<?php echo $this->createUrl('/demo/registration'); ?>">Registration
Saving incomplete forms, or saving on the last page
This extension is great, and I think I can find about 1000 ways to integrate it into my workflow.
I've got a problem currently in use that I think is a simple one to fix, I'm just not quite grasping how to approach it:
Say I've got a three page form, and I'm on the last page. I realize that I don't have all the information needed to complete the last page. I'd like to save my progress, but I can't save without all the required fields entered. If I fill the fields with temporary information and hit save, the form is submitted instead of saved.
How can a form be saved without being submitted on the last page? I think that if you suspend validation until the full form is submitted, you'd hit an issue where page x of an x page form had an error, and you'd need to redirect a user to that page of the form. Any advice would be greatly appreciated. I've taken a few swings at this issue, but I think a fresh set of eyes might be able to see what I'm missing easily.
Saving incomplete forms
@oceanscrashing
Not sure what you are having a problem with, but maybe this can help:
(1) you can save using Ajax without having to go out of the wizard "cycle".
(2) you can always call save() when a step is complete before moving to the next step.
(3) the wizard saves the filled data into the model, so you really do not lose any of the data while you are still within the wizard so you can go back and forth - i.e. show a "missing fields" dynamic step at the end asking to complete the missing fields, no need to save until the user submits the validated form.
Yii 2
Does it work for yii2?
to #19313
Yes, it works with Yii2, but with some minor updates. You may find modified versions at github.com
What is the github link to the yii2 version of this plugin?
Hi
I need to do multi step in yii2. May I have the github link to the new version of this plugin?
Thanks
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.