Create Simple Routing

Open /etc/routing.yml and add a new route resource.

flower:
    pattern: /flower/sakura
    controller: Flower\Controller\Sakura

If you use browser open /flower/sakura, Windwalker will find Flower\Controller\Sakura\GetController and execute it automatically.

The reason we locate GetController is because Windwalker routing supports RESTful protocal for that Get is commonly used as default yaml request method.

If you send Post request then SaveController will be executed instead. This is more efficient and faster to the better routing performance.

Methods supported:

Method Mapped Controller
GET GetController
POST SaveController
PUT SaveController
PATCH SaveController
DELETE DeleteController
HEAD HeadController
OPTIONS OptionsController

NOTE: Windwalker mapped POST, PUT and PATCH to SaveController, which includes both create and update. If you want to separate create and update to two controllers, see next section to override actions.

Use Controller

Now we use a route like this:

flower:
    pattern: /flower/(id)
    controller: Flower\Controller\Sakura

Lets create a controller at path : src/Flower/Controller/Sakura/GetController.php:

<?php
// src/Flower/Controller/Sakura/GetController.php

namespace Flower\Controller\Sakura;

use Windwalker\Core\Controller\Controller;

class GetController extends Controller
{
    protected function doExecute()
    {
        $id = $this->input->get('id');

        return 'Flower id is: ' . $id;
    }
}

Windwalker Controller follows single action pattern (similar to Joomla New MVC), every controller has only one action (execute()). This way, we keep controller itself as light as possible than other frameworks. You can add more logic to a controller but won't be confused by many actions in one class.

You don't have to include above files because Windwalker use autoloading, which follows PSR-4 standard. Go to composer.json you can see the autoloading option is set to "":"/src", so any file under /src folder hierarchy will be loaded automatically.

After controller created, go to /flower/25 at your host. You will see:

Flower id is: 25

Congratulations! Your first page is finished.

Override Actions

Add the action attribute:

flower:
    pattern: /flower/sakura
    controller: Flower\Controller\Sakura
    action:
        get: IndexController

The GET method will match Flower\Controller\Sakura\IndexController because we set a map to find new name. We can set more methods to mapping methods with controllers.

flower:
    pattern: /flower/sakura
    controller: Flower\Controller\Sakura
    action:
        get: IndexController
        post: CreateController
        put: UpdateController
        delete: DeleteController

Or use wildcards to map all methods to one controller:

    action:
        '*': SakuraController

Override Methods

If you want to send PUT and DELETE method from web form, you may add _method params in yaml query, this param will override real HTTP method. For example: &_method=DELETE will raise DeleteController.

If you think the HTTP standard methods are not enough to use for you, you can add your custom methods.

    action:
        export: ExportController

Then use &_method=EXPORT and the ExportController will be executed.

Custom Input Variables

    pattern: /flower/(id)/(alias)
    variables:
        foo: bar

The attributes in variables will auto set to input request if this route be matched and there is no same param name in HTTP query. So if this route matched, you can get foo value in controller:

$this->input->get('foo'); // bar

But if you type /flower/25/alias?foo=yoo, then you will get yoo.

Extra Params

The variables will auto set to input request so it is danger to store some sensitive settings in variables, we can set extra params instead.

    pattern: /flower/(id)/(alias)
    extra:
        layout: grid
        user:
            access: admin

Then you can get this extra params from global config.

$config = Ioc::getConfig();

$config->get('route.extra.user.access'); // admin

// OR in controller

$this->app->get('route.extra.user.access'); // admin

Hooks

You can add match and build hooks to every route.

flower:
    pattern: /flower/sakura
    controller: Flower\Controller\Sakura
    hook:
        match: MyRouteHandler::match
        build: MyRouteHandler::build

The hook example:

use Windwalker\Core\Router\RestfulRouter;
use Windwalker\Router\Route;

/**
 * The MyRouteHelper class.
 *
 * @since  {DEPLOY_VERSION}
 */
class MyRouteHandler
{
    /**
     * Match hook, will execute after route matched.
     *
     * @param RestfulRouter $router   The Router object.
     * @param Route         $route    The route object.
     * @param array         $method   The method to match route.
     * @param array         $options  The options to match route.
     *
     * @return  void
     */
    public static function match(RestfulRouter $router, Route $route, $method, $options)
    {
        // Do something
    }

    /**
     * Build hook, will execute after every route building.
     *
     * @param RestfulRouter $router   The Router object.
     * @param string        $route    The route name.
     * @param array         $queries  The HTTP query to build route.
     * @param string        $type     Build type, 'raw', 'path' or 'full'.
     * @param boolean       $xhtml    Encode special chars or not.
     *
     * @return  void
     */
    public static function build(RestfulRouter $router, &$route, &$queries, &$type, &$xhtml)
    {
        // Do something
    }
}

Limit By Methods

The yaml request will be ingnored according if it did not satisfy the given conditions. For example this config will only allow GET and POST, while PUT and DELETE will be ignored.

flower:
    pattern: /flower/sakura
    controller: Flower\Controller\Sakura
    method:
        - GET
        - POST

Limit By schema

flower:
    pattern: /flower/sakura
    controller: Flower\Controller\Sakura
    method:
        - GET
        - POST
    ## Only http & https
    scheme: http
    post: 80
    sslPort: 443

Route Pattern

Simple Params

Use parenthesis () to wrap param name.

    pattern: /flower/(id)/(alias)

For uri look like : /flower/25/article-alias-name, above pattern will be matched and there will be two input params.

[id] => 25
[alias] => article-alias-name
Limit By Requirement

Use Regular Expression to validate type of input. For example \d+ indicates that only Integer will be accepted as id input.

    pattern: /flower/(id)/(alias)
    requirements:
        id: \d+

Optional Params

Single Optional Params

Use (/{anyparam}) to wrap an Optional Param.

    pattern: flower(/id)

Below 2 uris will be matched simultaneously.

/flower
/flower/25
Multiple Optional Params
    pattern: flower(/year,month,day)

All uris below will be matched.

/flower
/flower/2014
/flower/2014/10
/flower/2014/10/12

Matched variables:

Array
(
    [year] => 2014
    [month] => 10
    [day] => 12
)

Wildcards

Use Wildcards to match all the successive params in uri.

    pattern: /king/(*tags)

Every param after /king will all be matched. For example: /king/john/troilus/and/cressida, will get these variables.

Array
(
    [tags] => Array
    (
        [0] => john
        [1] => troilus
        [2] => and
        [3] => cressida
    )
)

Build Route

Every route in Windwalker has a key, which allows every single route pattern can be access by route name or route resources, this will be helpful building a route quickly.

use Windwalker\Core\Router;

echo Router::build('{route name}', array('id' => 25, 'alias' => 'foo-bar-baz'));

The output will be:

{route name}/25/foo-bar-baz

This is a very useful function that you can change roue name but don't need to worry about invalid link.

For further information, see: Route and Redirect

Matchers

Windwalker Router provides some matchers to use different way to match routes.

You can set matcher name in /etc/config.yml:

routing:
    matcher: default

The default matcher is Sequential Matcher.

Sequential Matcher

Sequential Matcher use the Sequential Search Method to find route. It is the slowest matcher but much more customizable. It is the default matcher of Windwalker Router.

Binary Matcher

Binary Matcher use the Binary Search Algorithm to find route. This matcher is faster than Sequential Matcher but it will break the ordering of your routes. Binary search will re-sort all routes by pattern characters.

Trie Matcher

Trie Matcher use the Trie tree to search route. This matcher is the fastest method of Windwalker Router, but the limit is that it need to use an simpler route pattern which is not as flexible as the other two matchers.

Rules of TrieMatcher

Simple Params

only match when the uri segments all exists. If you want to use optional segments, you must add two or more patterns.

/flower
/flower/:id
/flower/:id/:alias
Wildcards

This pattern will convert all segments after /flower to an array which named tags:

/flower/*tags

If you found a typo or error, please help us improve this document.