The Laravel PHP Framework: A Walkthrough

5th August 2011 | Tags:

Lar­avel is the new kid on the block of PHP frame­works, and whilst in its early stages of evo­lu­tion (the author wrote it, he says, in the early part of 2011) it does promise — and deliv­ers, from what I can tell — an expres­sive syn­tax and an ele­gant (indeed, you might say elo­quent) ORM. Lar­avel is a fully Object Ori­ented MVC frame­work which makes exten­sive use of fea­tures new or recent to PHP — you can for­get about run­ning it on PHP4. With PHP5.3 com­pli­ant name­spacing mech­a­nisms as opposed to, for exam­ple, Zend Framework’s pseudo-​namespacing, it’s cer­tainly what you might call a mod­ern framework.

Routes

In Lar­avel you define routes — which, stay­ing true to the REST­ful par­a­digm, are defined not just as URI’s, but accord­ing to the HTTP oper­a­tion they are to react to. Thus, instead of defin­ing a route such as /about-us you would define a func­tion to respond to a GET request on that URI, which is done using a clo­sure which looks reas­sur­ingly like JavaScript:

'GET /about-us' => function()
{       
    return 'some output';
}

Wild-​card para­me­ters allow para­me­ters to be passed via the URI, and indeed you can even restrict these to numeric ID’s. So for exam­ple, your route def­i­n­i­tion could look like this: ‘

'GET /person/(:num)' => function($id)
{       
    $user = User::find($id);
    $view = View::make('user');
    $view->bind('user', $user);
    return $view;
}

Or, you can cre­ate a slug using the built-​in URL class:

$article->slug = URL::slug($article->title, "-");

Then switch from :num to :any in your route definition:

'GET /article/(:any)' => function($slug)
{       
    // .. find the article identified by slug ...
}

If you think of the routes def­i­n­i­tions as being light­weight con­trollers, you’ll quickly see from the exam­ples above where the other com­po­nents of a fully-​fledged MVC archi­tec­ture come in; the ORM (called “Elo­quent”) is in action, finding a User entity with the ID pro­vided from the route, then being binded (well, bound) to a View. Mov­ing on from pro­cess­ing GET requests, a process to han­dle the sub­mis­sion of a form might be defined thus:

'POST /form-submit' => function()
{       
    // do some processing
    Redirect::to('/')->with('status', 'Your form submission has been received');
}

Note the flu­ent inter­face in the redi­rec­tion func­tion, allow­ing a flash mes­sage to be passed along the request chain. Nice syn­tax. Keep­ing REST­ful, a func­tion could be defined to delete a resource using some­thing like this:

'DELETE /person/(:num)/delete' => function($id)
{       
    // .. delete person resource identified by $id ...
    Redirect::to(/);
}

I keep talk­ing about the REST­ful­ness because the first thing that struck me is that it looks a great lit­tle frame­work for quickly scaf­fold­ing a web ser­vice. While there is not cur­rently any con­tent nego­ti­a­tion built in which would make this even eas­ier, it’s not dif­fi­cult to see how this could be extended.

Fil­ters

One of the things I par­tic­u­larly like about Lar­avel is that your route def­i­n­i­tions can also apply fil­ters to your input (for exam­ple CSRF pro­tec­tion or input sani­ti­sa­tion) or on their out­put. There are a cou­ple of fil­ters built in (for CSRF pro­tec­tion and for authen­ti­ca­tion) and they can be shared amongst routes. Thus, you could restrict access to a route from unau­tho­rised users thus:

'GET /secure' => array('before' => 'auth', 'do' => function() {

In the exam­ple above, the fil­ter auth is to be exe­cuted before exe­cu­tion of the route logic (i.e., the clo­sure defined in the ‘do’ ele­ment). (The for­mat of the route dec­la­ra­tion has changed slightly from the pre­vi­ous, sim­ply because the def­i­n­i­tion is get­ting slightly more com­plex.) The auth fil­ter is defined for you in another clo­sure, this time in the file filters.php:

'auth' => function()
{
    return ( ! Auth::check()) ? Redirect::to_login() : null;
},

This call­back high­lights another gea­ture of Lar­avel; the built-​in authen­ti­ca­tion mech­a­nism, which I’ll cover later — for now, what about that Redirect::to_login() syn­tax? This leads nicely onto named routes.

Named Routes

One of the other fea­tures of the routes sys­tem designed for devel­oper con­ve­nience is the option to cre­ate a def­i­n­i­tion like this:

'GET /user/login' => array('name' => 'login', 'do' => function()
{       
    return View::make('user/login');
}), 

Now, with the route to user_login assigned the name login, you can start doing this:

return Redirect::to_login();

How­ever, there is more to Lar­avel than rout­ing; here are some other fea­tures that stood out for me.

The ORM

Lar­avel has a built-​in Object Rela­tional Map­per (ORM) for data per­sis­tance, and pro­vides the Model to the router’s Con­troller and the Views. Defin­ing a new model using the Elo­quent ORM is as sim­ple as this:

class Post extends Eloquent { }

In this exam­ple a new model is defined which, unless spec­i­fied oth­er­wise, expects to find a table named posts (a plu­ralised, low­er­case ver­sion of the model’s name) and a pri­mary key named id It’s com­mon to include time­stamps on cre­ation and updat­ing; Lar­avel takes care of this out of the box — all you have to do is add columns called created_at and updated_at (both of the MySQL type TIMESTAMP) to your base table, and the fol­low­ing line in the class definition: ‘

class Post extends Eloquent {

    public static $timestamps = true;

Rela­tion­ships can be mod­elled eas­ily and intu­itively; as is com­mon across Lar­avel, if you keep to cer­tain nam­ing con­ven­tions then con­fig­u­ra­tion is simple:

class Post extends Eloquent {

    public function author()
    {
        return $this->has_one('Author');
    }

    public function tags()
    {
        return $this->has_and_belongs_to_many('Tag');
    }

The assump­tions here are:

  • Posts are defined in a table called posts (lower case, plu­ralised form of the classname)
  • Authors are defined in a table called authors
  • Tags are defined in a table called tags
  • The posts table con­tains a col­umn called author_id
  • All tables’ pri­mary key columns are called id
  • A for­eign key in the tags table is called post_id

Data­base Support

In addi­ton to the ORM there is a nice flu­ent query builder (not dis­sim­i­lar to Zend, or Dru­pal 7’s new data­base abstrac­tion layer), and it’s database-​agnostic; there is sup­port cur­rently for SQLite (the Lar­avel default), the ubiq­ui­tous MySQL or Post­gres. Con­fig­u­ra­tion for this, as with other areas, is sim­ple — in fact if you’re using SQLite and place your data­base in the /application/storage/db direc­tory and call it application.sqlite, then it requires no con­fig­u­ra­tion what­so­ever. So, for exam­ple, you can do this for insertion:

$hits = Db::table('hits');

$hits->insert(
    array(
        'url' => $url,
        'ts' => time(),
        'ip_address' => Request::ip()
    )
);

Or cre­ate queries such as:

DB::table('posts')
     ->where('author_id', '=', '123')
     ->where_in('status', array('draft', 'unpublished'))
     ->get();

You can even use what Lar­avel calls dynamic where meth­ods:

$user = DB::table('users')->where_email_and_password('hello@lukaswhite.com', 'secret');

Sim­ple Configuration

Con­fig­u­ra­tion is in the form of a col­lec­tion of files in the /application/config direc­tory, each return­ing an array of named con­fig­u­ra­tion val­ues. Extend­ing the con­fig­u­ra­tion options is as sim­ple as adding a new file. Add a file called facebook.php, for example: ‘

return array(
    'app_id'    => 'YOUR_APP_ID_HERE',
    'secret'    => 'YOUR_SECRET_HERE',
);

…and you can do some­thing like this from any­where in your code:

$facebook_config = Config::get('facebook');  // returns an array of options

Or you can be more specific:

$api_key = Config::get('facebook.api_key');

In keep­ing with Laravel’s inten­tion to require the min­i­mum pos­si­ble con­fig­u­ra­tion, the pre-​existing con­fig­u­ra­tion files come pre-​loaded with sen­si­ble default val­ues, but chang­ing any of them is simple.

An Auth Component

Authen­ti­ca­tion is largely taken care of for you by the Auth class; pro­vided you adhere to cer­tain assump­tions; users are rep­re­sented by an “Elo­quent” model, there is a password and a col­umn called salt (which is gen­er­ated for you by the framework’s built-​in hash­ing util­ity). The iden­ti­fy­ing col­umn is con­fig­urable how­ever, should you wish to allow peo­ple to log in using an email address, for exam­ple. There is not, at time of writ­ing, an autho­ri­sa­tion /​ACL model, but the authen­ti­ca­tion sys­tem pro­vided is prob­a­bly ade­quate for most small web applications.

A Data Val­i­da­tion Library

Data val­i­da­tion is neatly done, with a basic set of generic val­ida­tors — lengths, inclu­sion (e.g. check­boxes or radio but­tons), reg­u­lar expres­sion match­ing or accep­tance of (like a check­box being ticked, e.g. accep­tance of terms and con­di­tions) — all with the min­i­mum of con­fig­u­ra­tion. If you call a set of val­ida­tors with­out telling it what to val­i­date, it will auto­mat­i­cally val­i­date the input (e.g. GET /​POST para­me­ters). Again, sim­plis­tic, but cover most sim­ple cases — and there’s noth­ing to stop you from defin­ing your own.

Text and Inflec­tion Utilities

There are some fairly stan­dard text-​related helper func­tions for oper­a­tions such as:

  • (Eng­lish) lan­guage pro­cess­ing, such as pluralisation
  • Char­ac­ter or word-​based trimming
  • There is even a basic bad-​word (“cen­sor”) fil­ter There is also some rudi­men­tary inter­na­tion­al­i­sa­tion sup­port, pri­mar­ily for trans­lat­able strings.

Caching and Logging

While devel­op­ing an appli­ca­tion with the frame­work, you can opt to get an error mes­sage and a full stack trace in a nicely pre­sented error page. Addi­tion­ally, log­ging to a file is sim­ple — change a con­fig­u­ra­tion value or two and set the log files’ direc­tory to be writeable.

Exten­si­bil­ity

The true test of any frame­work, arguably, is its exten­si­bil­ity — and over­rid­ing or extend­ing frame­work classes is easy. Every­thing is name­spaced with frame­work classes aliased, thus:

$view = View::make('user/login');

…would, were it not for alias­ing, be:

$view = System\View::make('user/login');

The alias.php con­fig­u­ra­tion file defines these aliases as a name/​value array, e.g.:

return array(
    'Auth' => 'System\\Auth',
    ...
    'View' => 'System\\View',
);

There­fore should you wish, for exam­ple, to start using a cus­tom View across your code, a quick re-​jig of your aliases and it’s done:

return array(
    'View' => 'My\\View',
);

Con­clu­sions

Even after look­ing closely at Lar­avel and build­ing a few lit­tle appli­ca­tions with it, I stick by my view that it looks like a tool for build­ing web ser­vices — but this is no bad thing. In part it’s because some of the things I’ve become accus­tomed to for web appli­ca­tions aren’t there; man­ag­ing depen­den­cies (e.g. of Javascript files), lay­outs, dec­o­ra­tors and so on. There are things that are either lim­ited or sim­ple aren’t par­tic­u­larly evolved — the form-​building func­tions (which I’ve not even touched on) are a shadow of those offered by Zend_​Form, or Dru­pal with its Form API, for exam­ple — but this is a very new frame­work and one which will surely evolve. One of the great things about the frame­work is its learn­ing curve — indeed, I’ve cov­ered a lot of the basics here and if you work through this post you wouldn’t be far off being able to develop a sim­ple appli­ca­tion. Con­fig­u­ra­tion is beau­ti­fully sim­ple, and whilst cer­tain assump­tions can be frus­trat­ing at first, if you’re build­ing some­thing from scratch then you’ll soon appre­ci­ate how lit­tle you actu­ally have to do to get up and run­ning. The main thing that’s not clear nor well doc­u­mented is the process for extend­ing the func­tion­al­ity; extend­ing the sys­tem classes is easy but the pack­ages mech­a­nism is largely undoc­u­mented — but pack­ages are start­ing to appear nonethe­less. My view is that the frame­work would be an excel­lent intro­duc­tion to a sim­ple web frame­work from an O-​O minded PHP devel­oper, a promis­ing way to start build­ing a web ser­vice and a good way of build­ing a quick pro­to­type for a web­site or appli­ca­tion. Very promis­ing indeed.

Comments

    Thanks for the write-up on Laravel! Just wanted to point out that Asset management (including dependencies) is available in the latest release of Laravel. Here are the docs for it: http://laravel.com/docs/start/views#assets

    Managing layouts, etc. is a lot easier now thanks to view "composers": http://laravel.com/docs/start/views#composers

    Also, the authentication and logging are more flexible now thanks to the use of closures in the config. Using Eloquent is no longer required in the Auth system. You could even use something like MongoDB if you wanted to. This may not have been implemented when you wrote this article.

    Authentication Docs: http://laravel.com/docs/auth/config

    For logging, check out the "logger" closure in config/errors.php of the latest Laravel release.

    Again, thanks for the write-up and thanks for checking out Laravel!

    5th August 2011
    Taylor Otwell
    Taylor Otwell

    You forget to mention how easy to save data.

    $new_user = new User;
    $new_user->username = Input::get('username');
    $new_user->…

    $new_user->save();

    5th August 2011
    Anonymous
    Anonymous

    Great post, great framework.. Thanks!

    27th November 2011
    Anonymous
    Anonymous

    Thank you for bringing my attention to Laravel (not Lavarel). Since moving from .Net to be an Apple user I have been looking for a good framework. I think this is it. Yep, it appears to be eloquent indeed.

    Well done.

    18th January 2012
    Stephen Lewis
    Stephen Lewis

    Thanks very nice blog!

    28th March 2016
    vitamin otak anak
    vitamin otak anak

Links and images are allowed, but please note that rel="nofollow" will be automactically appended to any links.