Aura.Web: Aura’s Page Controller for MVC

Share this article

MVC is an acronym that means Model-View-Controller. In this tutorial I would like to introduce you to Aura.Web, the controller component of the Aura Library. Here I’ll show you how to use it to build your own controller, and also how to use a response transfer object to issue HTTP response headers, and integrate a templating engine like Mustache for rendering views.

Architecture

The controller is the entry point for a page, and communicates with view and model components. Aura.Web helps us to easily build controllers that follow the Page Controller design pattern.

Each page of an application is usually unique, and Aura.Web provides the AuraWebControllerAbstractPage class that provides the basic functionality that we can extend and use. But in order to create an object that extends the AbstractPage class, we need to provide some dependencies, usually through the constructor. The dependencies are:

  1. An instance of an AuraWebContext class that represents the environment.
  2. An instance of an AuraWebAccept class which helps retrieve Accept headers.
  3. An instance of an AuraWebResponse which creates a response transfer object.
  4. An implementation of the AuraWebSignalInterface which is a signal manager to execute hooks (similar to an observer/event handler).
  5. An implementation of the AuraWebRendererRendererInterface to incorporate our rendering system.
  6. A parameter array in which we can pass the action methods.

The Context class accepts an array of global values, which in most cases will be $GLOBALS. From inside the controller we can use the Context object as $this->context, and can access $_GET, $_POST, $_FILES, raw php://input, and JSON decoded values using the object’s methods getQuery(), getPost(), getFiles(), getInput(), and getJsonInput() respectively. We can also check whether the request was made using GET via isGet(), PUT via isPut(), or an Ajax call via isXhr().

Let’s assume a request to http://localhost/?name=Hello is made. An example to get the value of the name parameter from within our controller is:

<?php
$this->context->getQuery('name', 'default value');

The second parameter to getQuery() is optional; it specifies a default value to be returned if the actual value is empty.

The Accept class accepts an array of $_SERVER information. The reason why it’s not just hardcoded in the constructor is to give us the flexibility to pass whatever we like for testing and such. The object is also available in our controller using $this->accept. It’s methods give us the accepted media type as an array via getContentType(), the character set via getCharset(), encoding via getEncoding(), and language via getLanguage(). A basic example from the action would be:

<?php
$this->accept->getContentType();

Note that an array of key/values is returned, similar to:

Array
(
    [text/html] => 1
    [application/xhtml+xml] => 1
    [application/xml] => 0.9
    [*/*] => 0.8
)

You may be familiar with using PHP’s header() function to add values to the HTTP response that is sent back to the client. Instead, a Response object is used as a web response transfer object. The object holds the values which we can pass along, and later convert to a proper HTTP response using tools like Aura.Http.

The Response object is also made available in the controller via $this->getResponse(). The object lets us set the response’s body content via setContent(), HTTP header values via setHeader(), cookies via setCookie(), and a redirect header via setRedirect(). We can also set the HTTP status code via setStatusCode() and the status text via the setStatusText() methods.

Here’s what extending and using an AbstractPage object looks like:

<?php
namespace SitePointTutorialWebController;
use AuraWebControllerAbstractPage;

class Index extends AbstractPage
{
    public function actionGreet() {
        $this->response->setContent(
            '<html>' . 
            '<head><title>Aura web controller</title></head>' .
            '<body>Hello World!</body>' . 
            '</html>'
        );
    }
}
<?php
use AuraWebContext;
use AuraWebAccept;
use AuraWebResponse;
use AuraWebSignal;
use AuraWebRendererNone as Renderer;
use SitePointTutorialWebControllerIndex;

$page = new Index(
    new Context($GLOBALS),
    new Accept($_SERVER),
    new Response(),
    new Signal(),
    new Renderer(),
    [
        'action' => 'greet',
    ]
);
$response = $page->exec();
echo $response->getContent();

The array of parameters passed as the last argument to our extended AbstractPage specifies which actions need to be called, which format needs to be passed to the rendering strategy, and any other parameters for the action method.

In the execution cycle initiated by exec(), the following are invoked:

  1. pre_exec, a hook which calls the page’s preExec() method.
  2. pre_action, a hook calling the preAction() method.
  3. action() to find and invoke the action method (it actually creates a Reflection class to retrieve the parameters for the method and then calls it).
  4. post_action, a hook calling the postAction() method.
  5. pre_render, a hook which calls the preRender() method.
  6. render() to render the view.
  7. post_render, a hook calling the postRender() method.
  8. post_exec, a hook which calls the postExec() method.

Rendering

In the example above we explicitly set the content in the controller, but this is not really the best way to organize our code. The view should be separated. Aura.Web doesn’t provide a rendering strategy by default, so it’s easy to integrate any rendering strategy we like. Here I’ll use Mustache.

To create a rendering strategy, we need to extend the AuraWebRendererAbstractRenderer class, in which we define the exec() method. The controller is available to us in the rendering strategy via $this->controller.

<?php
namespace SitePointFrameworkWebRenderer;
use AuraWebRendererAbstractRenderer;

class Mustache extends AbstractRenderer
{
    protected $mustache;

    public function __construct($mustache) {
        $this->mustache = $mustache;
    }

    public function exec() {
        $format = $this->controller->getFormat();
        if (! $format) {
            $format = '.html';
        }
        $response = $this->controller->getResponse();
        if (!$response->getContent()) {
            $data    = (array)$this->controller->getData();
            $view    = strtolower($this->controller->getAction());
            $lastval = basename(
                str_replace('\', '/', strtolower(
                    get_class($this->controller)
                ))
            );
            $file =  $lastval . '/' . $view . $format;
            $response->setContent(
                $this->mustache->render($file, $data)
            );
        }
        $response->setContentType($this->getContentType($format));
    }

    public function getContentType($format) {
        $mimetypes = [
            '.json' => 'application/json',
            '.xml'  => 'application/xml',
            '.html' => 'text/html',
            '.htm'  => 'text/html'
        ];
        return array_search($format, $mimetypes);
    }
}

I’ve made the assumption that we’re keeping all of the Mustache templates saved using the convention <controller name>/<action name>.<format>, where the folders mirror <controller name> and <action name>.<format> is the template’s filename. For example, a controller class Example with the action hello would find it’s template in example/hello.<format>.

Building HTTP Responses

We still haven’t built a proper HTTP response, so let’s see how we can do that now. Once we call the execution cycle of the controller with it’s exec() method we will get back a response transfer object.

The object contains the HTTP status code, status text, cookies, and header values. We can build the HTTP response from it with code similar to that given below:

<?php
$response = $page->exec();

// header('Status: 200 OK');
$statusCode = $response->getStatusCode();
$statusText = $response->getStatusText();

$response->getVersion();
$headers = $response->getHeaders();
foreach ($headers as $header => $value) {
    // header('Content-Type: text/html; charset=utf-8');
    header($header . ': ' . $value);
}

$cookies = $response->getCookies();
foreach ($cookies as $name => $cookie) {
    setcookie(
        $name,
        $cookie['value'],
        $cookie['expire'],
        $cookie['path'],
        $cookie['domain'],
        $cookie['secure'],
        $cookie['httponly']
    );
}

$contentType = $response->getContentType();
if (!$contentType) {
    $contentType = 'text/html; charset=utf-8';
}
header('Content-Type: ' . $contentType);
echo $response->getContent();

I’ve demonstrated only a pure PHP implementation so it’s easy for everyone to understand, but ideally we would use something like Aura.Http or another library which provides the necessary functionality for us.

Conclusion

In this article I’ve covered the basic working principles of Aura.Web, and also showed how we can integrate a rendering strategy and how to build proper HTTP responses. You can use the power of a routing library like Aura.Router which I discussed earlier to dynamically call the controller. Maybe in a future article I’ll show how to integrate all of this and build your own framework from Aura components. Stay tuned!

Image via Fotolia

Hari K THari K T
View Author

Hari K T is a Freelance LAMP developer/consultant, open-source contributor auraphp and speaker.

Advanced
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week