Front controller tutorial

1. Introduction

This tutorial describes use cases of the Front controller and provides implementation hints for your projects.

The use cases described in the following chapters are image delivery, log-in validation, and a language switch.

2. Delivery of images

Due to the fact, that the framework is designed to run in bootstrap mode, all content is delivered with one single file. The more modules an application consists of, the greater is the desire to deliver also popup windows content or media files with this mechanism. Merely, the popup windows contain print views or forms or media files like PDF or ZIP files.

To achieve this goal, another bootstrap file can be created, that handles the different kind of requests. Unfortunately, this procedure generates code redundancy.

With aid of the front controller, exacting front controller actions, the problem of redundant code can be solved clearly. The timing model of the front controller's dispatching process allows you to execute a front controller action before the page controller page is created (TYPE_PRE_PAGE_CREATE, see Front controller). Thereby, the developer can decide himself, whether the requested page (including the front controller action call) is displayed within the current browser window or in a now one. Another advantage is, that the program code responsible for the additional content can be delivered directly with the module package. This does not only lead to a better code quality but also the delivery is made easier. Further, within an front controller action a page controller page can be created and displayed.

As described in the front controller documentation, a front controller action is described by a section in the action configuration file. The configuration includes the definition of the location and name of the action and input class implementation. The complexity of the input class depends on the amount of the application's code or the tasks to be done. The socialbookmark module, that is delivered with each release, contains a action, that is intended do deliver the bookmark provider symbols. This action is now described in detail.

2.1. Action configuration

The file DEFAULT_actionconfig.ini in the /APF/config/modules/socialbookmark/action/sites/demosite folder (please refer to the current apf-demopack-* release file) defines the action's parameters. The InputParams attribute defines the default values of the input object. The configuration file looks as follows:

APF configuration
[showImage] ActionClass = "APF\modules\socialbookmark\biz\actions\ShowImageAction" InputParams = "img:bookmark_del_icio_us|imgext:png"

2.2. Action implementation

The file ShowImageAction.php (see ActionFile) contains the program code of the action. In case of the image delivery, tha path to the desired image is build up and afterwards the image itself is delivered to the browser. To stop execution of further actions or the creation of the front controller page, the action contains an exit() at the end of the run() method. If the exit() is not present, the defined page controller page will be delivered. This causes the browser to display a broken image. The complete source code of the image delivery action is printed in the following code box:

PHP code
use APF\core\frontcontroller\AbstractFrontcontrollerAction; class ShowImageAction extends AbstractFrontcontrollerAction { public function run() { $classLoader = RootClassLoader::getLoaderByVendor('APF'); $rootPath = $classLoader->getRootPath(); $image = $rootPath . '/modules/socialbookmark/pres/image/'; $image .= $this->input->getAttribute('img') . '.' . $this->input->getAttribute('imgext'); header('Content-type: image/' . $this->input->getAttribute('imgext')); header('Cache-Control: public'); readfile($image); exit(); } }

2.3. Input definition

In this case no further input parameter definition is necessary, thus no custom input implementation is required.

2.4. Practice

Within the socialbookmark module, bookmark service symbols are included by the following image tag:

APF template
<img src="/~/APF_modules_socialbookmark-action/showImage/imgext/png/img/bookmark_technorati" alt="" />

If you analyze the url requested, the parameters involved in the front controller action call have the following meaning:

  • modules_socialbookmark: Namespace where the definition (configuration see chapter 2.1) of the action is expected.
  • -action: This suffix is attended to indicate the action call within the url.
  • showImage: Name of the action and name of the configuration section at the same time.
  • imgext: Parameter imgext
  • png: Value of the parameter imgext
  • img: Parameter img
  • bookmark_technorati: Value of the parameter img

3. Validation of login credentials

As a rule, front controller actions are used to create and fill a application model - a model represents the status of an application - before the presentation layer is created. For this task, the action can revert to it's input object, that is procured by the front controller. If desired, the action definition can already define default values for certain input parameters. These parameters are then default values during action execution.

The main challenge of the "login check action" is to validate login information included in the request, a session or a cookie and to provide this information to the application. This information is then used by the business and presentation layer to control the application and to build the GUI. Front controller actions are defined to be business layer members.

The scenario described in the last break contains two main "business cases": login information are included in the request and second, login information must be gathered from other sources like sessions or cookies. To provide the ability to login via cookies, the action must be executed with each request. For these purposes, the action handling the login functionality can be registered to the front controller as a "permanent" action. To do so, the bootstrap file must be changed like this:

PHP code
$fC = &Singleton::getInstance('APF\core\frontcontroller\Frontcontroller'); $fC->setContext('my-app'); $fC->registerAction('VENDOR\app\namespace', 'myAction');

The implementation of the action contains of three components as well. When dealing with MVC, front controller and three tier based applications, it is advisable for us to define a central application model class, that holds the current status of the application. This object can be used for flow control or business layer functionality and to build the GUI, later on.

For this example, the class described in the following code box is intended to be the application model. To make the example not too complex, the model defines only few attributes:

PHP code
use APF\core\pagecontroller\APFObject; class ApplicationModel extends APFObject { public function __construct() { // defines the view that should be displayed (login|welcome) $this->attributes['view.content.template'] = 'login'; // user id of the logged in user (null|user_id) $this->attributes['user.id'] = null; // defines, how the user was logged in (request|cookie|session) $this->attributes['login.mode'] = 'request'; // indicates if a login has failed $this->attributes['login.status'] = null; } }

As you can see, the model uses the private member variable $attributes to store the information. That brings the advantage, that the model attributes can be used in the <fcon:importdesign /> tag to influence the GUI structure. Details on the tag can be read about in section Standard taglibs.

3.1. Action configuration

To be able to call the action via request or execute it as a permanent action it must have a configuration file. The section shown in the following code box can be used as a template:

APF configuration
[Login] ActionClass = "VENDOR\module\biz\actions\login\LoginAction"

3.2. Action implementation

The core functionality of an action is defined in the run() method. The following source code can be used to fulfill the functionality described above:

PHP code
use APF\core\frontcontroller\AbstractFrontcontrollerAction; use APF\tools\request\RequestHandler; use APF\core\session\Session; class LoginAction extends AbstractFrontcontrollerAction { public function run() { $username = RequestHandler::getValue('Username'); $password = RequestHandler::getValue('Password'); $id = RequestHandler::getValue('ID'); /* @var $model ApplicationModel*/ $model = & $this->getServiceObject('VENDOR\my\namespace', 'ApplicationModel'); // create the Session $session = new Session('MyApplication'); // case 1: (no direct user interaction) // // a) data from session are not null and are valid. // b) data are not included in the session. // if (!isset($_REQUEST['Login'])) { // case 1.1: extract data from session $Username = $session->loadSessionData('Username'); $Password = $session->loadSessionData('Password'); if (!empty($Username) && !empty($Password)) { if ($this->validateCredentials($Username, $Password)) { // case 1.1.1: data from session is valid, user is logged in $model->setAttribute('view.content.template', 'content'); } else { // case 1.1.2: data from session are not valid $model->setAttribute('view.content.template', 'login'); } } else { // case 1.2: no data available in session $model->setAttribute('view.content.template', 'login'); } } else { if (!empty($username) && !empty($password)) { // case 2.1: login form was sent if ($this->validateCredentials($username, md5($password))) { // case 2.1.1. login credentials are valid -> user is logged in $model->setAttribute('view.content.template', 'content'); } else { // case 2.1.2: login request failed due to wrong username and/or password $model->setAttribute('view.content.template', 'login'); $model->setAttribute('login.status', 'failed'); } } else { // case cases 2.2: form was submitted partial $model->setAttribute('view.content.template', 'login'); } } // case 3: (log-out) // if ($this->input->getAttribute('action') == 'logout') { // delete session attribute $session->destroySession('MyApplication'); // set view to "login" $model->setAttribute('view.content.template', 'login'); } } private function validateCredentials($username, $password) { ... } }

This private method validateCredentials() encapsulates the validation of the login credentials, that must be implemented by the business component.

In order to hold the model information within the entire session, the model can be created in SESSIONSINGLETON mode. This opens the possibility, to have the action only executed at login or logout, because the login information is already available in the session. To mime this behavior, the model must be created as follows:

PHP code
$model = &$this->getServiceObject( 'VENDOR\my\namespace\ApplicationModel', APFService::SERVICE_TYPE_SESSION_SINGLETON );

4. Language switch

The following example describes how a front controller action can be used to change the language of an application.

The language of an application is injected into all objects that derive from APFObject. You can use the $this->language variable within all of your components to determine the currently selected language or to display language dependent content.

The following code assumes that the language to set is passed by the URL parameter language. The current selection is stored using the Session to allow the selection to be memorized during the session. In case no information is present, en is used as a standard value.

PHP code
use APF\core\frontcontroller\AbstractFrontcontrollerAction; use APF\tools\request\RequestHandler; use APF\core\session\Session; class ChangeLanguageAction extends AbstractFrontcontrollerAction { public function run() { // set your favourite namespace here $session = new Session('mytools\languagechoose'); // set default language to 'en' $lang = 'en'; // try loading language of URL parameter $langFromUrl = RequestHandler::getValue('language'); if ($langFromUrl !== null) { $lang = $langFromUrl; } // try to load language from session $langFromSession = $session->loadSessionData('language'); if ($langFromSession !== null) { $lang = $langFromSession; } // save chosen language in session and set it in the application $session->saveSessionData('language', $lang); $actions = & $this->getFrontController()->getActions(); foreach ($actions as $hash => $DUMMY) { $actions[$hash]->setLanguage($lang); } $this->getFrontController()->setLanguage($lang); } }

The last few lines of the implementation show how the language is configured for already created components - actions and the Front controller itself - are applied the new language selection. It is important to re-configure all existing actions since they potentially contain logic that depends on the language of the application.

Within an action you can access the existing actions by

PHP code
$actions = &$this->getFrontController()->getActions();

Via

PHP code
foreach($action as $hash => $DUMMY){ $action[$hash]->setLanguage($lang); }

you can apply the new language value to the existing actions. The front controller itself can be applied the new language value using

PHP code
$this->getFrontController()->setLanguage($lang);
Please be aware that the front controller itself is also applied the new language selection. This is because the front controller created and initializes the Page controller.

Another implementation variant exists within the Forum (German).

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.