Simple and Easy Laravel Routing



One of the coolest (arguably the coolest) features of Laravel is its amazing routing features. Coming from Codeigniter where all routing is done by controller and method names, this was a breath of fresh air. I like seeing a top down view of my application and where all the pages are, and the routes file is like staring down at a roadmap from the clouds.
Today, we will not only explore the types of routes and features like filters, we will look more closely at applying them to real world scenarios. These scenarios include:
  • Routing for basic static pages
  • Passing parameters to functions
  • Using named routes to make URLs easier to use
  • Group routing
  • Filtering routes for Auth or anything else
  • Handling 404 Errors
Routing to Controllers and Controllers will be handled more in depth in a totally separate article. So much info there that it deserves its own party.

Let’s create an imaginary application to test all of our routing. We’ll make an online portfolio site with a blog and a backend admin section to add posts. Here are the guidelines for our little application.

Requirements:

  • Super cool name
  • Basic pages (Home, About, Work)
  • Blog pages (main page and category page)
  • Admin section (Authentication required)
  • Login page for the admin section
  • 404 page
Routing Only This tutorial will only deal with the code associated with routing. More tutorials will follow to explain how to fully build a web application with all the glorious parts of Laravel.
Let’s get started!

Super Cool Name

Naming your application is half the battle. Maybe not half, but it sure feels like it. Let’s call this project Lagavulin.

Basic Pages Basic Routing

Let’s open up our app/routes.php file and get our static pages (Home, About, and Work) routed to. These will be the easiest since they don’t really require anything other than their view. No login, no parameters, just good ol’ displaying a page.
Delete everything out of your routes.php and we will start fresh.
// app/routes.php
<?php

    // ===============================================
    // STATIC PAGES ==================================
    // ===============================================

    // show a static view for the home page (app/views/home.blade.php)
    Route::get('/', function()
    {
        return View::make('home');
    });

    // about page (app/views/about.blade.php)
    Route::get('about', function()
    {
        return View::make('about');
    });

    // work page (app/views/work.blade.php)
    Route::get('work', array('as' => 'work', function()
    {
        return View::make('work');
    }));

Now you can visit yoursite.com, yoursite.com/about, or yoursite.com/work.

Named Routing

You’ll notice on the work route, we’ve added an array as. This creates a named route. With a named route, you can link to it in your application views using {{ URL::route('work') }}. This will not work for the home or about page, however, since we did not name them.
The home and about pages will need to be linked to using {{ URL::to('/') }} and {{ URL::to('about') }}. The benefit of a named route is that even if you change the location of the route, as long as the name stays the same, the link will still work.
Here’s a little table to shows why named routes are great. Hint: You don’t have to go through your entire application spend time changing links.
Original Changed To Works?
Route::get(‘about’)… Route::get(‘about-us’)… {{ URL::to(‘about’) }}
Route::get(‘work’, array(‘as’ => ‘work’… Route::get(‘our-work’, array(‘as’ => ‘work’… {{ URL::route(‘work’) }}
That’s it for static pages. Laravel uses closures to handle routes. You can return anything you want here. In our case, we wanted to return a view file since these are just boring static pages.

Blog Pages with Categories Route Parameters

Now we’re past the easy peazy static page routing. Our blog pages will need more than just static pages. They will need information from the database like our posts which include pictures, content, title, author, date, etc…
We will create one route to handle all our blog stuff. This route uses optional route parameters to check if there are categories present. If there is a category, it will pull those blog posts. If not, it will show all posts.
To make the category required, just take out the ? and = null.
// app/routes.php
<?php

...

    // ===============================================
    // BLOG PAGES ====================================
    // ===============================================

    // blogroll to show all blog posts
    Route::get('blog/{category?}', function($category = null)
    {
        // get all the blog stuff from database
        // if a category was passed, use that
        // if no category, get all posts
        if ($category)
            $posts = Post::where('category', '=', $category);
        else
            $posts = Post::all();

        // show the view with blog posts (app/views/blog.blade.php)
        return View::make('blog')
            ->with('posts', $posts);
    });

You can break this down into separate routes if you like. This can make things cleaner for you if you need to have different styled views based on category or any other special things.
We get all the blog posts using Eloquent ORM You have other options for accessing the database, but since we’re just explaining routes, we’ll keep it simple. We’ll talk more on Eloquent and that database stuff later. That wraps it up for our blog pages.

Admin Section Group Routing, Route Prefixing, and Auth Filters

Our blog needs an admin section so we can add posts. It needs to be protected from the crazy people in the world so we’ll add authentication requirements. We also want all our URLs to start with /admin.
Route groups are a great way to apply the same prefixes, filters, or anything else to a group of routes. Read more about route groups.
Route prefixing will allow us to add /admin to the beginning of all admin routes. This way, we won’t have to do Route::get('admin/something')... every time. Read more about prefixing.
// app/routes.php
<?php

...

    // ===============================================
    // ADMIN SECTION =================================
    // ===============================================
    Route::group(array('prefix' => 'admin'), function()
    {
        // main page for the admin section (app/views/admin/dashboard.blade.php)
        Route::get('/', function()
        {
            return View::make('admin.dashboard');
        });

        // subpage for the posts found at /admin/posts (app/views/admin/posts.blade.php)
        Route::get('posts', function()
        {
            return View::make('admin.posts');
        });

        // subpage to create a post found at /admin/posts/create (app/views/admin/posts-create.blade.php)
        Route::get('posts/create', function()
        {
            return View::make('admin.posts-create');
        });
    });

Auth filters will be applied to the entire group to protect the entire admin section. You can create your own filters and do anything you like to limit access to a route. For our purposes, we will use the before Read more about filters.
We will add the auth filter, which is already defined in app/filters.php to our group. This will redirect a user to /login if they are not logged in. Let’s add the auth filter to the group and add a route for the login page.
// app/routes.php
<?php

...

    // ===============================================
    // LOGIN SECTION =================================
    // ===============================================
    // show the login page
    Route::get('login', function()
    {
        // show the login page (app/views/login.blade.php)
        return View::make('login');
    });

    // ===============================================
    // ADMIN SECTION =================================
    // ===============================================
    Route::group(array('prefix' => 'admin', 'before' => 'auth'), function()
    {
        // main page for the admin section (app/views/admin/dashboard.blade.php)
        Route::get('/', function()
        {
            return View::make('admin.dashboard');
        });

        ...

    });

Now if somebody tries to access lagavulin.com/admin or lagavulin.com/admin/posts, they will be redirected to the login page.

Login Form POST Routing

We will need to validate and process the login form. Let’s make a quick route to do that and use Redirect::intended() to redirect them back to their intended destination before our filter so kindly kicked them to the login page.
// app/routes.php
<?php

...

    // ===============================================
    // LOGIN SECTION =================================
    // ===============================================
    // show the login page
    Route::get('login', function()
    {
        // show the login page (app/views/login.blade.php)
        return View::make('login');
    });

    // process the login
    Route::post('login', function()
    {
        // validate
        // process login
        // if successful, redirect
        return Redirect::intended();
    });

    // ===============================================
    // ADMIN SECTION =================================
    // ===============================================

        ...

    }));


Creating Blog Posts

Now that we’ve seen how POST routing works, let’s handle creating the new blog post form. The Route::get('posts/create')... will show the form. In our form action, we can set it to <form action="/admin/posts/create">. Let’s add a route to process the form, and show a message that the post was saved.
// app/routes.php
<?php

...

    // ===============================================
    // ADMIN SECTION =================================
    // ===============================================
    Route::group(array('prefix' => 'admin', 'before' => 'auth'), function()
    {

        ...

        // subpage to create a post found at /admin/posts/create (app/views/admin/posts-create.blade.php)
        Route::get('posts/create', function()
        {
            return View::make('admin.posts-create');
        });

        // process the create blog post form
        Route::post('posts/create', function()
        {
            // add the post to the database
            // $post = new Post;
            // $post->title = Input::get('title');
            // more stuff here
            // $post->save();

            // create a success message
            Session::flash('message', 'Successfully created post!');

            // redirect
            return Redirect::to('admin/posts/create');
        });
    });

Processing forms and saving to the database is easy and fun. We’ll get to it in the next few Laravel posts. Now that we have static pages, blog pages, admin section and blog post creation, let’s handle 404 errors.

404 Errors

404 Errors are very easy to handle. We’ll add a simple route to the end of our routes.php file and call it a day. To read up more on handling all sorts of errors, read the error docs.
// app/routes.php
<?php

...

    // ===============================================
    // 404 ===========================================
    // ===============================================

    App::missing(function($exception)
    {

        // shows an error page (app/views/error.blade.php)
        // returns a page not found error
        return Response::view('error', array(), 404);
    });

Conclusion

As you can see, Laravel makes it very easy to lay the foundation of your entire site in the routes.php file. You can see every page, section, authorization requirements, and handle RESTful routes. This helps us to maintain our application from this file and really get a vision of how everything works together.
Here is the full code for our routes.php file now. We were able to set the foundation for a blog and portfolio site with an authenticated admin section!
// app/routes.php
<?php

    // ===============================================
    // STATIC PAGES ==================================
    // ===============================================

    // show a static view for the home page (app/views/home.blade.php)
    Route::get('/', function()
    {
        return View::make('home');
    });

    // about page (app/views/about.blade.php)
    Route::get('about', function()
    {
        return View::make('about');
    });

    // work page (app/views/work.blade.php)
    Route::get('work', array('as' => 'work', function()
    {
        return View::make('work');
    }));

    // ===============================================
    // BLOG PAGES ====================================
    // ===============================================

    // blogroll to show all blog posts
    Route::get('blog/{category?}', function($category = null)
    {
        // get all the blog stuff from database
        // if a category was passed, use that
        // if no category, get all posts
        if ($category)
            $posts = Post::where('category', '=', $category);
        else
            $posts = Post::all();

        // show the view with blog posts (app/views/blog.blade.php)
        return View::make('blog')
            ->with('posts', $posts);
    });

    // ===============================================
    // ADMIN SECTION =================================
    // ===============================================
    Route::group(array('prefix' => 'admin'), function()
    {
        // main page for the admin section (app/views/admin/dashboard.blade.php)
        Route::get('/', function()
        {
            return View::make('admin.dashboard');
        });

        // subpage for the posts found at /admin/posts (app/views/admin/posts.blade.php)
        Route::get('posts', function()
        {
            return View::make('admin.posts');
        });

        // subpage to create a post found at /admin/posts/create (app/views/admin/posts-create.blade.php)
        Route::get('posts/create', function()
        {
            return View::make('admin.posts-create');
        });

        // process the create blog post form
        Route::post('posts/create', function()
        {
            // add the post to the database
            // $post = new Post;
            // $post->title = Input::get('title');
            // more stuff here
            // $post->save();

            // create a success message
            Session::flash('message', 'Successfully created post!');

            // redirect
            return Redirect::to('admin/posts/create');
        });
    });

    // ===============================================
    // 404 ===========================================
    // ===============================================

    App::missing(function($exception)
    {

        // shows an error page (app/views/error.blade.php)
        // returns a page not found error
        return Response::view('error', array(), 404);
    });