Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
add post
  • Loading branch information
Paul M. Jones committed Dec 12, 2013
1 parent d970a6f commit fdf294f
Showing 1 changed file with 255 additions and 0 deletions.
255 changes: 255 additions & 0 deletions _posts/2013-12-12-aura-v2-web-project.markdown
@@ -0,0 +1,255 @@
---
title: A Peek At Aura v2 -- Aura.Web_Project, The Micro/Macro Web Framework
layout: post
tags : [v2, web, project]
author : Paul M. Jones
---

(In this series, we have been discussing the improvements over Aura v1 that
are being incorporated into v2.)

Lighter than [Silex][], and slimmer than [Slim][], the [Aura.Web_Project][]
package is a minimalist web-specific project framework that starts out small
and grows only as you need it.

By "minimal" we mean *very* minimal. The project package provides only a
[dependency injection container][Aura.Di v2],
a [configuration system][Aura.Project_Kernel],
a [router][Aura.Router v2],
a [dispatcher][Aura.Dispatcher],
a pair of [request and response][Aura.Web v2] objects,
and a [Monolog][] instance for logging.

This minimal implementation should not be taken as "restrictive":

- The DI container, coupled with the project kernel's two-stage configuration,
allows a wide range of programmatic service definitions. This means *no
more static calls* to configure services; edit the `modify` stage config
files, pull a service out of the container, and operate on it directly.

- You can add *any* library you want into the project, not just Aura
libraries, through Composer and the DI container. This means the
bundle/plugin universe is made up of **anything written in PHP**, not just
Aura-specific plugins.

- As noted in earlier articles in this series, the [router][] and [dispatcher][] are
completely separated from each other, and are built with iterative
refactoring in mind. This means you can start with micro-framework-like
closure controllers, and work your way into more complex controller objects
of your own design.


### Projects? Kernels? Dependencies?

It had to happen at some point. Unlike Aura *library* packages, the *project*
and associated *kernel* packages have dependencies. This is something we
can't get around; the idea behind Aura has always been to build its framework
out of the independent library packages, but of course that means the
framework itself is not independent of the libraries.

(The `Aura.*_Project` packages are system skeletons, providing a directory
structure for the proejct. The `Aura.*_Kernel` packages bring together
the libraries necessary for the project skeleton.)

### Getting Started

You can start a new [Aura.Web_Project][] via [Composer][] in a
`{$PROJECT_PATH}` of your choosing:

composer create-project --stability=dev aura/web-project {$PROJECT_PATH}

This will create the project skeleton and install all of the necessary
library packages.

Once you have installed the project, start the built-in PHP server inside the
new project web directory:

cd {$PROJECT_PATH}
php -S localhost:8000 -t web/

When you browse to <http://localhost:8000> you should see "Hello World!" as
the output.


### Controllers: Micro/Macro?

When we say "micro/macro", we mean that the system starts out as a the most
micro of micro-frameworks, but is built with the idea that eventually you may
want to refactor your architecture to a full-stack type of system.

#### Micro Controller

For example, you can start by embedding your controller logic directly in
the router system ...

{% highlight php %}
<?php
/**
* {$PROJECT_PATH}/config/default/modify/router.php
*/
$request = $di->get('web_request');
$response = $di->get('web_response');
$router->add('blog.read', '/blog/read/{id}')
->addValues(array(
'controller' => function ($id) use ($request, $response) {
$content = "Reading blog post $id";
$response->content->set(htmlspecialchars(
$content, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8'
));
}
));
?>
{% endhighlight %}

#### Macro Controller

You can then migrate that to a more full-stack system (or you can
start with a full stack style in the first place). This is more complex but
is much more powerful, maintainable, and testable in the long run.

First, define a controller class and place it in the project `src/` directory.

{% highlight php %}
<?php
/**
* {$PROJECT_PATH}/src/App/Controllers/BlogController.php
*/
namespace App\Controllers;
use Aura\Web\Request;
use Aura\Web\Response;
class BlogController
{
public function __construct(Request $request, Response $response)
{
$this->request = $request;
$this->response = $response;
}

public function read($id)
{
$content = "Reading blog post $id";
$this->response->content->set(htmlspecialchars(
$content, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8'
));
}
}
?>
{% endhighlight %}

(Note that you don't need to extend a base controller here. The
[Aura.Web_Project][] package does not force any specifici controller
methodology on you. Of course, if you want to, you can create your own base
controllers and extend them.)

Next, tell the project how to build the _BlogController_ through the DI
system. Edit the project `config/default/define.php` config file to tell the
DI system to pass _Request_ and _Response_ objects to the constructor.

{% highlight php %}
<?php
/**
* {$PROJECT_PATH}/config/default/define.php
*/
$di->params['App\Controllers\BlogController'] = array(
'request' => $di->lazyGet('web_request'),
'response' => $di->lazyGet('web_response'),
);
?>
{% endhighlight %}

After that, put the _App\Controllers\BlogController_ object in the dispatcher
under the name `blog` as a lazy-loaded instantiation ...

{% highlight php %}
<?php
/**
* {$PROJECT_PATH}/config/default/modify/dispatcher.php
*/
$dispatcher->setObject('blog', $di->lazyNew('App\Controllers\BlogController'));
?>
{% endhighlight %}

... and finally, point the router to the `blog` controller object and its
its `read` action:

{% highlight php %}
<?php
/**
* {$PROJECT_PATH}/config/default/modify/dispatcher.php
*/
$router->add('blog.read', '/blog/read/{id}')
->addValues(array(
'controller' => 'blog',
'action' => 'read',
));
?>
{% endhighlight %}


### Configuration?

As with the Aura v1, configuration files are located in
`{$PROJECT_PATH}/config` directory. Previously, they were single files,
labaled for the config mode. In Aura v2, config files are organized into
subdirectories by config mode. (The config mode is still stored in the `_mode`
file in the config directory.)

Whereas Aura v1 projects used a single-stage config system, loading up the
individual config files to define params, setters, and services, Aura v2
projects use a two-stage configuration system:

1. First, all `define.php` files are included from the packages and the
project; these define constructor parameters, setter methods, and shared
services through the DI container.

2. After that, the DI container is locked, and all `modify.php` files are
included; these are for retrieving services from the DI container for
programmatic modification.

(The `default` mode directory is always loaded; if the mode is something other
than `default` then the files in that directory will be loaded after `default`.)

This two-stage process provides a very powerful system for configuring any and
all PHP libraries, especially those that have been registered as services in
the DI container. However, this power comes with some level of complexity; the
rules are simple, but they lead to complex combinations and effects. As such,
will write about the configuration system in specific at a later time.

### Adding Functionality

Adding new functionality is as easy as modifying the project `composer.json`
file, doing `composer update`, and then changing your config files to build
the new library objects properly. If you are adding an Aura library, the
system knows enough to configure it automatically via the library package's
`config/` directory, but *any* reasonably well-structured PHP library can be
included and configured.


### Conclusion

The point of [Aura.Web_Project][] is to provide an absolute minimal framework
for building web apps in a way that promotes separation of concerns along with
a refactorable architecture. The DI container and two-stage configuration
system are the key components here, with the fully-separated router and
dispatcher layered on top of them.

If you like clean code, fully decoupled libraries, and truly independent
packages, then [the Aura project][Aura] is for you. Download a single package
and start using it in your project today, with no added dependencies.


[Aura.Dispatcher]: https://github.com/auraphp/Aura.Dispatcher
[Aura.Project_Kernel]: https://github.com/auraphp/Aura.Project_Kernel
[Aura.Router v2]: https://github.com/auraphp/Aura.Router/tree/develop-2
[Aura.Web v2]: http://github.com/auraphp/Aura.Web/tree/develop-2
[Aura.Web_Project]: https://github.com/auraphp/Aura.Web_Kernel
[Aura]: http://auraphp.com
[Silex]: http://silex.sensiolabs.org
[Slim]: http://www.slimframework.com
[Solar]: http://solarphp.com
[Monolog]: https://github.com/Seldaek/monolog
[Composer]: http://getcomposer.org
[router]: /blog/2013/11/18/aura-v2-router
[dispatcher]: /blog/2013/11/04/aura-v2-dispatcher

0 comments on commit fdf294f

Please sign in to comment.