Please note that this chapter was written for VERSION 3 of the Laravel PHP Framework.
In this chapter we will be creating a simple multi page website to demonstrate the workings of Laravel's routing system, without delving into anything too complicated.
As I mentioned in the previous chapter, there are two options available to route web requests to your code, Controllers and Routes. In this chapter we will be using Controllers since anyone joining us from other frameworks will be more familiar with them.
Routing Controllers
So let's start by taking a look at a Controller :
<?php
// application/controllers/account.php
class Account_Controller extends Base_Controller
{
public function action_index()
{
echo "This is the profile page.";
}
public function action_login()
{
echo "This is the login form.";
}
public function action_logout()
{
echo "This is the logout action.";
}
}
A Controller is a PHP Class that represents a section of your website, or web application. Its Methods or 'Actions' represent an individual page, or an end-point of a HTTP request.
In the above example our Account Controller represents our users section of the web site, a profile page, a login page, and a logout page. Note that the Controller names are appended with _Controller
and that action names are prefixed with action_
. Controllers must extend the Base_Controller
, Controller
or another Controller class.
Our controller is created in the application/controllers
directory as a lower-case file matching the controller name. The Controller above would be saved at :
/application/controllers/account.php
Before we can use our Controller we will need to register it in /application/routes.php
. Let's add the following line :
<?php
// application/routes.php
Route::controller('account');
If our controller is in a sub-directory of the controllers
directory simply use periods (.) to separate the directories like so :
<?php
// application/routes.php
Route::controller('in.a.sub.directory.account');
If our controller exists in a bundle, simply prefix with the bundle name and a double colon :
<?php
// application/routes.php
Route::controller('mybundle::account');
Now if we visit:
http://myproject/account/login
we see This is the login form.
. This is because now that our Controller has been mapped in the Route class, the first segment (between the slashes) of the URL specifies the controller, and the second segment (yes, again between the slashes) specifies the action.
In simple terms /account/login
is mapped to Account_Controller->action_login()
and the result of our method is displayed.
Now let's try visiting /account
instead :
This is the profile page.
Why does this happen? The index
action is a special action which is called when no action is specified in the URI, therefore the above page could also be "called" with the following URI :
/account/index
Passing Parameters
This simple routing is interesting, but it doesn't offer us anything that a simple PHP website could not.
Let's try something a little more dynamic. By adding parameters to our controller actions we can pass extra data as segments to the URL. Let's add a welcome action to our controller :
<?php
// application/controllers/account
public function action_welcome($name, $place)
{
echo "Welcome to {$place}, {$name}!";
}
Here our action parameters are method parameters, so the above code should seem familiar. Let's try visiting the route /account/welcome/Dayle/Wales
..
Welcome to Wales, Dayle!
Parameters can be used to pass resource identifiers to enable CRUD actions on data, or anything you can think of! As you can see, they offer a great deal of flexibility to our actions.
Note : You can assign values to your action parameters to make them optional in the URL.
Using Views
Echoing out source from our Controllers yields a result, but it's not an elegant solution. If you are interested in learning Laravel then elegant solutions may well have been what brought you here. The nature of MVC suggests that we separate our visual layer from the application's logic. This is where the 'Views' portion of the pattern comes into play.
With Laravel, views could not not be simpler. Simply add HTML templates to your /application/views/
directory with a lower-case file name, and a .php
extension. For example :
/application/views/welcome.php
With the contents :
<h1>Holla!</h1>
<p>This is the welcome action of the account controller.</p>
Now we need to return the View from our welcome action. Laravel has a beautiful expressive way of doing this, let's take a look :
<?php
// application/controllers/account.php
public function action_welcome($name, $place)
{
return View::make('welcome');
}
The more nerdy types among my readers will have realized that the statement is telling Laravel to create (make) a View object from the file application/views/welcome.php
(extension not needed here) and return it as the result of the welcome action.
You will also notice that the make()
method looks in the application/views
directory for its views. If you would like to specify an absolute path to a view file simply use the path:
prefix, for example path: /path/to/my/view.php
.
Now if we visit /account/welcome/Dayle/Wales
we will be greeted with the web page which we defined in our View file.
Note that you can also use the same sub-directory and bundle prefixes that we previously used with controllers, to refer to Views.
I know what you're thinking, now our welcome message isn't very dynamic at all? Let's see if we can fix this. Let's pass our action parameters to the View. We can do this using the with()
method and we can see Laravel's elegant method chaining in action, here we go!
<?php
// application/controllers/account.php
public function action_welcome($name, $place)
{
return View::make('welcome')
->with('name', $name)
->with('place', $place);
}
Using the with()
method we can pass any value (or object) to the View and give it a 'nickname' for accessing it from the view. We have used the same nicknames as our parameter names in this example, but you can call them anything you want!
Now let's use this data in our view :
<h1>Holla!</h1>
<p>Welcome to <?php echo $place; ?>, <?php echo $name; ?>!</p>
Now our action works as it did before, only better formatted with neater source code separating all logic from our visual layer.
Instead of using several with()
methods, you can pass an array as a second parameter to make()
with key-value pairs. This can save space but has the same result, here is an example.
<?php
// application/controllers/account.php
public function action_welcome($name, $place)
{
$data = array(
'name' => $name,
'place' => $place
);
return View::make('welcome', $data);
}
Note : I like to call my view array $data
, but you can call it whatever you want!
In a later chapter we will cover Views in more detail, including Blade templating, nested views and other advanced templating options.
RESTful Controllers
RESTful web applications respond to meaningful HTTP verbs with appropriate data. They are very useful when building public API's for your applications.
With Laravel you can have your controller actions respond to individual HTTP verbs using RESTful controller actions, let's see this in action.
<?php
// application/controllers/home.php
class Home_Controller extends Base_Controller
{
public $restful = true;
public function get_index()
{
//
}
public function post_index()
{
//
}
}
Simply add a boolean public class attribute named $restful and set it to true, then prefix your actions with the HTTP verb to respond to rather than action_.
Common HTTP verbs are GET, POST, PUT and DELETE.
The Base_Controller
You can edit the Base_Controller
, and extend it with your other Controllers to provide global functionality across all of your controllers. Add a default action index or class values, anything you want!
The Base_Controller can be found in /application/controllers/base.php
.
If you do not wish to use a Base_Controller, simply have your controllers extend the 'Controller' class instead.
Advanced Routing
Now we are able to map our controllers and actions to URI's in the format /controller/action/param/param/...
which is great, but we shouldn't be restricted to using only this format. Let's see if we can break out of the mold. Earlier we placed a controller declaration in routes.php
but now let's replace it with the following code.
<?php
//application/routes.php
Route::get('superwelcome/(:any)/(:any)', 'account@welcome');
Here we are saying, let's send all web requests with the GET
HTTP verb, and the address /superwelcome/(:any)/(:any)
to the welcome action of the account controller.
The (:any) segments are place-holders for our parameters, and will be passed in the order that they are provided. Using (:num)
will match only numbers, and using (:any?)
will create an optional segment.
So now a visit to /superwelcome/Dayle/Wales
will show our lovely view page!
The advantage of defining routes is that we can have our URLs in whatever order we like, in whatever format we like. For example we could also have..
<?php
//application/routes.php
Route::get('superwelcome/(:any)/(:any)', 'account@welcome');
Route::get('welcome/(:any)/to/(:any)', 'account@welcome');
Now we have two different routes, with the same result page.
It is worth noting that Routes that are defined "higher up" in the routes.php
file are given a higher priority. With the following example..
<?php
// application/routes.php
Route::get('(:any)/(:any)', 'account@welcome');
Route::get('welcome/(:any)/to/(:any)', 'account@welcome');
..the second route would never be triggered because the (:any)
in the first route would respond to the welcome
in the second route. This is a common mistake when starting out with Laravel. Be sure to keep an eye on the priority of your routes!
We will be covering routing in more depth in the next chapter which will also cover routing with closures instead of controllers.
My books are available online for free to encourage learning. However, if you'd like for me to keep writing, then please consider buying a digital copy over at Leanpub.com.
It's available in PDF, ePub, and Kindle format, and contains a bunch of extras that you won't find on the site. I have a full-time job, and I write my books in my spare time. Please consider buying a copy so that I can continue to write new books from the comfort of my sofa!