Skip to content
Luke Morton edited this page Mar 20, 2014 · 5 revisions

Routing

Now you've grasped the basics, we'll describe Lily\Application\RoutedApplication a fully functional application class you can use as a base for your routed application.

For most web applications you will want some kind of routing. The easiest way to define routes with Lily is to pass your routes into the constructor of Lily\Application\RoutedApplication as an array. A route consists of 3 values: the method, the URI pattern, and a response.

<?php
require __DIR__.'/vendor/autoload.php';

class BlogController
{
    public function index($request)
    {
        return 'Blog!!';
    }
}

$routes = [
    ['GET', '/', 'Hello world'],

    ['GET', '/another', [200, [], 'Hello another']],

    ['GET', '/name/:name', function ($request) {
      return 'Hello '.$request['params']['name'];
    }],

    ['GET', '/blog', [new BlogController, 'index']],
];

$handler = new Lily\Application\RoutedApplication(compact('routes'));
(new Lily\Adapter\HTTP)->run(compact('handler'));
?>

The first parameter request method should be an uppercase string representing the HTTP method it should match e.g. 'GET', 'POST', 'HEAD'. You may also provide the value NULL if you wish to match any method.

The URI pattern is the second parameter. It is a string that represents the URI you want to match. You may provide named parameters by prefixing a param with a colon, for example a request with the URI '/profile/Luke' would match a route URI of '/profile/:name' and $request['params']['name'] would be 'Luke'. This may also be set to NULL if you wish to match any URI.

The third parameter is the response. As you should know from learning the basics responses take the form of an associative array. Route responses can take a few more forms that ultimately get converted into associative arrays:

  • A string body response that is converted to an associative array with a 200 HTTP status
  • A numeric array comprised of a status, headers and body, e.g. [200, [], 'Hello world']
  • An associative array just like handlers passed to Lily\Adapter\HTTP::run()
  • A Callable that must return a response as described in the previous points, e.g. string, numerical array or associative array

Reverse routing

Routes get even more useful when you can reverse them. That is, get the value of a named route somewhere in your application. You can do this with the ::uri() method on the Lily\Application\RoutedApplication object which is passed into the request as $request['app']. ::uri() takes the name of the route and an optional parameter array for populating named parameters.

<?php
require __DIR__.'/vendor/autoload.php';

$routes = [
    'a' => ['GET', '/', function ($request) {
        return '<a href="'.$request['app']->uri('b', ['c' => 'bar']).'">b</a>';
     }],

    'b' => ['GET', '/b/:c', function ($request) {
        return '<a href="'.$request['app']->uri('a').'">a</a>';
     }],
];

$handler = new Lily\Application\RoutedApplication(compact('routes'));
(new Lily\Adapter\HTTP)->run(compact('handler'));
?>

Above you have an example of two pages linking to each other, useful huh?

Using controllers

In the first example above you see the '/blog' URI uses BlogController::index() as it's response handler. Using controller objects in this way allows you to abstract that logic away from the place you define your application and routes. Here's a more detailed example of using controllers.

Please note the following example won't work since not all classes used are defined.

<?php
require __DIR__.'/vendor/autoload.php';

use Blog\Data;
use Blog\Views;
use Blog\Actions;

use Core\View;
use Core\Exceptions\ActionValidationException;

use Lily\Util\Response;

class BlogController
{
    public function index($request)
    {
        return (string) new View('index', new Views\BlogIndexViewModel);
    }

    public function post($request)
    {
        return (string) new View('post', new Views\BlogPostViewModel(Data\BlogPostData::asArray([
            'id' => $request['params']['id'],
        ])));
    }

    public function newPost($request)
    {
        return (string) new View('new-post', new Views\BlogPostViewModel(Data\BlogPostData::asArray([
            'errors' => $request['session']['blogPostCreateErrors'],
            'post' => $request['session']['blogPostCreateData'],
        ])));
    }

    public function create($request)
    {
        $data = [
            'title' => $request['post']['title'],
            'body' => $request['post']['body'],
        ];

        try {
            $id = BlogCreateAction::exec($data);
            $response = Response::redirect(
                $request['app']->uri('post', compact('id')));
        } catch (ActionValidationException $e) {
            $response = Response::redirect($request['app']->uri('newPost'));
            $response['session']['blogPostCreateErrors'] = $e->errors();
            $response['session']['blogPostCreateData'] = $data;
        }

        return $response;
    }
}

$blogController = new BlogController;

$routes = [
    'index'   => ['GET',  '/',         [$blogController, 'index']],
    'post'    => ['GET',  '/post/:id', [$blogController, 'post']],
    'newPost' => ['GET',  '/new',      [$blogController, 'newPost']],
    'create'  => ['POST', '/create',   [$blogController, 'create']],
];

$handler = new Lily\Application\RoutedApplication(compact('routes'));
(new Lily\Adapter\HTTP)->run(compact('handler'));
?>

So although dense with concepts whose description are beyond the scope of this page, the example gives you one view into how you can use controllers with Lily.

Extending RoutedApplication

You may wish to place your routes into an object at some point. To do so you can extend Lily\Application\RoutedApplication and return your routes from a ::routes() protected method.

<?php
require __DIR__.'/vendor/autoload.php';

class MyApplication extends Lily\Application\RoutedApplication
{
    private function aRoute()
    {
         return ['GET', '/', function ($request) {
             return '<a href="'.$request['app']->uri('b', ['b' => 'bar']).'>b</a>';
         }];
    }

    private function bRoute()
    {
         return ['GET', '/b/:b', function ($request) {
             return '<a href="'.$request['app']->uri('a').'>a</a>';
         }];
    }

    protected function routes()
    {
        return ['a' => $this->aRoute(), 'b' => $this->bRoute()];
    }
}

$handler = new MyApplication;
(new Lily\Adapter\HTTP)->run(compact('handler'));
?>

Summary

  • A route is an array that consists of a:
    • request method – e.g. 'GET', 'POST', 'HEAD'
    • URI pattern – e.g. '/', '/profile/:id', '/wiki/:page(/edit)'
    • response – a string, numerical array, associative array or Callable
  • Routes can be reversed with Lily\Application\RoutedApplication::uri()
  • Use controllers to abstract that logic away from your routes
  • Extend Lily\Application\RoutedApplication and implement a ::routes() method if you need to

So there you have it. You're a routing pro now! The next page will explain how to get the most from middleware.

Clone this wiki locally