Aura.Web: Aura’s Page Controller for MVC

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

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • Kise S.

    I like Aura Framework and specially Aura.SQL, however, Aura.Web seems kind of overkill for simple MVC design.

    • http://harikt.com/ Hari K T

      Kise S, Thank you for the comments and I am happy to hear you like Aura.Framework (which itself uses Aura.Web) and Aura.Sql.
      About your feedback on Aura.Web:
      I don’t really know why you think so. To me, I haven’t found another library which has no dependencies on other packages. Most of them force you to consume more dependencies. If you’ve seen some other library that does the work perfectly let me know.

      If it’s about instantiation, the best thing is to add a factory and let it instantiate the controller.
      Thank you.

  • Thanks

    Thank you for your articles about Aura framework/components.
    However, I feel there is an overwhelming need to have a simple tutorial on how to use Aura from start to finish. A blog, even if overused as an example, it does demonstrate a lot of features present in a given framework. WDUT?

  • http://harikt.com Hari K T

    Hey,
    Thanks for your comments. I understand you completely. But if you check the website auraphp.com, you’ll notice the primary goal of Aura is “to provide high-quality, well-tested, standards-compliant, decoupled libraries that can be used in any codebase” which means building a framework is not the priority. Yes, I know we need to build one, and I am working on it in my free time, but that’s why we are missing a real world sample application. I will publish once it is done.
    Thank you

    • Great

      Hi Hari,
      I’m aware of that. However, the reason to have a comprehensive tutorial is to bring Aurora libraries and its ethos more to the fore of the PHP community. Everyone’s talking about Laravel at the moment. It’s good to hear that a tutorial is in the pipeline.
      Thanks for your fantastic contribution.