Routes and Controllers

A route is the logic that matches the URL requested by a visitor with the web page they want to visit. The Route class handles the routing process, delegating authority to a designated controller. In a standard Chyrp Lite installation, there are three controllers: Main, Admin, and Ajax. The main controller can determine all the actions a visitor to your blog might want to take, such as viewing pages and doing searches, whilst the admin controller determines the actions a logged-in administrator might want to take.

How Do Routes Work?

The ultimate aim of routing is to determine an action. The route action corresponds to a PHP function that will be called and expected to serve a page to the visitor; that function might reside in the controller (a "native" responder) or it might be a trigger function existing in an extension, matching the pattern route_<<action>>() or <<controller>>_<<action>>(). Firstly the Route class will decompose the requested URL and make an initial attempt to determine the action. Next the Route class will hand over to the controller. It's the job of the controller to parse the URL and build a list of actions that "look like" the URL should go to them. The controller then hands back to the Route class to try each action in turn. Here is the workflow Chyrp Lite uses when you request a URL:

  1. Receive URL request.
  2. Controller is prepared.
  3. Route is initiated with the prepared controller.
  4. Route makes an initial attempt at determining the action.
  5. Route gives the controller the opportunity to determine the action.
  6. Route loops through all potential actions until one returns !== false.

Once a potential action responds with a return value that is !== false the Route class will stop trying alternatives. The responding function can use the controller's display() method to render the page for the visitor. If Route tries all the possibilities and all return false without displaying anything, it will direct the visitor to the 404 (not found) error page as a last resort.

Custom Routes

A custom route tells MainController to interpret a given URL as a particular combination of action and parameters. Custom routes are defined in the file config.json.php in the includes folder of your Chyrp Lite installation.

When is a custom route necessary?

Custom routes are necessary only for actions that accept arguments via clean URLs, or actions that do not map directly to a responder function.

Adding Custom Routes Manually

To define a custom route manually, open the file config.json.php in a text editor and add your routes to the "routes" object in the file. For example, the following would tell Chyrp Lite to interpret [your domain]/blog as the blog's index action:

"routes": {
    "blog": "index"
},

Another example – interpret [your domain]/stuff as the tag action and tell the Tags module to show posts tagged with “foo”:

"routes": {
    "stuff": "tag;name=foo"
},

Multiple comma-separated routes can be added to the object:

"routes": {
    "blog": "index",
    "stuff": "tag;name=foo"
},

Adding Custom Routes Programatically

Modules can add a custom route by calling the add() method of the Route class. When a route is added programatically, it will be saved permanently to config.json.php therefore routes should be added one time only in your module's __install() function and removed in the __uninstall() function.

Route::current()->add("blog", "index");

Your custom route can also specify named $_GET parameters using parentheses, and if your route matches the URL requested by the visitor, those parameters will be populated by the controller and will be ready for use by your responder function. See the Tags module for an example:

Route::current()->add("tag/(name)/", "tag");

As part of the routing process, the Route class will search for trigger functions with names that match the current controller and action. The Tags module implements a main_tag() trigger function to enable it to respond when the main controller is initiated with the tag action.

If your module adds custom routes it should also implement the parse_urls($urls) function. This function provides the controller with a regular expression it can use to translate a prettified "clean" URL into a "dirty" argument set when the url() helper is called. See the Tags module for an example:

public function parse_urls($urls) {
    $urls["|/tag/([^/]+)/|"] = "/?action=tag&name=$1";
    return $urls;
}

Responding to Actions Without a Custom Route

Your module can also respond to an action using a trigger function even if it has not added a custom route, but it will be restricted to using a dirty URL. For example, the Likes module uses a main_like() trigger function to respond when a visitor clicks on a "Like!" link with the URL /?action=like. This method should be used only for actions that do not display content to avoid exposing dirty URLs to visitors.

Targeting Actions

When you browse your blog, route actions will appear in your web browser's address bar as part of the URL path. Your blog themes and modules can inspect the action and respond differently to different actions.

Actions in Twig

The following statement will evaluate to true only if the current page is the blog index:

{% if route.action == "index" %}

You can use Twig's url() function to insert the correct URL for a particular action into your blog pages:

{{ url('archive') }}

Actions in PHP

The following statement will evaluate to true only if the current page is the blog index:

if (Route::current()->action == "index")

You can also request the correct URL for an action by supplying the action to the url() helper function:

url("archive");

Overloading Native Responders

You can overload a native responder by creating a module that defines a correctly named trigger function. For example, you can overload MainController's native main_login() responder by adding a function named main_login() to your module. Your module's responder will be called before the one in MainController and you can decide whether or not to respond to the request. If your function returns false the routing process will continue and the native responder will handle the response.