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.
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.
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.
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:
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
I am front controller action class! My name is James Blunt!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.
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
/APF/config/modules/captcha/biz/projectone/DEFAULT_actionconfig.iniThe file itself must at least contain one action definition like this:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; <= 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:
To enable your application to use the front controller, the bootstrap file (index.php) must at least contain the following lines:
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 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.
$fC->registerAction('sites::demosite::biz','Login');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.
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:
{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:
?projects_projectone-action:setModel=pageid:1|lang:de&news-page=3&projects_projectone-action:stat=action:view|referer:32Using 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:
/~/{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:
/~/projects_projectone-action/setModel/pageid/1/lang/de/~/news-page/3/~/projects_projectone-action/stat/action/view/referer/32AbstractFrontcontrollerAction 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:
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.
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.
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
http://adventure-php-framework.org/Page/Home/benchmarkreport/true/param1/value1/param2/value2to the link
http://adventure-php-framework.org/Page/Guestbook/benchmarkreport/truein an easy way, the following code fragment can be used:
$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);Action definitions are treated as "normal" URL parameters as well. Thus is low-end to change the URL
http://adventure-php-framework.org/Page/ChangeLog/benchmarkreport/true/param1/value1/param2/value2to
http://adventure-php-framework.org/Page/Guestbook/benchmarkreport/true/param1/value1/param2/value2/~/modules_guestbook_biz-action/LoadEntryList/pagesize/20/pager/false/adminview/trueIn this case the following parameter array was given to the generateLink methode:
$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.
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:
$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.
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:
To define the right mode of your action, please define your action class as follows:
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.
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:
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
$Model = &$this->getServiceObject('sites::demosite::biz','DemoSiteModel');By adding the XML tag
<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
<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.