Cygnite - A Modern Toolkit For Web Developers

The Elegant Way of Building Full-Featured Web Applications

Cygnite Dynamic Routing

Routing


Documentation

URL Routing

Introduction

A Cygnite routing will invoke the route that matches the current HTTP request’s URI and method. If router doesn't find any routes matching with HTTP request URI and method then Cygnite router automatically throw "404 Page Not Found" error. Cygnite has powerful routing features which allows various routing patterns.

The main features of router are -

  • i. Resourceful Routing.
  • ii. Static Route Patterns.
  • iii. Dynamic Route Patterns.
  • iv. Optional Route Subpatterns.
  • v. GET, POST, PUT, DELETE, and OPTIONS request methods.
  • vi. Group Routing.
  • vii. Routing to CRUD controller.
  • viii. Routes Middleware.

Where Should I Define Routes?

Define most of the routes for your application in the src/Apps/Routing/Routes.php file. Router supports direct routing, closure callback, group routing, crud controller routing and RESTful resource routing etc. You may wish to organize all routes for your application, instead defining all routes in a single file, You can use src/Apps/Routing/RouteCollection.php file for defining extra routing to your application.

Apart from this the router also helps you to respond to CRUD controller, simply by defining a single route entry in the RouteCollection.php file. By default, single entry of CRUD routing respond to indexAction, addAction, editAction, showAction, deleteAction actions.

Route Patterns

By default Cygnite Router supports following routing patterns:

Default Routing Patterns

Using regular expressions in the routes looks ugly, therefore us below placeholders for different patterns.


    
   {:num}         = ([0-9]+),
   {:id}          = (\d+),
   {:name}        = (\w+),
   {:any}         = ([a-zA-Z0-9\.\-_%]+),
   {:all}         = (.*),
   {:namespace}   = ([a-zA-Z0-9_-]+),
   {:module}      = ([a-zA-Z0-9_-]+),   
   {:year}        = \d{4},
   {:month}       = \d{2},
   {:day}         = \d{2}(/[a-z0-9_-]+)


You are also allowed to use regular expression for routing. Some patterns described here.


  \d+         = One or more digits (0-9)
  \w+         = One or more word characters (a-z 0-9 _)
  [a-z0-9_-]+ = One or more word characters (a-z 0-9 _) and the dash (-)
  .*          = Any character (including /), zero or more
  [^/]+       = Any character but /, one or more


Static Routes To Controller

Consider you want to call indexAction of \Apps\Controllers\UserController from routes. You can add a single route entry as below.


 $app->router->get('user/list', 'User@index');

You may use closure callback to call controller's action as an example below.


 $app->router->get('/greet/', function($router) 
 {
    return $router->callController('Home@welcome', []);         
 });

Dynamic Routing

GET Routes


// Dynamic route: /hello/sanjoy/32
$app->router->get('/hello/{:name}/{:id}', function($router, $name, $id) 
{   
   echo 'Hello  ' . htmlentities(ucfirst($name)). ". Your id is $id ";
});

POST Routes


$app->router->post('/product/', function () 
{
   //Create Product
});

PUT Routes


  $app->router->put('/product/{:id}', function ($router, $id) 
  {
     //Update product identified by $id
  });

DELETE Routes


  $app->router->delete('/product/{:id}', function ($router, $id) 
  {
     //Delete Product identified by $id
  });

OPTIONS Routes


$app->router->options('/product/{:id}', function ($router, $id) 
{
   //Return response headers
});

Route to Respond Multiple HTTP Verbs

Sometimes you may need to register a route which respond to multiple HTTP verbs.


  // Will respond to GET|POST
  $app->router->match('GET|POST', '/product/', function ($router) 
  {
     //Respond to any HTTP verb as GET|POST
  });

Route to Respond Any HTTP Verbs

You may also register a route which respond to any HTTP verbs as below.


 // Will respond to any GET|POST|PUT|PATCH|DELETE
 $app->router->any('/foo', function ($router) 
 {
    //Hello!! Cygnite
 });

Note: The leading / at the very beginning of a route pattern is not mandatory, but is recommended.

Route Parameters

When multiple subpatterns are defined, the route handle parameters, are passed into the route handling function in the order they are defined. You can define as many parameter you want.


$app->router->get('/users/{:id}/photos/{:id}', function($router, $userId, $photoId) 
{
    echo 'User #' . $userId . ', photo #' . $photoId);
});

Optional Route Parameters

Route subpatterns can be made optional by making the subpatterns optional by adding a ? after them. For example think of blog URLs in the form of /blog(/year)(/month)(/day)(/slug):


 $app->router->get('/blog(/{:year}(/{:month}(/{:day}?)?)?)?',
     function($router, $year = null, $month = null, $day = null, $slug = null) 
     {
           if (!$year) { echo 'Blog overview'; return; }
           if (!$month) { echo 'Blog year overview'; return; }
           if (!$day) { echo 'Blog month overview'; return; }
           if (!$slug) { echo 'Blog day overview'; return; }

           echo 'Blog Post ' . htmlentities($slug) . ' detail';
     }
 );

The code snippet above responds to the URLs /blog, /blog/year, /blog/year/month, /blog/year/month/day, and /blog/year/month/day/slug.

Note: With optional parameters it is important that the leading / of the subpatterns is put inside the subpattern itself. Don't forget to set default values for the optional parameters.

The code snipped above unfortunately also responds to URLs like /blog/foo and states that the overview needs to be shown - which is incorrect. The successive pattern should resemble /blog(/year(/month(/day(/slug)))) instead of the previous /blog(/year)(/month)(/day)(/slug):

Note: It is highly recommended to always define successive optional parameters.

To make things complete use quantifiers to require the correct amount of numbers in the URL:



 // Override default patterns using custom patterns

 $app->router->pattern('{:year}' , '(\d{4})');
 $app->router->pattern('{:month}', '(\d{2})');
 $app->router->pattern('{:day}',   '(\d{2})'); 
  
 $app->router->get('/archive/{:year}/{:month}/{:day}', 
    function($router, $year = null, $month = null, $day = null, $slug = null) 
    {
       // ...
           
    }
 );

Unlimited Optional Parameters

For unlimited optional parameters in routes you can define as below.


 $app->router->get('/blog/.*', function ($router) {

    $content = 'This route respond to unlimited optional parameters.';
    //\Cygnite\Common\UrlManager\Url::segment($offset); to get the uri parameter.

    return Response::make($content);
});

Using Where

You may define your own pattern for the route using where method on route instance. The pattern method is alias of where.

     
// URI: /user/contact/
$app->router->where('{:name}', '([A-Za-z]+)')->get('/user/{:name}/', function()
{
    //    
});

Applying Global Constraints

You can also override default patterns of routes or define your own pattern globally as below.


  $app->router->pattern('{:number}', '([0-9]+)'); // applied new pattern

Once you define the pattern, it will be applied for all routes using that pattern name.

Override existing pattern placeholder as below.

     
  $app->router->pattern('{:id}', '([0-9]+)'); // override default pattern  

Group Routing

Sometime you may want to route group of resource on the same uri, such case use group routes to response to request.


 $app->router->group('/movies', function($route) {

    $route->get('/', function() {
        echo "Movies Overview";
    });

    $route->get('/{:id}/{:name}', function($route, $id ,$name) {
        echo "Movie $name and $id";
    });

    $route->get('price/{:num}', function($route, $price) {
        echo "Movie price:  $price";
    });

 });

Overriding And Custom Routes Patterns

Router allows to override default named routes and also to add custom routes. You can achieve it using where clause.


 // /movies
 $app->router->group('/movies', function($route) 
 {
    // /movies/21
    $route->where('{:id}', '(\d+)')->get('where/{:id}', function()
    {
        echo "Movie with custom where clause ";
    });

    // /movies/titanic/
    $route->where('{:string}', 'name')->get('/where/{:string}', function()
    {
        echo "Movie custom pattern";
    });

 });

Calling controller from the closure callback


     
$app->router->get('/greet/{:name}', function($router, $name) 
{
   $content = $router->callController('Home@welcome', [$name]);  

   return Response::make($content);          
});


Calling HMVC Module Controller From Route Callback

     
$app->router->get('/category/', function($router) 
{
   //Acme::controller.action
   $content = $router->callController(["Acme::User@index", [$id]]);

   return Response::make($content);      
});

RESTful Resource Routing

Cygnite router make your job just easy. Router not only allows you to different routing but also resourceful routing on controller classes. Which leads you to build powerful RESTful controllers. Cygnite's console also allows you on creating resourceful controllers.

Once you have created resourceful controller you need to register resourceful routing to the controller. Here let us register "UserController" as resource controller.

  
  
  //$app->router->resource('route-name', 'controller'); //syntax

  $app->router->resource('users', 'user');


This single route declaration creates multiple routes to handle various RESTful actions of "user" resource.

For more details read RESTful Resource Controllers and console documentation for creating resourceful controller.

Route CRUD Controller

You may use generate:crud command to generate CRUD controller with ease. You just need to define a single route entry simply by specifying the controller name in the src/Apps/Routing/RouteCollection.php file to respond to 'indexAction', 'newAction', 'createAction', 'showAction', 'editAction', 'deleteAction' actions. Consider you have generated a controller called \Apps\Controllers\ProductController using CRUD generator command. After generating the controller you need to add a route entry inside the \Apps\Routing\RouteCollection::executeStaticRoutes() method as below.


    protected function executeStaticRoutes()
    {
        $this->routesController->controller('Product');

        return $this;
    }


This single route declaration creates crud routes to handle various actions on your controller. You can also route to additional actions just by adding setAction() method on the controller route.

Routes Middleware

The router also supports impressive middlewares, which are executed before and after the route handling is processed. Basically HTTP Middleware classes are act as route middleware also if you specify the class name into the routes. If you specify the middleware namespace in src/Apps/Kernel.php file then middleware act as HTTP middleware.

Before Routes Middleware

You may wish to perform some action before the route processed. You may assign middleware to your route, simply by adding a middleware key in the HTTP route method, and the Middleware class name as value. Let us take a look at example:


$app->router->get('/user/profile/{:name}', ["middleware" => "Apps\Middleware\RouteMiddleware",
    function ($router, $name) {
        return "Hello $name";
    }
]);


The Apps\Middleware\RouteMiddleware::handle() method will get executed before processing route request. Let us take a look at RouteMiddleware class below.


namespace Apps\Middleware;

use Closure;
use Cygnite\Http\Requests\Request;
use Cygnite\Http\Responses\Response;

/**
 * Class RouteMiddleware
 *
 * Route middleware will be executed before processing
 * the route request.
 *
 * @package Apps\Middleware
 */
class RouteMiddleware
{
    /**
     * Executes Before Route Request
     *
     * @param Request $request
     * @param callable $next
     * @return static
     */
    public function handle(Request $request, Closure $next)
    {
        // Logic here

        return $next($request);
    }
}


After Routes Middleware

In some case you may wish to perform some tasks after route processed. You can achieve it simply adding a shutdown() method into your route middleware class. Let us take a look at the example:


 /**
   * Executes After Route Request
   *
   * @param Request $request
   * @param Response $response
   */
   public function shutdown(Request $request, Response $response)
   {
       // Your logic goes here
   }

Throwing 404 Errors

Trigger a 404 error from a route if none of routes matches. You can use application abort method to throws a \Cygnite\Exception\Http\HttpException exception with specified status code. If you specify the status code as 404 it will simply throw \Cygnite\Exception\Http\HttpNotFoundException exception.


  $app->router->set404Page(function () use($app) {
      $app->abort(404, "Abort 404 Page Not Found!");
  });

  $app->router->set404Page(function () use($app) {
      $app->abort(500, "Internal Server Error");
  });

Running Routes

The run is route independent and should be defined bottom of the src/Apps/Routing/Routes.php file.



 $app->router->run();


Follow Us On Facebook Twitter Google+ Linkedin
Released Under The MIT Public License. Copyrights @2012-2017. Powered by- Sanjoy Dey Productions.