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 :-)