Recent changes RSS feed
 

WACT Controller Architecture

This page describes the WACT controller architecture and how it helps to implement the Model-View-Controller pattern in PHP. This page assumes a familiarity with MVC.

Controller Overview

Controller objects in WACT are responsible for interpreting the incoming HTTP request, building a temporary model as a response, calling the Domain Model or the application and selecting a view to render that response model and Domain Model.

Controller Structure

Controllers in WACT are hierarchical and designed to loosely parallel the components of the view. The top level controller represents the point at which the HTTP Request is accepted. An example controller hierarchy might look like this:

PathInfoDispatchController module "bookmarks"
  PathInfoDispatchController operation "edit"
    Page "Edit"
      Form "Edit Bookmark"
        Button "Preview"
        Button "Save Changes"

This mirrors how the controllers of a GUI application might be hierarchically organized:

Application "bookmark Manager"
  Window "Bookmarks"
    Form "Edit Bookmark"
      Button "Preview"
      Button "Save Changes"

Building the Controller structure

A Controller base class can be subclassed and the child components created inside of its contructor. In this way creating a new instance of the base component automatically constructs the entire tree.

class EditPage extends PageController {
 
    function EditPage() {
        parent::PageController();
 
        $Form = new FormController();
        $Form->addChild('submit', new ButtonController());
        $Form->addChild('preview', new ButtonController());
 
        $this->addChild('EditForm', $Form);
    }
}

Here the addChild method adds a child controller to the tree and gives it a name. The entire tree in this example is created at one time. The tree created looks like this:

EditPage
  Form "EditForm"
    Button "submit"
    Button "preview"

The hierarchy of controllers may also be constructed by instantiating the parent controller and adding the children:

$Front = new PathInfoDispatchController();
$Front->addChild('index',   new Handle(APP_LIB_ROOT . 'phpmodule/index.page.php|IndexPage'));
$Front->addChild('details', new Handle(APP_LIB_ROOT . 'phpmodule/details.page.php|DetailsPage'));
$Front->addChild('delete',  new Handle(APP_LIB_ROOT . 'phpmodule/delete.page.php|DeletePage'));
$Front->addChild('edit',    new Handle(APP_LIB_ROOT . 'phpmodule/edit.page.php|EditPage'));
$Front->addChild('add',     new Handle(APP_LIB_ROOT . 'phpmodule/add.page.php|AddPage'));

This example shows using the Handle object to delay instantiation of each child controller object. The Handle object acts as a proxy for the specified object. When the child controller is referenced, the controller is constructed based on the information in the Handle object and the Handle object is replaced by the actual object.

The constructor technique works well with controllers that are the target of a Handle.

Event Driven

The WACT controller architecture is event based.

HTTP Request to Event Translation

Calling the start method on the top level controller in the hierarchy front or page begins event processing for the current HTTP request.

Each controller examines the incoming request and raises events representing the lifecyle of that controller. During this process, the controller examines the request and will dispatch to one of its child controllers to continue handling the request. If the controller cannot determine which child to dispatch to, the controller will dispatch to a registered default child controller. Each child controller repeats the process, handling the event or passing it on to its children. This is the Chain of Responsibility pattern.

A good analogy would be that when a GUI application receives a mouse click event, the coordinates of the click are compared down through the hierarchy of components against a rectangle representing that component until the event reaches the lowest level component possible. In much the same way, the raw input request is passed down the heirarchy of controllers, except instead of comparing mouse coordinates to determine which child controller should handle the event, the contents of the HTTP request are examined.

There is only one path from the top of the hierarchy to the lowest level child in the hierarchy for any given request. no events will be raised on the controllers in the hierarchy that are not on this path.

Event listeners

The framework user may register to listen to various events during the lifecycle of each controller. The event listeners typically have the following method signature:

function eventListener(&$sender, &$request, &$responseModel)

Arguments:

&$sender controller that trigger this event
&$request A Request object encapsulating the HTTP request made against the application. The Request object represents the external input to the application and only supports a read only interface.
&$responseModel A temporary model used for servicing the current request. The View object, which encapsulates the output of the application, renders The responseModel and the Domain Model.

The event listener may return a value which instructs the controller on which View to use to render the request.

The input-processing-output design of Model View Controller is clear here. The event listeners translate input into processing requests on the model, which consists of the responseModel and the Domain Model. The View renders a representation of the model. To maintain the separation between Domain Logic and Presentation Logic, the controller event listeners should not perform Domain Logic, but should instead dispatch to model components which perform this logic.

Registering to Receive Framework Event Notifications

Each Controller makes available a set of event registration methods, one per potential event. An event listener can be registered to receive notification of the event by creating a Delegate object that represents the listener.

class EditPage extends PageController {
 
    function EditPage() {
        parent::PageController();
 
        $Form = new FormController();
        $Form->registerOnActivateListener(new Delegate($this, 'listen'));
    }
 
    function listen() {
        echo "Activate!";
    }
}

In this example, the listen method is registered as a listener for the OnActivation event.

There are two kinds of Delegate objects, a standard Delegate and a StaticDelegate.

The Delegate object calls a method on an object. The first parameter to the constructor is the object to call and the second parameter is the method to call. A Handle object may be used as the target of the Delegate for Lazy loading of event listeners.

The StaticDelegate will perform a static method call on a class and takes a class name as the first parameter and a method name as the second. An optional third parameter can specify a file to be included when the Delegate is de-referenced.

Selecting the View

Each framework event handler may return a value that indicates a View. Different types of return values can result in different views being rendered.

string If a string is returned, the string will be used to look up the view from a list associated with the controller. If the view name is not found in the current controller, the list of views in the parent controller is consulted, moving up the tree until the view is found.
object If an object is returned, it is used as the view object.
Handle If a handle object is returned, it will be resolved into a View object
WACT_NO_VIEW If this constant is returned, event processing will be halted, but no view will be shown.
WACT_DEFAULT_VIEW If this constant is returned, event processing will be halted and the default view shown. If the current controller has no default view, the the default view of the parent controller is used, moving up the tree until a default view can be selected.
NULL if no value is returned, event processing will continue and the lowest level default view will be used unless an alternative view is specified by another event.

If any value other than NULL is returned by an event listener, no other events in the controller heirarchy will be triggered. The controller will instead immediately attempt to find the view and display it.

Associating Views with Controllers

The addView and setDefaultView methods are used to associate a view with a given controller. A view is also available to all of the child controllers of the controller it is associated with. Views are typically associated with controllers using Handles.

addView associates a logical view name with a view object:

$this->addView('notfound', new Handle(APP_LIB_ROOT . 'views/404.view.php|NotFoundView', array('/phpmodule/notfound.html')));

Some special constants are available that refer to WACT supplied view classes. They can be used to help construct handles to these classes.

WACT_VIEW This is the standard WACT view class that uses a WACT Template.
WACT_FORMVIEW This is a Template based view that contains a form.
WACT_REDIRECT This view redirects to a virtual path.
WACT_REDIRECTURL This view redirects to a specified URL.

Here is an example using the WACT_FORMVIEW constant in a default view:

$this->setDefaultView(new Handle(WACT_FORMVIEW, array('/phpmodule/edit.html')));

Additionally, the WACT_NO_VIEW and WACT_DEFAULT_VIEW constants can be associated with a logical view name using the addView method.

$this->addView('success', WACT_DEFAULT_VIEW);

The ResponseModel

The ResponseModel object is a temporary model which represents the application’s response to the current request. The response model is passed between various framework event handlers. The framework event handlers collaborate to build a proper response.

The Request

Rendering the View

The Page controller will trigger the rendering of the selected view, ending the request handling lifecycle. The View object binds the ResponseModel to the components in the template, and binds controller components that correspond the visual components in the Template. Such as Form controllers to Form components in the template The View object may also contain logic for manipulating the template and binding portions of the Domain Model to the Template.

Available Controllers

Page

The Page controller may only have a single default child. It is unusual in that it dispatches only to that single child.

Built-in controller events

OnActivate This event is triggered at the beginning of the controller life cycle. It is useful for security and integrity checks.
OnLoad This event is triggered before dispatching to child controllers. It is useful for loading data into the responseModel.
OnDeActivate This event is triggered after the child components fail to select a view. It is useful for last resort handling of the request.

Button

The button controller may not have children.

Built-in controller events

OnActivate This event is triggered at the beginning of the controller life cycle. It is useful for security and integrity checks.
OnClick This event is triggered when this button is clicked to submit a form.
OnDeActivate This event is triggered after the child components fail to select a view. It is useful for last resort handling of the request.

Form

Form controllers dispatch to their children based on the existence of a post back value by the same name as the child. This is useful for dispatching to buttons on the form.

Built-in controller events

OnActivate This event is triggered at the beginning of the controller life cycle. It is useful for security and integrity checks.
OnLoad This event is triggered the first time a form is loaded. It is useful for loading the initial values into the form.
OnPostBack This event is triggered when the form is posted. The event is triggered after the user entered values are loaded into the ResponseModel, but before dispatching to the next child controller.
OnDeActivate This event is triggered after the child components fail to select a view. It is useful for last resort handling of the request.

ParameterDispatchController

The ParameterDispatchController dispatches to its children based on a get parameter.

Built-in controller events

OnActivate This event is triggered at the beginning of the controller life cycle. It is useful for security and integrity checks.
OnDeActivate This event is triggered after the child components fail to select a view. It is useful for last resort handling of the request.

PathInfoDispatchController

The PathInfoDispatchController dispatches to its children based based on the Path Info.

Built-in controller events

OnActivate This event is triggered at the beginning of the controller life cycle. It is useful for security and integrity checks.
OnDeActivate This event is triggered after the child components fail to select a view. It is useful for last resort handling of the request.
 
wact/controller_architecture.txt · Last modified: 2006/12/09 17:16
 
Hosting for this site donated by Procata PHP Development