Routing for Yii Demystified

In this article by Suresh Kumar Mukhiya and Charles Portwood, the authors of the book, Building a Game with Unity and Blender, we will master the concepts of routing using Yii.

Like many modern web frameworks, Yii2 is built with a powerful router component, which we can utilize to handle a variety of URIs coming from both our end users and application. This functionality is further enhanced by Yii2's powerful request and response handlers, which we can use to manipulate both the request and response bodies. In this chapter, we'll cover the basics of how to manipulate Yii2's URL Manager for adjusting routes, explore how to configure Yii2 to respond in different ways, and learn how to send and listen to events within our application.

(For more resources related to this topic, see here.)

Routing

Routing within Yii2 is managed by the UrllManager component defined in our application configuration. The router in Yii2 is responsible for determining where Yii2 routes external URI requests to internal controllers and actions. In this article, we'll cover how Yii2 knows how to route those requests inside of our application by exploring Yii2's UrlManager in more detail.

Routing in Yii2 can be broken down into two basic steps. The first of which is to parse the incoming request and query parameters (by default which are stored in the GET parameters of our request with the r parameter, but can be retrieved from the request URI if we have pretty URLs enabled). The second is to create an instance of the corresponding controller action, which will ultimately handle the request.

By default, Yii2 will break the route down on the forward slashes of the URL to map it to the appropriate module, controller, and action pair. For instance, the route site/login would match to the controller site, and the action named login in the default module of the application instance. Internally Yii2 will take the following steps to route the request:

  1. By default, Yii2 will set the current module to be the application.
  2. Examine the controller map of the application to see if it contains the current route. If so, a controller instance will be created according to the controller map defined within the module, at which point the action will be created according the action map defined in step four. Yii2 will, by default, create a controller map based upon the controllers found within the @app/controllers folder, but this may be customized within the module (or the UrlManager).
    @app/controllers folder, but this may be customized within the module (or the UrlManager).
    yii\base\Module::$controllerMap = [
        'account' => 'app\controllers\UserController',
        'post' => [
             'class' => 'app\controllers\ContentController'
        ],
    ]
    
  3. If the controller map of the application module is not found within the application module, Yii2 will iterate through the modules list in the modules property of the application module to see if a route matches there. If a module is found, Yii2 will instantiate the module using the provided configuration, and then create the controller using the details outlined in the previous step.

Yii2 will then look for the action within the action map defined in the module's configuration. If found, it will create an action according to that configuration, otherwise it will attempt to create an inline action defined in the action method corresponding to the given action.

If at any point during this process an error occurs, Yii2 will throw yii\web\NotFoundHttpException.

Default and catch all routes

When Yii2 receives a request that is parsed into an empty route, the default route will be used instead. The default route is set to be site/index, which references the index action of the site controller. This behavior can be changed by setting the defaultRoute property of the application component as follows.

[
    .// index action of main controller
    .'defaultRoute' => 'main/index' 
]

Additionally, Yii2 can be configured to forward all requests to a single route by setting the catchAll property of yii\web\application. This can be beneficial when needing to perform application maintenance.

[
    // Display a maintenance message
    'catchAll' => 'site/maintenance'
]

Custom routes and URL rules

Rather than relying upon the default controller/action routes Yii2 internally generates, custom URL rules can be written to define our own URL routes. URL routes in Yii2 are implemented by an instance of yii\web\UrlRule, and consist of a pattern used for patching the path information and query parameters of a given route. When using custom URL rules, Yii2 will route request to the first matching rule for the accompanying request. Moreover, the matching rule determines how the request parameters are split up. Additionally, when using the yii\helpers\Url helper will also rely upon the list rules to internally route requests.

URL rules in Yii2 can be defined in our application configuration by setting the yii\web\UrlManager:$rules property as an array with key containing the URL pattern to match and the value being the corresponding route. For example, supposing we had a controller for managing published content we could write custom rules as follows to route posts and post to our content:

[
    'posts' => 'content/index', 
    'post/<id:\d+>' => 'content/view',
]

Now when navigating to the /posts endpoint of our application, the content/index controller action pair will be triggered. As shown in the previous example, URL rules can extend beyond simple strings, and can contain complex regular expressions, which we can use to conditionally route rules. In the previous example, a route to the endpoint /post followed by an integer ID will route to the content/view controller action pair. Moreover, Yii2 will automatically passing the parameter $id to the action.

class ContentController extends yii\web\Controller
{
    // Responds to content/index and /posts
    public function actionIndex() {}

    // Responds to content/view/id/<id> or /post/<id>
    public function actionView($id) {}
}

Regular expressions can only be specified for parameters. However, as we'll see later in this section we can parameterize our routes to make the controller and action more dynamic.

These regular expressions can be further customized to include more complex routes. For instance, adding the following to our URL routes would enable us to pass additional information to our content/index action, such as the year, month, and day we want to show published entries for.

[
    'posts/<year:\d{4}>/<month:\d{2}>/<day:\d{2}>' 
    => 'content/index',
]

As you may expect from the expression, this route will only match four digit years, and two digits months and days. Moreover, as mentioned previously, by adding this information to our URL rules, the yii\helper\Url helper will additionally understand any URL created with this pattern.

// Routes to posts/2014/09/01
Url::to(['posts/index', 'year' => 2014, 'month' => 09, 'day' 
=> 01]);

URL routes can also be defined to route domain names and schemes. For instance, the following routes could be written to ensure that different domain names route to different parts of the site.

[
    'https://dashboard.example.com/login' => 
    'dashboard/default/login',
    'https://www.example.com/login' => 'site/login'
]

This is beneficial when handling multiple front-end applications within the same codebase.

Parameterizing routes

In addition to named parameters as described in the previous section, parameters can also be embedded within the URL rule itself. This approach enables Yii2 to match a single rule to multiple routes, which can greatly reduces the number of URL rules and consequently the performance of your router. Take for instance the following route:

[
    '<controller:(content|comment)>/<id:\d+>/<action:
    (create|list|delete)>' => '<controller>/<action>',
]

This route will match both the content and comment controller with a given ID for the create, list, and delete action, and pass it to the appropriate action. In order for a route to match however, all named parameters must be defined. If a given route does not contain the given parameters, Yii2 will fail to match the route, which will most likely result in the route hitting a 404 error. One way to get around this limitation is to provide default parameters for the routes as shown in the following example:

[
    // ...other rules...
    [
        'pattern' => 'content/<page:\d+>/<name>',
        'route' => 'content/index',
        'defaults' => ['page' => 1, 'name' => NULL],
    ],
]

In this example, page will default to 1, and name will default to NULL. This URL rule can match multiple routes. In this specific instance, several routes will be matched:

  • /content, page=1, name=NULL
  • /content/215, page=215, name=NULL
  • /content/215/foo, page=215, name=foo
  • /content/foo, page=1, name=foo

URL suffixes

As an alternative to declaring a key value pair for a URL route, routes can also be defined as an array of key value pairs containing the pattern, route, and even a custom URL suffix to specifically respond to.

[
    [
        'pattern' => 'posts',
        'route' => 'content/index',
        'suffix' => '.xml',
    ],
]

These routes can be used to configure your application to respond in different formats to certain types of requests.

By default, rules created this way will be created as an instance of yii\web\UrlRule, but can be changed by defining the class parameter.

HTTP method specific URL rules

At times, you may find it beneficial route different types of HTTP methods to the same route, but handle them in different actions. In Yii2, this can be achieved by prefixing the method types before the route key as shown in the following example.

[
    'PUT,POST users/<id:\d+>' => 'users/create',
    'DELETE users/<id:\d+>' => 'users/delete',
    'GET users/<id:\d+>' => 'users/view',
]

From API perspective, all requests would ultimately route to users/<id>, but depending upon the HTTP method, a different action would be executed.

URL rules with specified HTTP methods will only be used for routing purposes, and won't be used to create URLs such as when using yii\helper\Url.

Custom URL rule classes

While yii\web\Url is extremely flexible, and should cover the majority of uses cases you need for a URL rule, there are often times where a custom URL would be required. For instance, a publisher may want to support a format for representing authors and books, such as /Author/Book, where both Author and Book are data retrieved from the database. Custom URL rules in Yii2 can be created to solve this problem by extending yii\base\Object, and implementing yii\web\UrlRuleInterface as shown in the following example.

<?php

namespace app\components;

use yii\web\UrlRuleInterface;
use yii\base\Object;

class BookUrlRule extends Object implements UrlRuleInterface
{

    public function createUrl($manager, $route, $params)
    {
        if ($route === 'book/index')
        {
            if (isset($params['author'], $params['book']))
               return $params['author'] . '/' . $params['book'];
            else if (isset($params['author']))
                return $params['author']
        }
        return false;
    }

    public function parseRequest($manager, $request)
    {
        $pathInfo = $request->getPathInfo();
        if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))
        {
            // If the parameterized identified in $matches[] 
               matches a database value
            // Set $params['author'] and $params['book'] to those 
               attributes, then pass
            // those arguements to your route
            // return ['author/index', $params]
        }
        
        return false;
    }
}

Our custome rule can then be implemented within our yii/web/UrlManager :: $rules section by declaring our desire to use that class:

[
    // [...],
    [
        'class' => 'app\components\BookUrlRule'
    ],
    /
/ [...],
]

Dynamic rule generation

Rules can be programmatically and dynamically added to your application in several different ways. Dynamic rule generation can take the form of a custom URL rule class as outlined in the previous section or a custom URL manager. The simplest way to add new URL rules dynamically however is to use the addRules() method of the URL Manager. For rules to take effect, they need to occur early in the bootstrapping process of the application. For modules to dynamically add new rules they should implement yii\base\BootstrapInterface and add the custom URL rules in the bootstrap() method as shown in the following example:

public function bootstrap($app)
{
    $app->getUrlManager()->addRules([
        // Add new rules here
    ], false);
}

Modules should then be listed in yii\web\Application::bootstrap() so that they can take part of the bootstrapping process.

In complex web applications, it's important to monitor how many URL rules you have. Adding many different rules can seriously damage the performance of your application as Yii2 needs to iterate over each rule until it finds the first matching rule. Parameterized routes and reducing the number of URL rules can significantly improve performance of your application.

Summary

In this article, we covered the basics of how routing is implemented in Yii2. We explored how Yii2 handles routing of URL routes, and learned to manipulate and create our own custom URL rules.

<!-- [if gte mso 9]>



<![endif]-->

Our custom rule can then be implemented within our yii\web\UrlManager::$rules section by declaring our desire to use that

<!-- [if gte mso 9]>

Normal
0




false
false
false

EN-US
X-NONE
X-NONE
























<![endif]--><!-- [if gte mso 9]>
DefSemiHidden="true" DefQFormat="false" DefPriority="99"
LatentStyleCount="267">
UnhideWhenUsed="false" QFormat="true" Name="Normal"/>
UnhideWhenUsed="false" QFormat="true" Name="heading 1"/>


















UnhideWhenUsed="false" QFormat="true" Name="Title"/>

UnhideWhenUsed="false" QFormat="true" Name="Subtitle"/>
UnhideWhenUsed="false" QFormat="true" Name="Strong"/>
UnhideWhenUsed="false" QFormat="true" Name="Emphasis"/>
UnhideWhenUsed="false" Name="Table Grid"/>

UnhideWhenUsed="false" QFormat="true" Name="No Spacing"/>
UnhideWhenUsed="false" Name="Light Shading"/>
UnhideWhenUsed="false" Name="Light List"/>
UnhideWhenUsed="false" Name="Light Grid"/>
UnhideWhenUsed="false" Name="Medium Shading 1"/>
UnhideWhenUsed="false" Name="Medium Shading 2"/>
UnhideWhenUsed="false" Name="Medium List 1"/>
UnhideWhenUsed="false" Name="Medium List 2"/>
UnhideWhenUsed="false" Name="Medium Grid 1"/>
UnhideWhenUsed="false" Name="Medium Grid 2"/>
UnhideWhenUsed="false" Name="Medium Grid 3"/>
UnhideWhenUsed="false" Name="Dark List"/>
UnhideWhenUsed="false" Name="Colorful Shading"/>
UnhideWhenUsed="false" Name="Colorful List"/>
UnhideWhenUsed="false" Name="Colorful Grid"/>
UnhideWhenUsed="false" Name="Light Shading Accent 1"/>
UnhideWhenUsed="false" Name="Light List Accent 1"/>
UnhideWhenUsed="false" Name="Light Grid Accent 1"/>
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 1"/>
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 1"/>
UnhideWhenUsed="false" Name="Medium List 1 Accent 1"/>

UnhideWhenUsed="false" QFormat="true" Name="List Paragraph"/>
UnhideWhenUsed="false" QFormat="true" Name="Quote"/>
UnhideWhenUsed="false" QFormat="true" Name="Intense Quote"/>
UnhideWhenUsed="false" Name="Medium List 2 Accent 1"/>
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 1"/>
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 1"/>
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 1"/>
UnhideWhenUsed="false" Name="Dark List Accent 1"/>
UnhideWhenUsed="false" Name="Colorful Shading Accent 1"/>
UnhideWhenUsed="false" Name="Colorful List Accent 1"/>
UnhideWhenUsed="false" Name="Colorful Grid Accent 1"/>
UnhideWhenUsed="false" Name="Light Shading Accent 2"/>
UnhideWhenUsed="false" Name="Light List Accent 2"/>
UnhideWhenUsed="false" Name="Light Grid Accent 2"/>
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 2"/>
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 2"/>
UnhideWhenUsed="false" Name="Medium List 1 Accent 2"/>
UnhideWhenUsed="false" Name="Medium List 2 Accent 2"/>
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 2"/>
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 2"/>
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 2"/>
UnhideWhenUsed="false" Name="Dark List Accent 2"/>
UnhideWhenUsed="false" Name="Colorful Shading Accent 2"/>
UnhideWhenUsed="false" Name="Colorful List Accent 2"/>
UnhideWhenUsed="false" Name="Colorful Grid Accent 2"/>
UnhideWhenUsed="false" Name="Light Shading Accent 3"/>
UnhideWhenUsed="false" Name="Light List Accent 3"/>
UnhideWhenUsed="false" Name="Light Grid Accent 3"/>
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 3"/>
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 3"/>
UnhideWhenUsed="false" Name="Medium List 1 Accent 3"/>
UnhideWhenUsed="false" Name="Medium List 2 Accent 3"/>
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 3"/>
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 3"/>
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 3"/>
UnhideWhenUsed="false" Name="Dark List Accent 3"/>
UnhideWhenUsed="false" Name="Colorful Shading Accent 3"/>
UnhideWhenUsed="false" Name="Colorful List Accent 3"/>
UnhideWhenUsed="false" Name="Colorful Grid Accent 3"/>
UnhideWhenUsed="false" Name="Light Shading Accent 4"/>
UnhideWhenUsed="false" Name="Light List Accent 4"/>
UnhideWhenUsed="false" Name="Light Grid Accent 4"/>
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 4"/>
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 4"/>
UnhideWhenUsed="false" Name="Medium List 1 Accent 4"/>
UnhideWhenUsed="false" Name="Medium List 2 Accent 4"/>
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 4"/>
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 4"/>
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 4"/>
UnhideWhenUsed="false" Name="Dark List Accent 4"/>
UnhideWhenUsed="false" Name="Colorful Shading Accent 4"/>
UnhideWhenUsed="false" Name="Colorful List Accent 4"/>
UnhideWhenUsed="false" Name="Colorful Grid Accent 4"/>
UnhideWhenUsed="false" Name="Light Shading Accent 5"/>
UnhideWhenUsed="false" Name="Light List Accent 5"/>
UnhideWhenUsed="false" Name="Light Grid Accent 5"/>
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 5"/>
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 5"/>
UnhideWhenUsed="false" Name="Medium List 1 Accent 5"/>
UnhideWhenUsed="false" Name="Medium List 2 Accent 5"/>
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 5"/>
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 5"/>
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 5"/>
UnhideWhenUsed="false" Name="Dark List Accent 5"/>
UnhideWhenUsed="false" Name="Colorful Shading Accent 5"/>
UnhideWhenUsed="false" Name="Colorful List Accent 5"/>
UnhideWhenUsed="false" Name="Colorful Grid Accent 5"/>
UnhideWhenUsed="false" Name="Light Shading Accent 6"/>
UnhideWhenUsed="false" Name="Light List Accent 6"/>
UnhideWhenUsed="false" Name="Light Grid Accent 6"/>
UnhideWhenUsed="false" Name="Medium Shading 1 Accent 6"/>
UnhideWhenUsed="false" Name="Medium Shading 2 Accent 6"/>
UnhideWhenUsed="false" Name="Medium List 1 Accent 6"/>
UnhideWhenUsed="false" Name="Medium List 2 Accent 6"/>
UnhideWhenUsed="false" Name="Medium Grid 1 Accent 6"/>
UnhideWhenUsed="false" Name="Medium Grid 2 Accent 6"/>
UnhideWhenUsed="false" Name="Medium Grid 3 Accent 6"/>
UnhideWhenUsed="false" Name="Dark List Accent 6"/>
UnhideWhenUsed="false" Name="Colorful Shading Accent 6"/>
UnhideWhenUsed="false" Name="Colorful List Accent 6"/>
UnhideWhenUsed="false" Name="Colorful Grid Accent 6"/>
UnhideWhenUsed="false" QFormat="true" Name="Subtle Emphasis"/>
UnhideWhenUsed="false" QFormat="true" Name="Intense Emphasis"/>
UnhideWhenUsed="false" QFormat="true" Name="Subtle Reference"/>
UnhideWhenUsed="false" QFormat="true" Name="Intense Reference"/>
UnhideWhenUsed="false" QFormat="true" Name="Book Title"/>



<![endif]--><!-- [if gte mso 10]>

<![endif]-->

You've been reading an excerpt of:

Building a Game with Unity and Blender

Explore Title