Laravel-5.0 And Routing
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.
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 🙂