Front controller

1. Introduction

The front controller is - along with the Page controller - another central integral part of the adventure php framework. Basic to this implementation are the definition of the front controller pattern of Martin Fowler and the java sources of the java application framework struts. To go further into this topic please visit the PHP patterns website (German).

The front controller provides several entry points to influence the request processing of the framework concerning the below timing model. It is the basis to execute code and provide common information before the page is created. After page creation or transformation further code can be executed to process your application's logic.

Frontcontroller timing model

In complex web applications you often have the need to know about the pieces of the page before page creation to be able to flexibly change them concerning the user information (e.g. Login). Further implementation ideas can be taken from the model based view concept chapter. In order to execute a given action or a set of given actions at each request the developer can register these actions within the bootstrap file. In terms of the timing model, they act as "normal" actions.

The front controller component of the application framework consists of a singleton component of the Frontcontroller class. Using the appropriate input filter the request url and the request parameters are analyzed and the actions encoded in the url are executed concerning the timing modell.

The APF offers two basic classes for action implementation: AbstractFrontcontrollerAction to create actions and FrontcontrollerInput to implement custom input classes.

The wiki page Unterschied Frontcontroller und Pagecontroller (German) details the idea of the page controller and describes the main application case.

2. Implementation

Software written in front controller style merely contain two areas. For a start action and input classes must be written that inherit from AbstractFrontcontrollerAction and FrontcontrollerInput, further, a configuration that describes the action must be built.

Separating implementation and configuration has the advantages that the implementation is hidden from the outside and additional dependencies can be resolved by the implementation trynsparently.

2.1. Action and input classes

Classes that are descended from the abstract action and input classes encapsulate the functionality of an action. As it is described in the API documentation an action class must implement the run() method, because this function is execuded for each action during dispatching. An input class is a data class that contains the model information of the action. In a simple application, the input object of an front controller action can form the model concurrently. The code example printed after this passage shows two simple action an input classes:

PHP-Code
class DemoAction extends AbstractFrontcontrollerAction { public function run() { echo 'I am front controller action class! My Name is '. $this->input->getAttribute('Name').'!'; } } class DemoInput extends FrontcontrollerInput { public function __construct() { $this->setAttribute('Name', 'Max Mueller'); } }

If this action is executed, the output shows the sentence

Code
I am front controller action class! My name is James Blunt!
Since release 1.14 input classes are optional. In case the logic of the input class is limited to storing the input parameters it is not necessary to implement a custom one. Here, you can instruct the front controller to use the FrontcontrollerInput class that is shipped with the APF.

Due to the fact that actions know the context of the entire application, actions can be used to encapsulate various parts of an application if reasonable. A popular example is user authentication. This can be achieved by checking the user's credentials in a "prepagecreate" action and set the parameters of the application to the desired valued.

Within an action it is easy to access the input object via the $this->input variable. If you have to access an action's input from outside the action, you can use the getInput() method. This function returns the action's input as a reference.

2.2. Configuration

Every action must be described within a configuration file. The definition includes namespace, file name for action and input classes, the name of the classes and model information. The configuration files must be placed under the action namespcve using the APF configuration schema.

In case you intent to call an action named showCaptcha having the namespace modules::captcha::biz und dem Namen showCaptcha (url or direct registration) the front controller expects the action configuration beeing located in the actionconfig.ini file located in the namespace mentioned above. Given the fact, that the current APF installation is configured for context projectone and the default setting for the environment parameters the configuration file is expected under

Code
/APF/config/modules/captcha/biz/projectone/DEFAULT_actionconfig.ini

The file itself must at least contain one action definition like this:

APF-Konfiguration
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; <= Version 1.13 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; [{ActionName}] FC.ActionNamespace = "" FC.ActionFile = "" FC.ActionClass = "" FC.InputFile = "" FC.InputClass = "" FC.InputParams = "" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; > Version 1.13 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; [{ActionName}] FC.ActionNamespace = "" FC.ActionClass = "" [FC.InputClass = ""] FC.InputParams = ""

Besides, the listed parameters have the following meanings:

  • ActionName:
    Name of the action. This name must be present in the URL if the defined action should be called (Example: setModel).
  • FC.ActionNamespace:
    Namespace of the action's configuration file (Example: sites::demosite::biz::actions).
  • FC.ActionFile:
    Name of the file that contains the action class implementation (Example: LoadModelAction).
  • FC.ActionClass:
    Name of the action class (Example: LoadModelAction).
  • FC.InputFile:
    Name of the file, the input class resides (Example: DemositeModel).
  • FC.InputClass:
    Name of the input class (Example: DemositeModel).
  • FC.InputParams:
    Configuration directive that defines the start parameters of an input object. This is often used to configure a specific action for the use in a specific application without changing the source code. (Example: login:true|headview:menu. Key and value are separated by ":", different pairs by "|").
In case no custom input implementation is needed you can leave out the FC.InputClass parameter. Custom input class implementations are only necessary if it contains action parameter processing.

2.3. The bootstrap file

To enable your application to use the front controller, the bootstrap file (index.php) must at least contain the following lines:

PHP-Code
require('./apps/core/pagecontroller/pagecontroller.php'); import('core::frontcontroller','Frontcontroller'); $fC = &Singleton::getInstance('Frontcontroller'); $fC->setContext('sites::demosite'); $fC->setLanguage('de'); echo $fC->start('sites::demosite::pres::templates','website');
Since release 1.13 the result of the front controller execution is not printed automatically but must be sent to the client using echo. This enables you to precess the result within unit tests or things like this.

Since the front controller uses the Page controller the latter must be included before using the front controller using include() or require().

As you can take from the timing diagram in chapter 1 the input and output filters are executed during the front controller execution. The are intended to resolve the incoming request concerning the url layout and pass the action instructions to the front controller. In case of a custom url layout this fact must be taken into consideration.

Since version 1.14 input and output filters are only executed by the front controller. Thus, the Page controller can be used to generate HTML but is no more the leading component for request processing by concept.
In case your process requires actions that are executed on every request, these actions can be added before starting the front controller by adding the line
PHP-Code
$fC->registerAction('sites::demosite::biz','Login');
Registering an action is thereby independent of the type of the action (e.g. prepagecreate, ...).

As the design pattern teaches us, the front controller is able to execute multiple actions per requests - so it the APF front controller. As already mentioned above, actions are subjected to the timing model and can be executed at various time points.

Management of the action calls is part of the front controller implementation. In contrast, the registration is done by the the appropriate Filter (since 1.14) or by the developer itself calling the registerAction() method.

Please note, that all informations in chapter 2.4.3 up to 2.4.6 are deprecated with release 1.14. Please use the link generating facility described in chapter Links (since 1.14) to generate action links!
2.4.1. URL layout

To be able to provide the possibility to execute multiple actions per request a special url layout has been defined that allows to define various action calls having various parameters. Please note, that there is a natural border converning the limit of the character amount within urls. The scheme is as follows:

Code
{namespace}-action:{config-name}={param1}:{value1}|{param2}:{value2}|...

Thereby, {namespace} is the namespace of the action configuration file and {config-name} is the name of the section that defines the action implementation. Param sets are separated by "|", name and value are separated by ":". As mentioned above it is possible to place multiple action calls within the url. Please see the following example that contains two action calls as well as simple parameters:

Code
?projects_projectone-action:setModel=pageid:1|lang:de&news-page=3&projects_projectone-action:stat=action:view|referer:32
Please be aware, that the order of the definition directly influences the execution order!

Using rewrite urls the scheme has been designed to distinguish between action instructions and normal parameters, too. Here, "/~/" is used as a separator. The scheme is similar to the scheme presented above:

Code
/~/{namespace}-action/{config-name}/{param1}/{value1}/{param2}/{value2}/...

"Normal" parameterers are also separated from action instructions or action instruxtions from other actions with the separator noted above. The rewrite url according to the sample above is then:

Code
/~/projects_projectone-action/setModel/pageid/1/lang/de/~/news-page/3/~/projects_projectone-action/stat/action/view/referer/32
This url approach is designed for generic use. If this layout is not sufficent concerning your SEO or other requirements it can be changed using a custom input filter. You are able to generate action calls with RewriteRules as well as using an adapted input filter that decodes your url layout. A tutorial on creating a custom url layout using input filters can be found within the Wiki (German).
2.4.2. Managing actions

AbstractFrontcontrollerAction defines further mechanisms that can be used to influence action execution behaviour. Beneath the timing mode and the facility to register permanent actions, it is possible to activate and deactivate on demand. You can use this to execute an action when a specific action is on the stack or is not. Further, you are free to place your own logic.

For this reason isActive() can be overriden from AbstractFrontcontrollerAction to encapsulate your own logic:

PHP-Code
public function isActive() { $captcha = &$this->getParentObject()->getActionByName('showCaptcha'); return $captcha === null; }

The code placed in the above code box activates the action in case the showCaptcha is not requestes - the action is not on the stack.

This functionality can be used as of release 1.13!
2.4.3. The FrontcontrollerLinkHandler

To ease url generation concerning the scheme described in the chapters before the FrontcontrollerLinkHandler is shipped with the APF release in parallel to the LinkHandler that generates links for the Page controller's url layout.

This component is able to manipulate links on basis of the current url and includes all action instructions into ths url.

This behaviour is only activated, if the constructor of the desired action contains the assignment of the value true to the $keepInURL class member. By default, the value is false. If not set to true, all action instructions are removed from the generated URL. This adds the possibility to have more than one action definition in one URL and so create complex programms.
2.4.4. Simple manipulation of parameters

In many applications it is necessary to generate dynamic links. Within front controller based applications where no dynamic URLs are necessary the LinkHandler can be used instead of the FrontcontrollerLinkHandler component. But it is recommended to use the the latter to be secure. To change the link

Code
http://adventure-php-framework.org/Page/Home/benchmarkreport/true/param1/value1/param2/value2

to the link

Code
http://adventure-php-framework.org/Page/Guestbook/benchmarkreport/true

in an easy way, the following code fragment can be used:

PHP-Code
$URL = 'http://adventure-php-framework.org/Page/ChangeLog/benchmarkreport/true/param1/value1/param2/value2'; // define changes $ChangeParams = array( 'Page' => 'Guestbook', 'param1' => '', 'param2' => '' ); echo FrontcontrollerLinkHandler::generateLink($URL,$ChangeParams);
2.4.5. Manipulation of parameters and actions

Action definitions are treated as "normal" URL parameters as well. Thus is low-end to change the URL

Code
http://adventure-php-framework.org/Page/ChangeLog/benchmarkreport/true/param1/value1/param2/value2

to

Code
http://adventure-php-framework.org/Page/Guestbook/benchmarkreport/true/param1/value1/param2/value2/~/modules_guestbook_biz-action/LoadEntryList/pagesize/20/pager/false/adminview/true

In this case the following parameter array was given to the generateLink methode:

PHP-Code
$URL = 'http://adventure-php-framework.org/Page/ChangeLog/benchmarkreport/true/param1/value1/param2/value2'; // define changes to the URL $ChangeParams = array( 'modules_guestbook_biz-action:LoadEntryList' => 'pagesize:20|pager:false|adminview:true', 'Page' => 'Guestbook' ); echo FrontcontrollerLinkHandler::generateLink($URL,$ChangeParams);

If the desired action was added as a "permanent" action it is only necessary to call the FrontcontrollerLinkHandler without a second parameter. In doing so all actions that have configured $keepInURL to true will be contained in the URL definition.

2.4.6. Manipulation of parameters and actions with generateURLParams()

The example in chapter 2.4.5 has the disadvantage, that the developer must be aware of the syntax of a frontcontroller URL. To make generation of front controller URLs more comfortable, the FrontcontrollerLinkHandler features the method generateURLParams. This function can generate an action parameter array for use with the generateLink() method. Generation goes as follows:

PHP-Code
$URL = 'http://adventure-php-framework.org/Page/ChangeLog/benchmarkreport/true/param1/value1/param2/value2'; // define URL changes $ChangeParams = array( 'Page' => 'Guestbook' ); // Parameter der FrontController-Action erzeugen $ChangeParams = array_merge( $ChangeParams, FrontcontrollerLinkHandler::generateURLParams( 'modules::guestbook::biz', 'LoadEntryList', array( 'pagesize' => '20', 'pager' => 'false', 'adminview' => 'true' ) ) ); echo FrontcontrollerLinkHandler::generateLink($URL,$ChangeParams);

Owing to performance issues this method should not be used excessively. One generation commonly consumes about 0.004 sec to generate the parameter array. If the operation mode concerning the url style is clear, the URLs should be generated as described in chapter 2.4.5.

2.5. Timing model

The front controller has it's own timing model that enables the developer to influence at which point of time the action is executed. The time can be defined the class attribute $type of the AbstractFrontcontrollerAction class. This value is prepagecreate by default. There are four modes defined:

  • prepagecreate (TYPE_PRE_PAGE_CREATE): Action is executed before the page object is created an the DOM tree is created.
  • postpagecreate (TYPE_POST_PAGE_CREATE): Action is executed after the creation of the page controller page.
  • pretransform (TYPE_PRE_TRANSFORM): Action is executed before transforming the page controller page.
  • posttransform (TYPE_POST_TRANSFORM): Action is executed after transforming the page controller page.

To define the right mode of your action, please define your action class as follows:

PHP-Code
class MyAction extends AbstractFrontcontrollerAction { // set timing at class declaration for release <= 1.13 protected $type = 'pretransform'; // set timing at class declaration for release > 1.13 protected $type = self::TYPE_PRE_PAGE_CREATE; public function __construct() { // set timing at construction for release <= 1.13 $this->type = 'pretransform' // set timing at construction for release > 1.13 $this->type = self::TYPE_PRE_PAGE_CREATE } public function run() { } }

Details for the function run() can be seen in the API documentation of the class Frontcontroller.

2.6. Model based view concept

In contrast to page controller applications, the front controller allows to instanciate the business layer before the presentation tier. This holds the advantage to use the business layer to control the presentation tier, in particular, the views or the content of the views of an application.

To rip this advantage off in GUI design the XML taglib fcon_taglib_importdesign was introduced. This component includes views, that are defined within an application model, that is filled by front controller actions. In common the developer can create a independent business class like this:

PHP-Code
class SiteModel extends APFObject { public function __construct(){ $this->setAttribute('view.content.template', 'login'); $this->setAttribute('view.topmenu.template', 'empty'); } }

Filling the attributes can be achieved by getting reference on this class by

PHP-Code
$Model = &$this->getServiceObject('sites::demosite::biz','DemoSiteModel');

By adding the XML tag

APF-Template
<fcon:importdesign templatenamespace="sites::apfdocupage::pres::templates" modelnamespace="sites::demosite::biz" modelfile="SiteModel" modelclass="SiteModel" modelparam="view.content.template" [sessionsingleton="true|false"] />

to a template file a view depending on the model's parameters can be included. To use the tag it must be announced using the

APF-Template
<core:addtaglib namespace="tools::html::taglib" prefix="fcon" class="importdesign" />

directive. The sessionsingleton attribute defines, whether the model class is created in SESSIONSINGLETON or in SINGLETON mode. Details on the object creation can be taken from the Services chapter.

With this concept views can easily be included by model information and the GUI can be controlled by the business layer completely. Moreover this is more flexible compared with the page controller as a single responsible entity.

Comments

Do you want to add a comment to the article above, or do you want to post additional hints? So please click here. Comments already posted can be found below.
There are no comments belonging to this article.