Using the atkFrontController
From Achievo/ATK Wiki
|
ATK Howto: Using the atkFrontController
|
Contents |
Preface
The atkFrontController can be used to implement the MVC pattern in your web application. It's part of ATK and can be used stand-alone for ATK applications. If you use a recent version of ATK the atkFrontController is out-of-the-box available without any special installation or configuration needed. The atkFrontController has been heavily inspired by Ruby on Rails.
This how-to will give a short introduction on usage of the atkFrontController in stand-alone mode. It will soon be extended to add information about usage of Ajax and some new features that will be coming soon.
Basic usage
First you need to checkout ATK or download the latest ATK nightly in a directory that's browseable by your webserver. Copy atk.inc and the example config.inc.php from the atk/skel directory to the root directory of your application. Create a directory atktmp and make it writeable to your webserver. Create a file named index.php with the following contents:
<?php $config_atkroot = "./"; require_once($config_atkroot."atk.inc"); module("example"); atkimport('atk.front.atkfrontcontroller'); $content = atkFrontController::dispatchRequest($_REQUEST); $output = atkinstance('atk.ui.atkoutput'); $output->output($content); $output->outputFlush(); ?>
Create a directory modules and inside this directory create a directory called example, this will be the ATK module we will be working from. Inside the module directory you need to create a directory called controllers. Inside this directory you can place your controller files. A controller file should be named class.<name>controller.inc where <name> can be replaced by your custom name. Inside the controller file you should create a class which follows the same naming scheme and extends the atkFrontController class. E.g.
class HelloController extends atkFrontController { }
The example controller above should be placed in a file named class.hellocontroller.inc.
After you've created the controller class you can start adding actions. An action is a method that uses the following naming scheme <name>Action and is declared public. Inside an action you can assign template variables as if they were instance variables of your controller you can also choose to use your controller as if it were an array and assign template variables that way. E.g.
class HelloController extends atkFrontController { public function helloAction() { // assign a template variable (method 1) $this->title = "Hello!"; // assign another template variable (method 2) $this['name'] = $this->request->name; } }
In the example above you can also see that request variables can be accessed by the /$this->request/ object. In the same manner you can store/retrieve information to/from the session using the /$this->session/ object.
Now that we've assigned some template variables let's look at how we can access them in our template. First we need to create a template for our action. Templates are stored in the templates directory of the controller's module. They are stored using the naming scheme <module>/<controller>/<action>.tpl. So this means that for our hello action we need to create a template called hello.tpl in the directory templates/example/hello of our module. Our template might look like the following:
<h1>{$title}</h1>
Hello {$name}!
As you can see you can simply use the template variables by name. You can also choose to store your template in a templates directory in the root of your ATK application or in the skel/templates directory inside your module directory. ATK will first try to find the template in the application root templates directory, then in your module's templates directory and finally in the module's skel/templates directory
Because we are using the atkFrontController in stand-alone mode we should also create a layout. The layout is given the result of the action and all of the action template variables. The result of the action is made available through the special template variable /$content_for_layout/. Our layout file might look like the following:
<html>
<head>
<title>{if $title}{$title}{else}Hello{/if}</title>
</head>
<body>
{$content_for_layout}
</body>
</html>
A layout is first searched for in the application root's layouts directory, then in the module's layouts directory and finally in the module's skel/layouts directory using a naming scheme of <module>/<controller>.tpl. If no layout is found the exact same directories are searched for a template using the naming scheme <module>/root.tpl. Finally if still no layout template has been found the exact same directories are searched for a template called root.tpl.
Now that we've created our controller with custom action let's browse to it to check if everything works as expected. You should browse to the earlier created index.php and add a parameter uri with the value <module>/<controller>/<action> and another parameter name with your name, so for example http://localhost/?uri=example/hello/hello/&name=Peter.
If everything works as expected you will end up with a page that says Hello Peter! (although it might say something different if your name isn't Peter...).
Spicing things up
Until now we've only seen the tip of the iceberg. The real power of the atkFrontController relies in the way you can link actions together, use Ajax etc. So let's look at a more complex version of our HelloController. Instead of having to add a name parameter to the URL we create a nice form in which the user can enter his/her name. If the user clicks on the "Say hello!" button we want to update a div on the page with a message saying hello to the user. We will use Ajax to update the div so that we don't need to refresh the complete page. But, because we are good programmers, we also respect people that have JavaScript turned off or have a browser that doesn't support JavaScript, if that's the case the form must be posted the normal way. So let's first look at our updated controller:
class HelloController extends atkFrontController { public function indexAction() { $this->name = $this->request['name']; } public function resultAction() { $this->name = $this->request['name']; $this->renderPartial('result'); } }
Still doesn't look that complicated, does it? The result action needs some explanation though. As you can see the last call inside this action is a call to the method renderPartial. As we've seen before if you simply assign some template variables in your action method ATK automatically renders a template with the same name as your action. If you want to (re-)use another template you can call the render method inside your action method and specify a different template. However, you can also choose to call the renderPartial or renderContent method.
If you call the renderPartial method you need to supply a name for the partial template you want to render. A partial template is a template that is prefixed by an underscore ("_"). If you call renderPartial the partial template will be rendered instead of the normal action template and the results will be immediately outputted to the browser. So this means the layout will not be rendered. This makes the renderPartial really useful for Ajax requests where you need to update only a small part of a page and don't want an entire page to be returned.
For even more simple Ajax requests where you only need to return a simple string for example you can use the renderContent method. You need to supply this method with a string you want to output to the browser. Just like with the renderPartial method the renderContent method outputs it's content immediately and terminates the script (without rendering the layout first).
Now that we've created our controller and actions, let's create the template for the index action, named index.tpl:
<form action="{_url}" method='post' onsubmit="new Ajax.Updater('result', '{_url action="result"}', {literal}{ parameters: Form.serialize(this) }{/literal}); return false;">
<input type="text" name="name" value="{$name|escape}" />
<input type="submit" value="Say hello!" />
</form>
{_partial "result"}
Inside the index action template we use some new tags. The {_url} tag renders a string containing an URL to a certain controller action. If you don't specify the controller parameter it assumes you want to use the current controller. So in the example above we render the URL to the result action of the hello controller. The {_partial} tag can be used to include partials in your template. The partial template has all template variables available of the action template but you can choose to override template variables by specifying them as parameters for the {_partial} tag.
We use Prototype's Ajax.Updater to update an element called result with the results of the result action of our controller. We supply the action with all the form parameters using the Form.serialize method. We also return false in the onsubmit so we can be sure the form will not be posted the normal way. However, for people who don't have JavaScript (turned on) the onsubmit will never be called. Instead the form is posted the normal way to the given URL in the action parameter. We used the {_url} tag with no parameters to the URL to the current controller and action. In other words, if the user doesn't have JavaScript we will post the name field to the index action.
The partial result template needs to be named _result.tpl and needs to have the following contents:
<div id="result">
{if $name}Hello {$name}!{/if}
</div>
This template is both used from within the result action (by calling renderPartial) and the index action's template (by using the {_partial} tag). As you can see we check inside this template if the name variable is set and has a non-empty value, if so we render the string "Hello {$name}!". If the user has JavaScript turned on the result action will be called which will assign the name request parameter to the template, so that the partial can immediately use it's value. The non-JavaScript version calls the index action and also supplies it with a request parameter name. The index action assigns this request parameter to the template which will also make it available inside the partial template included using the {_partial} tag.
One last thing we need to do is update the layout template so that the Prototype JavaScript library is included:
<html>
<head>
<title>Hello</title>
<script src="./atk/javascript/prototype/prototype.js"></script>
</head>
<body>
<h1>Hello</h1>
{$content_for_layout}
</body>
</html>
That's it! Try it out (http://localhost/?uri=example/hello/index) and you will see that the result div is updated without a full page refresh. Also try what happens when you turn JavaScript off. The form should still work as expected, but this time a full page refresh will be needed.
Miscellaneous
There is some other stuff you should look at when developing your own controllers. Inside your action method you can not only call one of the render methods but you can also call the redirect method. This method can be used to redirect to other controllers/actions after you are done in your action. You can also call the url method to build an action URL. If you want to change the content type for your action you can call the setContentType method. For more utility methods please look at the PHPdocs for the atkFrontController.
More to come
The atkFrontController is actively being developed. At the moment we are working on adding routing and improving the caching support. This how-to will be updated with more information on this subject as soon as it will be available in ATK.