Structuring a Laravel site

overview

Whilst I have created many sites using PHP in the past, I had never really used a framework outside of the very lightweight but powerful framework created by the head of programming at my work. Having recently been in website mode (working on a .NET MVC project) I was constantly reminded of how outdated and neglected my personal site had become. Enter Laravel.

I had heard many great things about Laravel over the past few months and I especially liked how it made a bold claim of implementing the best parts of other frameworks and languages and brought them together, so I decided to bite the bullet and give it a shot.

I set Laravel up on my Raspberry Pi which already had LAMP installed and was ready to go. One thing that was new to me was Composer which Laravel uses to install itself and its dependancies. Composer is essentially a package manager just like apt-get or pacman. I love the idea of composer but one has to question the security aspect of it (you have to trust the code which it downloads is safe to run on your server).

Laravel utilises the MVC (Model/View/Controller) software pattern. The Model part is essentially your abstract data representation and logic, the View contains code for the UI (in this case the HTML the user sees) and the Controller binds them together.

The Task

The task was to not only build a simple to use and easy to maintain blogging system (ugh I hear you cry, not another one!), but also a place I can show off some code and have fun at the same time. Why not just use Wordpress I hear you ask… well it’s a case of the been there, done that syndrome.

Web Technologies Used

Looking back I can say that on top of Laravel I used
* Google Prettify - code highlighting
* Pagedown - Great markdown editor used to write the website content
* Pagedown Extra - Adds some great time saving features on top of pagedown
* three.js - for the webgl 404 page you should totally checkout (defaults to an image if you don’t have webgl available)

Laravel allows you to install Bootstrap themes, but in this case I just created my own CSS.

Routing & Controllers

The routing feature in Laravel is great as it allows you to have pretty URLs whilst neatly directing incoming requests to the correct place within your code. Most of the examples of routing on the Laravel website show nested functions within the route handlers but I tend to avoid nesting functions as much as possible. Luckily Laravel supports sending the incoming request to a controller class method you designate using the syntax

Route::get('/', 'PageController@getIndex');

where the first parameter is the URL of the request coming in, and the second parameter contains the name of the controller class along with the name of the function you wish to run on that controller, separated by the @ symbol.

Having a routes.php file that contains pointers to controller methods like above makes for a very neat and readable file which allows you visualise your whole site as a map from one central place. This method also has the advantage of being able to specify route filters such as the built in before filter (this allows you to run a designated function before the request hits the intended route function). To explain further this would allow you to, for example, run a function that checks whether a user is logged in and has permission to access the requested page - if they don’t you could redirect them to another page, for example a login page.

Services

Now with the routes in place pointing towards controller methods, I decided to create a services layer so the controllers I create do not get over burdened with unnecessary logic. I created a directory called services in Laravels app/ directory and within it created a BaseServices class. This class would act as foundation for the rest of the services I would make in the future. I spent the next few hours using PHPs require_once() function to load my services into the controller classes before I discovered Laravels class loader functionality. This functionality essentially autoloads any classes you may need without having to manually use require() or require_once() yourself. Handy! I just had to fire up app/start/global.php and add an entry for the path to the services directory I had previously created. Any classes within this directory will now be immediately accessible in the code. Great stuff.

The idea of the services layer is to handle pulling data from the database and hand it back to the controller in a sanitised form which could then be passed into a Laravel view for displaying to the user. To use the services I would create a controller class global variable called $services and initialise it to a new instance in the controllers constructor. I could then access this $services variable from any method within the controller, for example:

public function create_article($id=-1)
    {
        $article = new ArticleModel();

        if($id > -1)
        {
            $article = $this->services->getArticle($id);
        }

        if(Request::isMethod("post"))
        {
            $article = ArticleModel::fromInput();
            if($article->isValid())
            {
                $this->services->saveArticle($article);
                return Redirect::action("AdminController@view_articles");
            }
        }

        return View::make("admin/create_article")->with("article", $article);
    }

The route for the create_article() above looks like this:

Route::match(array('GET', 'POST'), '/admin/create_article/{id?}', 'AdminController@create_article');

Essentially, this create_article() function is used to show a form to an administrator which they can use to create an article for the website. If an id is supplied (pulled from the route via the {id?} part and injected into the controller functions parameters - the question mark means it’s optional) the services will retrieve the article from the database with the provided id. This then gets returned to the view and the form will auto populate itself with the values provided in the ArticleModel instance.

You may have also noticed the function also checks whether we are currently within a POST request (see how the route handles both GET & POST with the matches() function). This will happen when the user hits submit on the form within the view. When this happens we first receive the POST data via the static fromInput() method I had created. Laravel provides an Input interface that allows you to receive values from GET and POST methods, and also allows you to provide default values if a requested value does not exist. I had made an IViewModel interface that required all view related models I create to have a static fromInput() function. This function creates an instance of itself (the class implementing the interface) based on the values from the Input data. The ArticleModel fromInput() function looks like this:

public static function fromInput()
    {
        $id = Input::get("id", -1);
        $title = Input::get("title", "");
        $markdown = Input::get("markdown", "");
        $content = Input::get("content", "");
        $publish_date = Input::get("publish_date", "");
        return new ArticleModel($id, $title, $markdown, $content, $publish_date);
    }

So now we have an instance of the ArticleModel class based on the values from the form, we can validate the data to ensure all fields are provided and it is safe to store in the database. Laravel provides validation functionality that you can implement and extend. My IViewModel interface also contains an isValid() function which means each model must check itself to ensure the data it contains is not only present but also safe.

If the article passes the validation we can then finally utilise the $services again to save the article to the database and redirect to another page once we are done. If the data is invalid the model is passed back to the view (which again auto populates itself) but this time will show any error messages created by the validation.

Databases

Laravel has a few options for querying databases but I chose to go with the Query Builder which reminded me of .NETs Linq syntax. I never once had to write any raw SQL for the site as the Query Builder was powerful enough to handle the sites needs. As well as the basic select, amongst others it has functionality for joins, group by, limiting and ordering. I can highly recommend the Query Builder to anyone interested in trying it. One of the great things about it is that it takes care of SQL injection for you so you don’t have to worry. Here is an example query this site uses in Query Builder form:

$results = DB::table("page_types")
    ->where("name", $type)
    ->join("pages", "pages.page_type_id", "=", "page_types.id")
    ->join("page_tags", "page_tags.page_id", "=", "pages.id")
    ->select("page_tags.tag")
    ->orderBy("tag", "ASC")
    ->get();

The next site I create I will investigate the usage of Laravels Eloquent ORM which looks akin to .NETs Entity Framework model, which could be very promising indeed.

Views

Laravel has some great templating features which utilises the Blade templating engine. This engine reminds me greatly of the .NET MVC razor template engine which is great to see. I like how you can create a master layout file containing your main site template but also create your own custom render sections. For example I made a couple of custom sections in the sites like so:

<!doctype html>
<html>
<head>
...
@yield('css')
@yield('scripts')
...
<title>Dan Cotton</title>
</head>

which allows me to provide custom CSS and Javascript based on the current page you are viewing. To provide data to these sections you would load up a view and use this:

@section('css')
<link rel="stylesheet" type="text/css" href="/css/pagedown.css">
@stop

The above code was added to the create_article page discussed above. This CSS sheet would then get added to the of the create_article page.

Generally I found that in .NET MVC creating the view content was faster than in Laravel due to Visual Studios built in scaffolding, however I have recently discovered that the Laravel community has created scaffolding engines which I have yet to play with.

wrap up

It’s safe to say that the next site I create will be using Laravel as it has so many out-of-the-box features that make your life so much easier. For example, usually I would utilise mod_rewrite by creating a RewriteRule in an .htaccess file to create pretty URLs but Laravel already has this in place and takes the pain away from it with its built in routing. It’s database handling was great fun to use and it’s built in user auth makes it a winner.

Tagged with
Laravel , PHP