Page 2 of 2

Re: Wizards and MVC

Posted: Fri Jan 28, 2011 9:58 am
by Jenk
Another way to look at it, is that a Wizard is just a paginated form.

Re: Wizards and MVC

Posted: Fri Jan 28, 2011 11:03 am
by VladSun
Jenk wrote:Another way to look at it, is that a Wizard is just a paginated form.
That's only the "linear" wizard, IMHO

Re: Wizards and MVC

Posted: Fri Jan 28, 2011 12:08 pm
by Jenk
There's another type of wizard? :P

Re: Wizards and MVC

Posted: Fri Jan 28, 2011 12:12 pm
by John Cartwright
Jenk wrote:There's another type of wizard? :P
I imagine he is refering to "wizards" where steps are not required to be completed sequentially.

Re: Wizards and MVC

Posted: Fri Jan 28, 2011 2:24 pm
by Jenk
That's even more like a paginated form than a linear wizard then! :P

Re: Wizards and MVC

Posted: Fri Jan 28, 2011 7:24 pm
by Christopher
Jenk wrote:There's another type of wizard? :P
I think paginated forms is a simple way to view a wizard (and not a bad one necessarily) . However there are a number of cases where there are either multiple paths through a sequence or where certain steps can be skipped. I gave the checkout process as a place where I often use an Application Controller. Here are some examples of the non-linear cases I mentioned using checkout as a use case.

A common step in the checkout process is to require the customer to either log-in or register. However, if the customer is already logged-in then this step can be skipped and they can go directly to confirming the order, their address, or whatever the next step would be. This log-in or register also shows the idea of multiple paths through the sequence.

Another example of multiple paths I have implemented is different steps for different types of customers. For example, there my be regular customers and business customers. The registration and checkout sequences may have more options or be more involved for business customers.

As Josh said, all this can be done with if()'s in your controllers. I think most people don't have Application Controller code available or perhaps don't like the more event driven style of setting up states and transitions for it require.

Re: Wizards and MVC

Posted: Sun Jan 30, 2011 5:14 am
by kr1pt
VladSun wrote:How do you guys write your wizard pages in a way that fits the MVC approach? I'm not interested in passing the current model state. I'm more interested in the M-V-C relations defined.

The problem I'm facing is that the Controller doesn't know the logic flow to follow - it's in the Model.
In homepage, Router checks if there is controller selected in URL. If isn't, script gets default controller from configuration file and instantiates it. If method for controller is not set, it loads $controller->index() method. If not, loads: $controller->$selected_method();

Abstract class "Base controller", has function for loading models, and accessing them through variable objects. $this->my_model = new Model();

And controller goes and checks for data in model, and just renders it.

Controller example:

Code: Select all

class Blog // implements Controller; extends Controller
{
    public function index()
    {
        $this->get_view('blog_index');
    }
    
    public function new_post()
    {
        $this->get_view('blog_new_post');
    }
    
    public function get_post($id = null)
    {
        if (!is_null($id))
        {
            $this->get_model('My_Model');
            
            $this->my_model->get_post_by_id($id);
            
            $this->variables['posts'] = $this->my_model->get_post_by_id($id);
            
            /**
             * View file:
             * 
             * foreach ($this->variables as $variable => $key)
             *  return $key
             */
            $this->get_view('blog_get_post');
        }
    }
}

Re: Wizards and MVC

Posted: Mon Jan 31, 2011 6:38 am
by VladSun
kr1pt, it's not the "wizard" I meant.

Re: Wizards and MVC

Posted: Thu Feb 03, 2011 3:49 pm
by VladSun
I faced two problems:
1) A "silent" stage - i.e. stage that will be executed, but its View is not rendered ever. Input data need some processing, but no user interaction needed - just jumping to the next stage. [ SOLVED ]
2) A "repeated" stage - i.e. a stage (together with substages hierarchy) that should be repeated several times (in a loop) [ STILL SOLVING IT]

Re: Wizards and MVC

Posted: Thu Feb 03, 2011 5:03 pm
by Christopher
Again, the point of an Application Controller is that the "stages" do not need to know any of this -- the rules/conditions manage the program flow. So silent or repeated are irrelevant.

Re: Wizards and MVC

Posted: Thu Feb 03, 2011 5:29 pm
by VladSun
What I have by now is:

(it's JS, remember? And it has some AJAX calls in it, so it's all asynchronous)

Application.js

Code: Select all

		this.configurator	= new Ext.App.Model.Configurator();
		
		this.wizard = new Ext.ex.Wizard.Panel(
		{
			configurator	: this.configurator,
			router		: new Ext.ex.Wizard.Router(
			{
				map	:
				[
					{
						object	: new Ext.App.Component.Wizard.Stage.FileUpload(),
						stages	:
						[
							{
								object	: new Ext.App.Component.Wizard.Stage.SchemaInfo()
							},
							{
								object	: new Ext.App.Component.Wizard.Stage.TablesPicker()
							},
							{
								object	: new Ext.App.Component.Wizard.Stage.RelationsEditor()
							}
						]
					}
				]
			})
		});
Every stage object extends the Ext.ex.Wizard.Stage.Component:

Code: Select all

Ext.ex.Wizard.Stage.Component = function(config)
{
    Ext.apply(this, config);
    Ext.ex.Wizard.Stage.Component.superclass.constructor.call(this, config || {});

	this.addEvents(
	{
		stageInitialized	: true,
		stagePrepared		: true,
		stageCancelled		: true,
		stageCompleted		: true,
		stageFailed		: true,
		stageReady		: true
	});
}

Ext.extend(Ext.ex.Wizard.Stage.Component, Ext.Panel,
{
	model		: null,

	init	: function (model)
	{
		this.model = model;
		this.fireEvent('stageInitialized', true, this);
	},

	prepare	: function ()
	{
		this.fireEvent('stagePrepared', true, this);
	},

	redraw	: function ()
	{
	},

	process	: function ()
	{
		this.fireEvent('stageCompleted', true, this);
	},

	cancel	: function ()
	{
		this.reset();
		this.fireEvent('stageCancelled', true, this);
	},

	reset	: function ()
	{
	}
});
Stages notify listeners (i.e. the wizard) about their state. The Wizard makes decision what to do.

After all stages report "stageInitialized" Wizard gets the first available stage object from the Router and initialize it.

On "stageReady" Wizard checks whether the stage "requests" to be executed or not. If not, the next stage object is requested from the router.

On stageProcess the stage is executed. On stageCompleted Wizard gets the next stage from the Router. Etc.

So, on every step my stages decide whether their are needed or not. It's done by passing the current configurator/model state.

An absolutely centralized logic model would be too complicated to define and implement (sounds like a God object IMHO).
I just define the routes, but the stage object itself decides whether it should be executed or not.

Re: Wizards and MVC

Posted: Fri Feb 04, 2011 6:36 am
by VladSun
Maybe the JS example is too complicated because of its AJAX nature. In PHP that would be something like this:

Code: Select all

interface IStage
{
	function setModel($model);

	function preWizardInit(); // check for some preconditions met before the Wizard starts. Run at Wizard init.
	function didPreWizardInitSucceeded(); // shell we run the Wizard at all?

	function preProcess(); // check for some condtions (together with $model current data). Run "before" stage.
	function didPreProcessSucceeded(); // Stage has errors? If yes Wizard stops - Next not available, Prev available
	function isStageSilent(); // Does Stage need a View or it may process silently then jumping to the next stage
	function isStageRequired(); // Is Stage required at all, if not all substages are not required either.

	function process(); // If isStageRequired == true, execute this method
	function didProcessSucceeded(); // Stage has Errors?
}

Code: Select all

class Router
{
	public function setMap($map);
	public function setStateManager($stateManager);
	public function getNextStage(); // returns IStage
}

Code: Select all

class Wizard extends ActionController
{
	public function __construct($router, $stateManager);

	public function nextStage();
	public function finish();
}
An example of usage:

Code: Select all

class MyWizard extends Wizard
{
	public function __construct()
	(
		parent::__construct
		(
			new Router
			(
				array
				(
					array
					(
						'stage' => new Stage1(),
						'substages' => array
						(
							array
							(
								'stage'	=> new Stage1_1(),
								'substages' => null
							),
							array
							(
								'stage'	=> new Stage1_2(),
								'substages' => array
								(
									'stage'	=> new Stage1_2_1(),
									'substages' => null
								),
							),
						)
					),
					array
					(
						'stage' => new Stage2(),
						'substages' => null
					),
				)
			),
			new StateManager()
		);
	)
}