Well,
Laravel version 5.0 has some major changes in it's folder structure and it's really better than before. The new structure separates the
Http Layer (The delivery mechanism of a web application) and inside the
app directory there is a new
Http folder which contains three folders,
Controllers,
Middleware,
Requests and the
routes.php file. As the title of this article tells that I'm going to discuss about
Routing so it's obvious that I'm not not discussing about anything but
routes.php file here which used for route declarations.
The
Laravel-5.0 is still in the development stage (at the time of writing of this post) and it may very at later releases, until the final release this post will be updated if required. So please stay tuned and check the final release of
Laravel and the documentation on
Laravel website as well.
Well, until
Laravel version-5.0 we used to declare a route basically using something like this:
Route::get('url', 'ClassNameController@method');
It's a very basic of routing and there are other ways tho, such as
$app['router']->get(..) and even more ways and the
routes.php file was in the
app folder and it was just a normal
PHP script but anyways. In
Laravel version 5.0 there are some changes in route declaration including some other things. First of all, the basic recommended way to declare a route in
Laravel version 5.0 is something like this:
$router->get('url', 'ClassnameController@method');
Also, we may use something like this as well:
Route::get('url', 'ClassnameController@method');
Also using a helper method and a
Closure, something like this:
get('url', function(){
//...
});
In this case, if we use
$router then it works and it means that, somehow the
$router variable is available in the scope of
routes.php file but it's not a
global variable in this case, instead, it's a
function scoped variable and that function is an anonymous function inside the
App\Providers\RouteServiceProvider class but still available in the
routes.php file and that is bacause the
routes.php file is a part of the
App\Providers\RouteServiceProvider class which is included within that class. Let's check the code given below, which is taken from the
App\Providers\RouteServiceProvider.php class:
public function map()
{
$this->app->booted(function()
{
$this->namespaced('App\Http\Controllers', function(Router $router)
{
require app_path().'/Http/routes.php'; // <-- routes.php file is included here
});
});
}
In the
map method, the innermost
Closure the
routes.php file is being included so the code in the
routes.php file is actually a part of
App\Providers\RouteServiceProvider class or we may think that, we are declaring the routes inside the class. Which means, if we use for example,
dd($this) then we'll get a dumped
App\Providers\RouteServiceProvider object. Since the
App\Providers\RouteServiceProvider class extends the
Illuminate\Foundation\Support\Providers\RouteServiceProvider class so it has access to it's parent class so that, it can call it's parent class'
namespaced method and this
namespaced method is as given below:
protected function namespaced($namespace, Closure $callback)
{
if (empty($namespace))
{
$callback($this->app['router']);
}
else
{
$this->app['router']->group(compact('namespace'), $callback);
}
}
So, it's now obvious that, the
namespaced method calls the
Closure (with the only argument
$this->app['router']) that is passed as the second argument of
namespaced method from the
map method of
App\Providers\RouteServiceProvider class and the
map method actually registers the
$app->booted event handler (The
Closure passed into the
booted(...) is the handler) so when the
booted event gets fired by the framework, the
namespaced method gets called and so the
app/Http/routes.php file gets included inside the
App\Providers\RouteServiceProvider class and the real magic actually happens inside the the
Illuminate\Foundation\Support\Providers class'
boot method; from where the process begins. The
boot method looks something like this:
public function boot()
{
// Calls the before method of App\Providers\RouteServiceProvider,
// route model binding should be coded here in the "before" method
$this->app->call([$this, 'before']);
// If routes are cached (new feature in version-5.0) then load cached routes
if ($this->app->routesAreCached()) return $this->loadCachedRoutes();
// Otherwise load routes.php
$this->loadRoutes();
}
The
loadRoutes method looks something like this:
protected function loadRoutes()
{
// Loads scanned (annotated) routes (new feature in version 5.0)
if ($this->app->routesAreScanned()) $this->loadScannedRoutes();
// Calls the "map" method from child class
// to register the booted event handler
$this->app->call([$this, 'map']);
}
So, that's pretty obvious that, when the frameworks gets booted then the
routes.php file gets included through the
RouteServiceProvider class and
routes.php file is actually the
App\Providers\RouteServiceProvider class itself, at least we can think so. As a result, the
$router variable is scoped to that
Closure and also the route declarations as well, check that anonymous function again here:
$this->namespaced('App\Http\Controllers', function(Router $router)
{
// $routes is available here and scope is limited to
// this Closure, separated from global scope
require app_path().'/Http/routes.php';
});
The "namespaced" method also loads the routes file within a route group which automatically sets the
App\Http\Controllers namespace as well. If the first argument is passed (which is
App\Http\Controllers) then the following line (in
namespaced method) does the trick:
$this->app['router']->group(compact('namespace'), $callback);
It sets the namespace so we don't need to use the namespace in route declaration manually, this is really awesome. Now, lets check the
before method in
App\Providers\RouteServiceProvider class. Well, this function is useful for route model binding, which means if we want to bind models to our routes then we need to do it within this
before method but in the earlier versions we used to do it in the
routes.php file, so let's see how can we do it, it's just as simple as it was before:
public function before(Router $router, UrlGenerator $url)
{
$url->setRootControllerNamespace('App\Http\Controllers');
// Binding the User model to 'example.com/user/1' route
// so the user model will be injected automatically
$router->model('user', 'App\User');
}
Also, we can use something like this as well:
public function before(Router $router, UrlGenerator $url)
{
$url->setRootControllerNamespace('App\Http\Controllers');
// Binding the User model to 'example.com/user/heera' route
// so the user model will be injected automatically
$router->bind('user', function($name){ // $name = heera
return \App\User::where('username', $name)->first();
});
}
Read more about routing on
Laravel website. The
before method gets called before our routes are loaded. So, this is different from older versions of the framework. Also, in
version 5.0 we are able to use
route annotations, which means if we don't want to declare our routes in the
routes.php file manually then we may leave it to the framework but in this case we need to use annotations in the controller, for example we may declare a method in a class with annotations, using something like this:
/**
* @Get("/users/{id}", as="user.show")
*/
public function show($id)
{
// ...
}
Now, if we run
artisan route:scan from command prompt/terminal then it'll automatically generate the routes for us and that will be saved in the
storage/framework/routes.scanned.php file so we don't need to declare the route manually in the
routes.php file. We can also use annotations for
Middleware and
Event as well, for example:
/**
* @post("/users/create", as="user.create")
* @Middleware("auth")
*/
public function register($id)
{
// ...
}
For
event we may use something like this:
/**
* @Hears("user.signup")
*/
public function UserRegistration(User $user)
{
// ...
}
In this case you need to run
artisan event:scan from the command prompt/terminal so the framework will register the listener or listeners (if there are more) into
storage/framework/events.scanned.php file. While annotation support is great but I like be explicit on route declaration and it looks properly organized to me but anyways, it's just personal preference. You may also like this, it's about
Middleware. Have a good time :-)
— The End —