DooPHP IRC channel


Routing : Defined Routes

Reference Guides for using specific components of DooPHP

Routing : Defined Routes

Postby RichardM » Sun Aug 15, 2010 1:01 pm

This thread covers all aspects of using DooPHPs support for defined routes. Please see Routing: Auto Routing for more information on auto routing support in DooPHP.
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Setup

Postby RichardM » Sun Aug 15, 2010 1:28 pm

By default DooPHP will use defined routing over Auto Routing. However, if you have been using auto routes and want to swap back to defined routes follow the below steps:

  1. First you need to modify your /protected/config/common.conf.php file
    Code: Select all
    // Ensure this line is commented out
    //$config['AUTOROUTE'] = true;
    // OR set to false
    $config['AUTOROUTE'] = false;
  2. You will now need a file to manage your routes in. Therefore create the following file if it does not exist:
    /protected/config/routes.conf.php
  3. Lastly you need to ensure you tell DooPHP about the routes you have defined therefore open up your index.php file and ensure the following lines are set:
    Code: Select all
    // This should appear directly after the include of common.conf.php
    include './protected/config/routes.conf.php';

    // This should appear just before your Doo::app()->run() call
    Doo::app()->route = $route;


    The index.php file should at the very least take the form of:
    Code: Select all
    <?php
    include './protected/config/common.conf.php';
    include './protected/config/routes.conf.php';

    include $config['BASE_PATH'].'Doo.php';
    include $config['BASE_PATH'].'app/DooConfig.php';

    Doo::conf()->set($config);

    // remove this if you wish to see the normal PHP error view
    include $config['BASE_PATH'].'diagnostic/debug.php';

    Doo::app()->route = $route;
    Doo::app()->run();

    Note: You will likely have more defined here depending on other modules you are using

The routes used by your project will then be managed in the new file routes.conf.php file. See the following posts for more information on defining actual routes.
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Introduction to Routing (Simple / Static routing)

Postby RichardM » Sun Aug 15, 2010 1:48 pm

Defined routes in DooPHP allow you to map a specified url (ie. /contact-us) onto a specified controller::action pair.

The simplest form of this is taking a static (defined) url and mapping this onto a folder. This would be achieved by entering the following into your routes.conf.php file:
Code: Select all
$route['*']['/contact-us'] = array('MainController', 'contactUs');


We will ignore the 1st part of the array (['*']) for now. For more on this see the following post. We will focus on the second array (['/contact-us']) for now.

When a user visits http://example.com/contact-us DooPHP will search all the routes you define in your $route array and look for the first matching route. In the example above we have a matching route and so DooPHP will create an instance of the 'MainController' (defined in /protected/controller/MainController.php) and call the function contactUs()*.

If however we have defined:
Code: Select all
$route['*']['/contact-us'] = array('ContactController', 'contactUs');
$route['*']['/contact-us'] = array('MainController', 'contactUs');

Then DooPHP will instantiate the ContactController and not the MainController as it has already found the positive match. This holds true for ALL the routes covered latter. DooPHP will always stop on finding a match so be aware of the order you define your routes.

You must also ensure that routes start with a forward slash (/) and NOT end with a (/) in the route definition. However, you the addition of a / at the end of the requesting URL is optional.
Code: Select all
$route['*']['/contact-us'] = array('MainController', 'contactUs'); // Correct
// Above will match both http://example.com/contact-us AND
// http://example.com/contact-us/

$route['*']['/contact-us/'] = array('MainController', 'contactUs'); // Incorrect (trailing / in the route definition)


You are not limited to just one level either so you could have a static route of the form:
Code: Select all
$route['*']['/help-and-support/privacy-policy'] = array('HelpAndSupportController', 'privacyPolicy');

OR more importantly you can define what to display when people visit your homepage (example.com/)
Code: Select all
$route['*']['/'] = array('MainController', 'home');


The parts of the url should only contain the following: a-zA-Z0-9-_
Others may work but I have not tested these. However, you can not use a colon (:) at the start of each folder within a route as this is used for a special purpose in DooPHP (see later posts in this topic)

Its a good idea to setup an error handler within your application. One option for this is using an 'error route' which can be achieved through the following steps:
  1. In the /protected/config/common.conf.php file you should ensure you have the following defined:
    Code: Select all
    $config['ERROR_404_ROUTE'] = '/error';

  2. Then in your routes.conf.php file ensure you have a route defined:
    Code: Select all
    $route['*']['/error'] = array('ErrorController', 'showError');

  3. And lastly define a ErrorController in protected/controller/ErrorController.php
    Code: Select all
    <?php
    class ErrorController extends DooController {
       public function showError() {
          echo "An Unknown Error Has Occurred";
       }
    }

*This is a simplified example...DooPHP does do a little bit more here. But its not important at this stage
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Request Type Routing

Postby RichardM » Sun Aug 15, 2010 3:07 pm

DooPHP provides support to route the user to different controller::action pairs depending on the request type sent to the same url. For example you might want to have DooPHP handle a request to example.com/rss/ in 2 different ways. The first might handle GET requests and just provide the user with the RSS feed very quickly while you may want to accept a POST request which contains filtering instructions and handle this by a slightly slower but more powerful handler.

This is also useful when you are implementing pages containing forms. You may want to have the post action send to the same URL as the GET request to render the form but for it to be handled by another function.

This is achieved by modifying the first part of the route definition which you saw earlier (['*']). DooPHP supports the following options:
  • get - GET requests
  • post - POST requests
  • put - PUT requests
  • delete - DELETE requests
  • * - a catch all which will catch any request type if no matching route of the requested request type is found

As an example we might want a controller of the following form:
Code: Select all
class MainController extends DooController {
    public function test() {
        echo "Test Function - Global Handler";
    }

    public function test_get() {
        echo "Test Function - GET Handler";
    }

    public function test_post() {
        echo "Test Function - POST Handler";
    }
}


If we now define our routes as follows:
Code: Select all
$route['*']['/test'] = array('MainController', 'test');
$route['get']['/test'] = array('MainController', 'test_get');
$route['post']['/test'] = array('MainController', 'test_post');


If we now visit http://example.com/test/ we should see:
Code: Select all
Test Function - GET Handler

We do not see the Global Handler because DooPHP will always try and find a matching route of the request type before falling through to the global handler.

If we visit the page through a post request then we will see the following:
Code: Select all
Test Function - POST Handler


And if you request the page via either a DELETE or a PUT request you will see the following as no request type specific route is defined for either:
Code: Select all
Test Function - Global Handler


Note that all the type handlers must be specified in lower case when being defined.

Request Type Routing support is supported by all types of defined routes. No support for this selectiveness currently exists for auto routing.
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Parameters in Routes

Postby RichardM » Sun Aug 15, 2010 3:40 pm

So hopefully you now understand the basics of defined routing in DooPHP. How about if you want to have some more dynamic routes? Something like:
  • example.com/news/article/12/ - Show the 12th news article
  • example.com/news/category/doophp/ - Get all news articles in the category 'doophp'

We can make use of parameters in our URLs as follows:
Code: Select all
$route['*']['/news/article/:id'] = array('NewsController', 'viewArticle');
$route['*']['/news/category/:category'] = array('NewsController', 'viewCategory');


In the example above we define out parameters by starting the variable part of the url with a colon (:) so in the first example our parameter is id and in the second its category. We can then make use of the parameters within our controller as follows:
Code: Select all
<?php

class NewsController extends DooController {

   public function viewArticle() {
      // Fetch the article ID from the params array
      $articleId = $this->params['id'];

      // Code would go here to validate the ID and
      // fetch the article from db etc...

      echo "Viewing Article: {$articleId}";
   }

   public function viewCategory() {
      // Fetch the category name from the params array
      $categoryName = $this->params['category'];

      // Code would go here to validate the category name and
      // fetch the relevant articles from the db

      echo "Viewing Category: {$categoryName}";
   }
}


If we try visiting the 2 example URLs from before we will see the following results
Code: Select all
Viewing Article: 12 // When we call example.com/news/article/12/

Viewing Category: doophp  // When we call example.com/news/category/doophp/


DooPHP will accept any string in this parameter up to the next / in the url. Therefore the following url would not be accepted:
  • example.com/news/article/12/some-article-name
Note: you may see a warning when trying to access this page if you do not have a error route configured with a corresponding controller::action

We can allow this url to work by changing our routing definition to:
Code: Select all
$route['*']['/news/article/:id/:name'] = array('NewsController', 'viewArticle');

The article name could now be fetched from the params array to:
Code: Select all
$this->params['name']

However, by doing this we can no longer visit the original form without the article name as we now expect 2 parameters to be provided. For now we could simply define both routes like so:
Code: Select all
$route['*']['/news/article/:id'] = array('NewsController', 'viewArticle');
$route['*']['/news/article/:id/:name'] = array('NewsController', 'viewArticle');


Another option does exist to support this but we will cover this later on.

Some things to be aware of:
  1. You can not have 2 routes with the same variable parameter in the same place and reach both:
    Code: Select all
    $route['*']['/news/:id'] = array('NewsController', 'viewArticle');
    $route['*']['/news/:category'] = array('NewsController', 'viewCategory');

    You would just make it to the viewArticle every time!
  2. You can not put a parameter on the root of the url like so:
    Code: Select all
    $route['*']['/:page'] = array('MainController', 'viewPage');

  3. You can put a variable parameter within static parameters for example:
    Code: Select all
    $route['*']['/news/article/:id/full-page'] = array('NewsController', 'viewArticleFullPage');
    $route['*']['/news/article/:id/narrow'] = array('NewsController', 'viewArticleNarrow');

    However, You must ensure that you do not have a variable parameter root before these routes or it will catch them instead. Ie. put something like:
    Code: Select all
    // Uncommenting the below line will cause it to catch both of the other urls
    // $route['*']['/news/article/:id/:name'] = array('NewsController', 'viewArticle');
    $route['*']['/news/article/:id/full-page'] = array('NewsController', 'viewArticleFullPage');
    $route['*']['/news/article/:id/narrow'] = array('NewsController', 'viewArticleNarrow');

    // You could however place it here like so to catch the other options
    // $route['*']['/news/article/:id/:name'] = array('NewsController', 'viewArticle');

    // OR you could also support 2 params inside static sections:
    $route['*']['/news/article/:id/:name/full-page'] = array('NewsController', 'viewArticleFullPage');
    $route['*']['/news/article/:id/:name/narrow'] = array('NewsController', 'viewArticleNarrow');

As you can see its important to track the order in which you define your routes. We will also cover how to handle the first 2 problems in the following sections.
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Routing : Catch All

Postby RichardM » Sun Aug 15, 2010 10:12 pm

If you want to allow an unlimited number of folders in a dynamic url you can make use of the catchall routes. Catch all routes allow you to define a parameter at the end of the specified url which will receive requests containing any number of trailing sub folders in the path.

Taking an example from above you might want to catch both:
  • example.com/news/article/12 AND
  • example.com/news/article/12/some-article-name

This can be captured with the following route:
Code: Select all
$route['*']['catchall']['/news/article/:all'] = array('NewsController', 'catchAllViewArticle');


Calling the above 2 example urls would result in the params array accessible in the controller containing the value 12 for the array key of all and then if the article name was passed in the url to it would exist in the params array at index 0. Below are some example outputs of the $this->params array from a print_r statement.
Code: Select all
// example.com/news/article/12
Array
(
    [all] => 12
)

// example.com/news/article/12/some-article-name
Array
(
    [all] => 12
    [0] => some-article-name
)

// example.com/news/article/12/some/article/name
Array
(
    [all] => 12
    [0] => some
    [1] => article
    [2] => name
)


At this time the catchall routes can only be defined on the request type of * (ie. ['*'] and not for example ['get']['catchall']) If you need this support though let me know (PM) and I will look at the possibility of adding such support.

Unlike the other rules you can define a catch all url on the root level of the url for example:
Code: Select all
$route['*']['catchall']['/:all'] = array('MainController', 'catchEverything');

Note: This would catch everything except for a request to the the root of the application.

Catch all routes will not be tried until all normal routes have been tried and no matches are found.
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

Routing: Dynamic root level routes

Postby RichardM » Sun Aug 15, 2010 10:21 pm

So far you have seen how you can use static routes and dynamic routes and the limitation of not being able to use a dynamic variable on the root level of the array for example:
Code: Select all
// This will not work!
// example.com/some-page
$route['*']['/:page'] = array('MainController', 'viewPage');


This is primarily for performance reasons. You have also seen how you could use a catchall route to get any url from the route however this is harder to go matching and does not allow you to use static parts to the array following the last dynamic variable. However, you can use root urls to catch a dynamic url which starts on the root of the url for example you might want to catch:
  • example.com/some-page/wide-screen
  • example.com/another-page/wide-screen
where by the first part of the url is variable (some-page AND another-page). You can capture these using the following route:
Code: Select all
$route['*']['root']['/:page/wide-screen'] = array('MainController', 'viewPageInWideScreen');


At this time the root routes can only be defined on the request type of * (ie. ['*'] and not for example ['get']['root']) If you need this support though let me know (PM) and I will look at the possibility of adding such support.

These will be tested after normal routes have been tried but before catchall routes.
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK

File Extensions in Routes

Postby RichardM » Sun Aug 15, 2010 10:42 pm

So far we have looked at routes which end with a string or a / like you would do with folder names in normal URLs. We will now look at matching file extensions in urls. In this example we will look the match the following urls
  • example.com/news/article/all.html
  • example.com/news/article/all.xml
  • example.com/news/article/view/42.html
  • example.com/news/article/view/42.xml

The first 2 urls are simple to detect and handle and we can simply hard code the url into the routes as so:
Code: Select all
$route['*']['/news/article/all.html'] = array('NewsController', 'viewAllArticlesAsHtml');
$route['*']['/news/article/all.xml'] = array('NewsController', 'viewAllArticlesAsXml');


However, we can not do this on the next 2 as we want to accept a dynamic news article id. Therefore we will make use of the optional 'extension' which we can define for the routes location to test the extension supplied in the requesting url. The 2 routes would be written as:
Code: Select all
$route['*']['/news/article/view/:id'] = array('NewsController', 'viewArticleAsHtml', 'extension' => '.html');
$route['*']['/news/article/view/:id'] = array('NewsController', 'viewArticleAsXml', 'extension' => '.xml');


Notice that the extension is passed as a string and the extension value starts with the leading full stop (.)

However, If you want to accept more than one type of extension for a given route for example we might want to render .html, .htm and .php using the same route we can pass the extensions parameter as an array.
Code: Select all
$route['*']['/news/article/view/:id'] = array('NewsController', 'viewArticleAsHtml', 'extension' => array('.html', '.htm', '.php'));


Lastly if you want to know what extension was matched in the url you can get this information from the controller. For example:
Code: Select all
<?php
class NewsController extends DooController {
   public function viewArticleAsHtml() {
      echo "View Article as HTML";
      echo "Requested Extension: " . $this->extension; // Get the matching extension
   }
}

These can be used on ALL types of routes
Note: code samples my not be 100% accurate.
RichardM
 
Posts: 1329
Joined: Sun Aug 30, 2009 6:03 pm
Location: Cumbria, UK


Return to References

Who is online

Users browsing this forum: No registered users and 1 guest

cron