Easy PHP Routing Management

in #php5 years ago

When you are learning PHP programming, your URL's should look something like this:

http://yourdomain.com/showcustomerdetail.php?id=73

and it is completely ok to keep it like that, but as you advance in your projects and you start wanting to have more control over what you are building, you might want to clean up your URL to something cleaner and readable to something like this:

http://yourdomain.com/customer/detail/73

What is Routing

Routing is basically a technique that adds separation between files and urls. And because of that, you'll have to implement some code to manage that separation. And since URL's options can (and probably will) grow a lot, it is a good practice to have some logic that handles a specific URL pattern you decide, and then build all the pages on top of that.

Flight PHP

Flight PHP is a very lightweight microframework that can do a lot of cool stuff, including URL routing. It can be installed via Composer, or manually downloaded and extracted to your project directory.

The way it handles routing is by redirecting all URL requests to your index.php file, and from there you can say what to do with each URL:

Flight::route('/', function(){
    echo 'This is my Home';
});

//
Flight::route('/customer/detail/@id', function($id){
    echo 'Load details for customer with id: ' + $id;
});

You can look at the Documentation to see all the options they have to define patterns to your urls.

The 3 Levels

For this tutorial, lets build a single routing logic that can handle up to 3 elements in a url: controller, method and parameter:

  • Root Level: /
  • Controller Level: /customer
  • Method Level: /customer/new
  • Parameter Level: /customer/detail/73

To do this, we can specify this specific pattern:

Flight::route('/(@controller(/@method(/@id)))', function ($controller, $method, $id) {

    /*
     * check if url has a controller level information and creates a controller name based on you namespace convention
     * if dont have controller level (root), creates a controller name as HomeController
     */
    $controller = $controller ? $controller : 'Home';
    $conName = '\YourNamespace\Controller\\' . ucfirst(strtolower($controller)) . 'Controller';

     /*
     * check controller name created previously existis.
     */
    if (!class_exists($conName)) {
        not_found();
    }

    /*
     * Instantiates an object of that controller
     */
    $ct = new $conName();

    if (!$method) {
        /*
         * If URL didn't have method level, call method main, in controller object
         */
        $ct->main();
    } else {
        /*
         * If have method level, check if method exists in the controller object, and case true, call method in controller object
         * If have parameter level information, pass parameter into method specified. (if don't, pass NULL)
         */
        if (!method_exists($ct, $method)) {
            not_found();
        }
        $ct->$method($id);
    }
});

Handling 404

You might have noticed that there is a function called not_found. Create this function to handle all the cases where a controller and/or a method don't exist. There you can do whatever you might want:

  • Show a 404 error page.
  • Redirect to another URL
  • Log a message to the server
  • Throw an exception
function not_found()
{
    Flight:redirect('/not-found.php');
}

Using API and Webpage together

If you are building a project where you have API and webpages handled by the same domain, you can use this same logic to handle both of them just by having another similar routing function to API calls

/*
 * Handles all calls that starts with /api
 * search for controllers inside ApiController namespace
 */
Flight::route('/api/(@controller(/@method(/@id)))', function ($controller, $method, $id) {

    $controller = $controller ? $controller : 'Home';
    $conName = '\YourNamespace\ApiController\\' . ucfirst(strtolower($controller)) . 'Controller';
    
    ...
        
});

/*
 * Handles all webpage calls
 * search for controllers inside Controller namespace
 */
Flight::route('/(@controller(/@method(/@id)))', function ($controller, $method, $id) {

    $controller = $controller ? $controller : 'Home';
    $conName = '\YourNamespace\Controller\\' . ucfirst(strtolower($controller)) . 'Controller';
    
    ...
        
});

Since Flight resolves the first pattern that matches the requested URL, it is wise to put more specific ones first, and then the generic ones after that.

Hope this helps you guys out, Bye!