Skip to content
taiwen edited this page Mar 14, 2013 · 1 revision

Custom Router Guide

Contents

  • Introduction
  • Creating configuration file
  • Route Class
  • A Simple Case To Create Custom Route

Introduction

What is a router? Before answer this question, we should introduce something else. Generally, we access a page by inputting the file path into browser such as localhost/index.html, this is almost used in static pages. In dynamic page, especial in MVC, a page is define as a controller/action, so user can not use file name as url path to access the page. Users must put the controller and action information into the url and then framework will resolve the url and then access the right controller/action, and return data. A router is such a method used to assemble url by passed parameter and resolve the url to right action.

The following flow chart is a simple introduction on url resolving of Pi:

Create router instance -> Reading all routers from database -> Trigger route event

Route event: this event will match all existing router with current url, if the url is match correctly, the router and passed parameters will be saved into Zend\Mvc\Router\RouteMatch instance.

If a user want to create custom router, the following thing must be done:

  1. Adding a route configuration in module;
  2. Creating route class for url assembling and route resloving;
  3. Assembling url.

Creating configuration file

A configuration is generally known as route.php in config folder, this configuration will helps defining basic route information and adding router into database (core_route table). Therefore the application will get the router and add it into routers parameter for later use.

config/route.php

return array(
    'default'   => array(
        'section'   => 'front',
        'priority'  => -999,

        'type'      => 'Standard',
        'options'   =>array(
            'structure_delimiter'   => '/',
            'param_delimiter'       => '/',
            'key_value_delimiter'   => '-',
            'defaults'              => array(
                'module'        => 'system',
                'controller'    => 'index',
                'action'        => 'index',
            )
        )
    ),
);

In this code, the default field is the route name:

  • section - decides which section's action to request, it can be front, admin and feed;
  • priority field - defines the priority of the url resolving, the smaller digit the lower priority, generally we recommend set lower priority to the common router.

The type field defines which class is used to resolve the url, there has three types recommend, which are: Standard, Home and User. If user want to create custom router, other name should be used, such as: Module\Channel\Route\Channel.

The options array defines how to display the url:

  • structure_delimiter, param_delimiter and key_value_delimiter - defines the delimiter of url, it will be used in router class.
  • defaults array - describes the default action to request if there is not set the controller and action field in navigation file.

Route Class

A route class is custom class inherit from Pi\Mvc\Router\Http\Standard class. This class contains two method which is used to assemble url and match url, it will be call automatically when application runs.

A module custom route class should be created in the Route folder of module, such as:

usr
 |-- module
      |-- channel
           |-- src
                |-- Route
                     |-- Channel.php

This class must has a name match to that declares in configuration files. This structure of this class is:

namespace Module\{Module name}\Route;
use Pi\Mvc\Router\Http\Standard;
use Zend\Mvc\Router\Http\RouteMatch;
use Zend\Stdlib\RequestInterface as Request;
use Pi;

class {Route name} extend Standard
{
    protected $prefix = '';

    protected $defaults = array(
        'module'     => {module name},
        'controller' => {controller name},
        'action'     => {action name},
    );

    public function match(Request $request, $pathOffset = null)
    {
        $result = $this->canonizePath($request, $pathOffset);
        // Route not match
        if (null === $result) {
            return null;
        }
        list($path, $pathLength) = $result;

        // Add yourselves route matching codes
        ...

        // Route matched
        return new RouteMatch(array_merge($this->defaults, $matches), $pathLength);
    }

    public function assemble(array $params = array(), array $options = array())
    {
        $mergedParams = array_merge($this->defaults, $params);
        if (!$mergedParams) {
            return $this->prefix;
        }

        // Adding yourselves assemble codes
        ...

        return $this->paramDelimiter . $url;
    }
}

In the class the prefix and defaults parameters will be overridden by the value defines in configuration. The prefix parameter is used to assemble url, and it can be null, the defaults parameter defines the default action to access if no controller and action is resolved in match method.

The match method will override the same method define in Standard class, it is used to match url. In the method, the canonizePath method is used to resolve path and its length.

The return null will tell the application the url not match this route, and application will choose the next route to match, and the return new RouteMatch tell the application this route is match with the url, and the resolving data will be passed to the RouteMatch instance.

note: the $match parameter contain resolving data, such as action, module or other parameters post by GET method.

In the assemble method, parameters passed to assemble url are assign to $params, users can assemble url by these parameters and return.

A Simple Case To Create Custom Route

In this case, we will introduce how to add a custom route step by step. A demo module will be took as an example, and its Slug route will resolve a url such as:

  1. url/$id-$slug;
  2. url/$id;
  3. url/$slug.

defining configuration files

module/demo/config/route.php

return array(
    'slug'  => array(
        'section'   => 'front',
        'priority'  => 1,

        'type'      => 'Module\\Demo\\Route\\Slug',
        'options'   => array(
            'prefix'    => '/demo-route',
            'defaults'  => array(
                'module'        => 'demo',
                'controller'    => 'route',
                'action'        => 'slug'
            ),
        ),
    ),
);

module/demo/config/module.php

The configuration file should be added into module.php after create.

return array(
    ...
    'route'     => 'route.php',
);

Then a route class should be created to achieve route assembling and matching. Creating the following file and add codes.

module/demo/src/Route/Slug.php

namespace Module\Demo\Route;
use Pi\Mvc\Router\Http\Standard;
use Zend\Mvc\Router\Http\RouteMatch;
use Zend\Stdlib\RequestInterface as Request;

class Slug extends Standard
{
    protected $prefix = '/demo-route';

    protected $defaults = array(
        'module'        => 'demo',
        'controller'    => 'route',
        'action'        => 'slug'
    );

    public function match(Request $request, $pathOffset = null)
    {
        $result = $this->canonizePath($request, $pathOffset);
        if (null === $result) {
            return null;
        }
        list($path, $pathLength) = $result;
        if (empty($path)) {
            return null;
        }

        // Adding matching codes
        list($id, $slug) = array(null, null);
        if (false === ($pos = strpos($path, '-'))) {
            if (is_numeric($path)) {
                $id = $path;
            } else {
                $slug = $path;
            }
        } else {
            list($id, $slug) = explode('-', $path, 2);
            if (!is_numeric($id)) {
                $id = null;
                $slug = $path;
            }
        }

        // Assigning match parameters
        $matches = array(
            'action'        => (null === $slug) ? 'id' : 'slug',
            'id'            => $id,
            'slug'          => urldecode($slug),
        );

        return new RouteMatch(array_merge($this->defaults, $matches), $pathLength);
    }

    public function assemble(array $params = array(), array $options = array())
    {
        $mergedParams = array_merge($this->defaults, $params);
        if (!$mergedParams) {
            return $this->prefix;
        }
        $url = isset($mergedParams['id']) ? intval($mergedParams['id']) : '';
        if (isset($mergedParams['slug'])) {
            $url .= ($url ? '-' : '') . urlencode($mergedParams['slug']);
        }

        return $this->paramDelimiter . trim($this->prefix, $this->paramDelimiter) . $this->paramDelimiter . $url;
    }
}

So far, we have achieved the custom route Slug, users should reinstall the module to enable this route.

Now let's learn how to get custom url, it is very simple by using $this->url() in action method or phtml file.

In action and template:

echo $this->url('demo-slug', array('id' => 88453));
echo $this->url('demo-slug', array('slug' => 'hello'));
echo $this->url('demo-slug', array(
    'id'   => '63354',
    'slug' => 'nice',
));

The codes will call assemble method, and output:

'domain/demo-route/88453'
'domain/demo-route/hello'
'domain/demo-route/63354-nice'

We can notice that, the route name is consist by module name and route name, if the above code is used in demo module, the demo- can be replaced by ..

In demo module:

$this->url('.slug', array('id' => 88453));

If assemble url is not realize in action and template, using the following code:

Pi::engine()->application()
            ->getRouter()
            ->assemble(array('id' => 88453, array('name' => 'demo-slug')));
Clone this wiki locally