About myself

Ben Scholzen is …

  • Freelancing Software Architect & Consultant
  • Author and maintainer of core components in Zend Framework
  • Open Source Contributor

The basic principle of routing

  • Matching a request and extracting parameters
  • Assembling new requests

The gibs of all routes

namespace Zend\Mvc\Router;
use Zend\Stdlib\RequestInterface as Request;

interface RouteInterface
{
    public static function factory($options = array());

    public function match(Request $request);

    public function assemble(
        array $params = array(),
        array $options = array()
    );
}
      

Route stacks

Using multiple routes together

Route stacks

  • Collection of routes to match or assemble
  • Based on priority stacks
  • Implement the route interface
  • Types:
    • SimpleRouteStack
    • Http\TreeRouteStack
    • Console\SimpleRouteStack

The Http\TreeRouteStack

  • Takes care of base URL handling
  • Provides functionallity for tree routing
  • Helps assembling canocial URLs

Creating a new route stack

use Zend\Mvc\Router\Http\TreeRouteStack;

$router = TreeRouteStack::factory(array(
    'routes' => array(
        // …
    )
));
      

or

$router = new TreeRouteStack();
$router->addRoutes(array(/* … */));
      

Handling the base path

  • Base URL is used as offset for all routes
  • Usually automatically set by ZF2
$router->setBaseUrl('/my/application');
      

Canonical URLs and the request URI

  • Used for assembling canonical URLs without hostname routes
  • Usually automatically set by when Router::match() is called
use Zend\Uri\Http as HttpUri;

$router->setRequestUri(new HttpUri('http://example.com'));
      

Default parameters

  • One to rule them all
  • Passed to any route when assembling
  • Added to any match if not already set
$router->setDefaultParam('key', 'value);
      
$router->setDefaultParams(array(
    'key' => 'value'
));
      

Assembling a route

  • Route name is passed as option to the router
$url = $router->assemble(
    array(), // Params
    array('name' => 'bacon') // Options
);

echo $url; // /bacon
      

Assembling a canonical URL

$url = $router->assemble(
    array(), // Params
    array(
        'name' => 'bacon',
        'force_canonical' => true,
    )
);

echo $url; // http://example.com/bacon
      

Matching a request

use Zend\Http\PhpEnvironment\Request;

$request    = new Request();
$routeMatch = $router->match($request);

$routeMatch->getMatchedRouteName();
$routeMatch->getParams();
$routeMatch->getParam($name, $default = null);
      

Path routes

The basic route types

Literal route

  • Simplest route type
  • Matches and assembles literal strings
  • Does not match any parameters

Literal route (example)

array(
    'articles' => array(
        'type' => 'literal',
        'options' => array(
            'route' => '/articles',
            'defaults' => array(
                'controller' => 'Application\Controller\Article',
                'action'     => 'index',
            )
        )
    )
)
      

Regex route

  • Fastest parameter-matching route type
  • Matches based on a regular expression
  • Assembles based on a replacement pattern

Regex route (example)

array(
    'article-details' => array(
        'type' => 'regex',
        'options' => array(
            'route' => '/articles/(?P<id>\d+)',
            'spec' => '/articles/%id%',
            'defaults' => array(
                'controller' => 'Application\Controller\Article',
                'action'     => 'details',
            )
        )
    )
)
      

Wildcard route

  • Matches an unlimited amount of key/value pairs
  • Allows to modify key/value and parameter delimiters
  • Will assemble any parameters not used by parent routes
  • Not suggested to be used on public sites due to duplicate content
    • /controller/index/action/index
    • /action/index/controller/index
  • Also has the disadvantage of no validation

Wildcard route (example)

array(
    'wildcard' => array(
        'type' => 'wildcard',
        'options' => array(
            'key_value_delimiter' => '/',
            'param_delimiter'     => '/'
        )
    )
)
      

The hero: segment route

  • Most flexible route type
  • Very fast matching, as segment patterns are internally converted to regular expressions
  • Provides parameter matching based on delimiters and constraints
  • Allows optional segments (both literal and parameter parts)

A basic segment example

array(
    'article-details' => array(
        'type' => 'segment',
        'options' => array(
            'route' => '/articles[/:id]',
            'constraints' => array('articles' => '\d+'),
            'defaults' => array(
                'controller' => 'Application\Controller\Article',
                'action'     => 'index',
            )
        )
    )
)
      

Structure of segment patterns

  • Optional parts …
    • … are denoted by square brackets
    • … can be nested
    • … can contain literal strings and parameters
  • Parameters are prepended and optionally appended with a colon
  • Default delimiter for parameters is a forward slash, but can be changed with curly brackets

Examples of segment patterns

  • /articles[/page-:pageNo]
    Matches "/articles" and "/articles/page-1"
  • /articles/[page-:pageNo]
    Matches "/articles/" and "/articles/page-1"
  • /articles/:id{-}-:slug
    Matches "/articles/194-my-awesome-article", but also "/articles/foo/bar/baz-my-awesome-article"
  • /conferences/:conference{0-9}::year
    Matches "/conferences/zendcon2012" and "/conferences/dpc2010"

Tree routing

Or how to combine your routes

The history of routing

Traditional routing

/controller/action/key1/value1/key2/value2 …

  • Advantages
    • Easy to use
  • Disadvantages
    • Very inflexible
    • Neither search-engine nor user friendly
  • ZF < 0.6
  • ZF 0.6 – 1.5
  • ZF 1.6 – 1.12
  • ZF 2.0

The history of routing

Fixed routing

  • Advantages
    • Very flexible
    • Search-engine and user friendly
  • Disadvantages
    • Lots of repetition
    • Slow, as many single routes need to be tested
  • ZF < 0.6
  • ZF 0.6 – 1.5
  • ZF 1.6 – 1.12
  • ZF 2.0

The history of routing

Chain routing

  • Advantages
    • Easy to set up
    • Chains themself were hidden
  • Disadvantages
    • Lots of repetition
    • Slow, as internally, every repeated branch was tested over and over
  • ZF < 0.6
  • ZF 0.6 – 1.5
  • ZF 1.6 – 1.12
  • ZF 2.0

The history of routing

Tree routing

  • Advantages
    • Easy to set up
    • Great performance
  • Disadvantages
    • None known so far
  • ZF < 0.6
  • ZF 0.6 – 1.5
  • ZF 1.6 – 1.12
  • ZF 2.0

How tree routing works

  • Every route can have an unlimited number of child routes
  • Child routes are only instantiated when the parent route matches or assembles

Tree specific routes

namespace Zend\Mvc\Router\Http;
use Zend\Mvc\Router\RouteInterface as BaseRoute;

interface RouteInterface extends BaseRoute
{
    public function getAssembledParams();
}
      

The part route (classified internals)

  • Automatically created whenever a route has defined child routes
  • Consists of a base route and multiple child routes
  • Usually never used directly by the user

Combining some routes

array(
    'articles' => array(
        'type' => 'literal',
        'options' => array(
            'route' => '/articles', 'defaults' => array(
                'controller' => 'Application\Controller\Article',
                'action' => 'index'
            )
        ),
        'may_terminate' => true,
        'child_routes' => array(
            'details' => array(
                'type' => 'segment', 'route' => '/:id',
                'constraints' => array('articles' => '\d+'),
                'defaults' => array('action' => 'details')
)   )   )   )
      

Assembling child routes

  • Names of parent and child routes are separated with a forward slash
$router->assemble(
    array(),
    array('name' => 'articles/details')
);
      

Advanced route types

Matching everything which is not in the path

Hostname route

  • Allows matching and assembling hostnames
  • Provides simple parameter matching

Hostname route (example)

array(
    'user' => array(
        'type' => 'hostname',
        'options' => array(
            'route' => ':user.users.example.com',
            'defaults' => array(
                'controller' => 'Application\Controller\User',
                'action'     => 'index',
            )
        )
    )
)
      

Scheme route

  • Matches and assembles a specific scheme (http, https, …)

Scheme route (example)

array(
    'secure' => array(
        'type' => 'scheme',
        'options' => array(
            'scheme' => 'https'
        )
    )
)
      

Method route

  • Matches one or more specific methods (GET, POST, PUT, …)
  • Will do nothing on assembling

Method route (example)

array(
    'non-get' => array(
        'type' => 'method',
        'options' => array(
            'verb' => 'post,put'
        )
    )
)
      

Query route

  • Converts any query parameter into a route match
  • Like the wildcard route, it will assemble any parameters not used by parent routes

Query route (example)

array(
    'query' => array(
        'type' => 'query'
    )
)
      

The router in your application

Making actual use of it

Defining routes in your config

return array(
    'router' => array(
        'routes' => array(
            // …
        )
    )
);
      

Getting parameters in the controller

class BaconController extends AbstractActionController
{
    public function indexAction()
    {
        $id = $this->params('id'/*, null */);
    }
}
      

Redirecting within your controller

class BaconController extends AbstractActionController
{
    public function indexAction()
    {
        $this->redirect()->toRoute(
            'route-name',
            array(), // Params
            array()  // Options
        );
    }
}
      

Assembling URLs in your controller

class BaconController extends AbstractActionController
{
    public function indexAction()
    {
        $url = $this->url(
            'route-name',
            array(), // Params
            array()  // Options
        );

        $this->redirect()->toUrl($url . '#hash-tag');
    }
}
      

Assembling URLs in your view

<a href="<?php echo $this->url(
    'route-name',
    array(), // Params
    array()  // Options
); ?>">Some Link</a>
      

A look into the future (2.1+)

  • Route aliases
  • Translatable segments
  • Locale detection

Question?

Whatever you wanna know.