laravelbook.com

Warning: Work in Progress!

This site contains a a few lessons on Laravel 4. It's still in it's early phases. Besides, Laravel 4 itself is a moving target and a few things may have changed by the time you read this.

I will regularly update the content. Thanks!

Introduction to Laravel Framework

This book is about Laravel, a web application development framework that saves you time and effort and makes web development a joy. Laravel has shaken up the PHP community in a big way - especially when you consider that version 1.0 of Laravel was only a couple of years ago. It has been generating a lot of buzz with the promise of making web applications fast and simple to create. Using Laravel, you can build and maintain high-quality web applications with minimal fuss.

Certain parts of a web application development process can be a repetitive, boring task. Laravel lets you focus on the fun stuff - the crux of your web app - while easing the pain of the repetitive bits. In doing so, it provides high-level abstractions of common web development patterns, convenient shortcuts for frequent programming tasks, and clear conventions for how to solve problems.

What Is a Web Framework?

Laravel is a prominent member of a new generation of web frameworks - but what does that term mean, precisely?

Basically, a web framework makes it easier for you to develop your application. Most sites have a common set of functionality (like handling sessions, data validation, etc) and a framework is something that prevents you from re-writing this each time you create a website.

Let’s dive in with a quick example that demonstrates the difference between the previous approach and a web framework’s approach.

We will build a very simple guestbook application using plain vanilla PHP without a framework. We’ll create a new MySql database named “guestbook” and create the “entries” table:

CREATE TABLE `entries` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `username` VARCHAR(50) NOT NULL,
        `email` VARCHAR(100) NOT NULL,
        `comment` TEXT NOT NULL,
        `date_added` DATETIME NOT NULL,
        PRIMARY KEY(`id`)
);

On to the PHP stuff. Below is the entire source code of the guestbook script:

<?php
$db_host = "localhost";
$db_database = "guestbook";
$db_user = "Your_Database_Username";
$db_pass = "Your_Database_Password";

// connect to database
$db_conn = mysql_connect($db_host, $db_user, $db_pass);
mysql_select_db($db_database, $db_conn);

$gb_entries = ""; // the string we'll append entries to

// if form is submitted, then insert into database
if (!empty($_POST["submit"])) {
    $username = $_POST["frmName"];
    $email = $_POST["frmEmail"];
    $comment = $_POST["frmComment"];
    $date = Date("Y-m-d h:i:s");

    $sql_insert = "INSERT INTO entries(username, email, comment, date_added) VALUES('$username', '$email', '$comment', '$date')";

    mysql_query($sql_insert);
    $num_rows = mysql_affected_rows();

    // See if insert was successful or not
    if ($num_rows > 0) {
        $ret_str = "Your guestbook entry was successfully added.";
    } else {
        $ret_str = "Your guestbook entry was NOT successfully added.";
    }

    // append success/failure message
    $gb_entries .= "<p>$ret_str</p><br />";
}

$sel_select = "SELECT username, email, comment, DATE_FORMAT(date_added, '%m-%d-%y %H:%i') date_added FROM entries";
$result = mysql_query($sel_select);
while ($get_row = mysql_fetch_array($result, MYSQL_ASSOC)) {
    $username = $get_row["username"];
    $email = $get_row["email"];
    $comment = $get_row["comment"];
    $date = $get_row["date_added"];

    $gb_entries .= "<p>$comment</p><p>Posted on $date by <a href=\"mailto:$email\">$username</a><hr />";
}

// cleanup
mysql_free_result($result);
mysql_close($db_conn);
?>
<HTML>
<HEAD>
    <TITLE>Php Guestbook</TITLE>
</HEAD>
<BODY>
    <? echo $gb_entries; ?>

    <form action="<?php echo $PHP_SELF;?>" method="POST">
        <table border="0">
            <tr>
                <td>Name</td>
                <td><input type="text" name="frmName" value="" size="30" maxlength="50"></td>
            </tr>
            <tr>
                <td>Email</td>
                <td><input type="text" name="frmEmail" value="" size="30" maxlength="100"></td>
            </tr>
            <tr>
                <td>Comment</td>
                <td><textarea name="frmComment" rows="5" cols="30"></textarea></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" name="submit" value="submit">
                    <input type="reset" name="reset" value="reset"></td>
            </tr>
        </table>
    </form>
</BODY>
</HTML>

The guestbook application is ridiculously simple - it will display a form, collect some information from visitors and display the comments posted by them.

Here’s our guestbook app in action:

This PHP script combines all the logic and markup in a single file. This is not necessarily a best practice or modern programming technique in PHP, but it should be straightforward enough for any PHP developer to understand. With a single-page simple web app like this one, the write-it-from-scratch approach isn’t necessarily bad. For one thing, this code is simple to comprehend - even a novice developer can read and understand it fairly easily. But despite its simplicity, this approach has a number of problems and annoyances.

Ask yourself these questions:

  • What happens when multiple parts of your application need to interact with the database? Surely that database-related code shouldn’t need to be duplicated in each and every PHP script. The prudent thing to do would be to refactor all database code into a shared PHP file.

  • Should a developer really need to worry about opening and closing database connections, learn all the different data-access APIs for multiple database engines and memorize many different SQL dialects? This sort of boilerplate reduces programmer productivity, increases development time and introduces opportunities for bugs. The database-related tasks would best be handled by some database-agnostic, higher-level infrastructure that shields the developer from nitty-gritty of the underlying database implementation.

  • What happens when your web app is deployed in multiple heterogenous environments, each with a separate database and credentials? At this point, some environment-specific configuration becomes essential.

  • What happens when you develop your app using the MySql database, but your client wants to use PostgreSql instead? Should you go back and change all the mysql_* data-access APIs and SQL queries? The pragmatic thing would be to develop the application using database-agnostic libraries such the PDO. But that still doesn’t protect you from rewriting at least some of the SQL DDL and/or DDL queries.

  • What happens when a HTML/CSS/Javascript designer who has no experience coding PHP wishes to redesign the page? Altering the files in wrong places could crash your entire application. Ideally, the business logic of the web app as well as the database-related code should be separate from the HTML view, so that a designer could edit the latter without affecting the former.

These problems are precisely what a web development framework intends to solve. A web framework provides a programming infrastructure for your applications, so that you can focus on writing clean, maintainable code without having to reinvent the wheel. That’s exactly what Laravel does!

Let’s re-build the guestbook app from scratch using the Laravel framework. We will create a new database named “laravel_guestbook”.

I’ll assume you have Laravel installed in your local machine (installation of the Laravel framework will be discussed in a later lesson). Next open up the database.php file in the app/config directory of your Laravel application. Make sure that the default key is set to mysql:

return array(
        ...
        'default' => 'mysql',

Then enter your database information:

...
'connections' => array(
        'mysql' => array(
                'driver'    => 'mysql',
                'host'      => '127.0.0.1',
                'database'  => 'laravel_guestbook',
                'username'  => 'Your_Database_Username',
                'password'  => 'Your_Database_Password',
                'charset'   => 'utf8',
                'collation' => 'utf8_unicode_ci',
                'prefix'    => '',
        ),
...

It’s good practice to use migrations to track the changes in the database. You may think of Laravel migrations as version control for your databases. In order to use migrations, you first have to allow Laravel to create a special table in your database (this table is used by Laravel internally to to track the migrations that are executed).

We will use the Laravel Artisan command-line utility to install migrations in our database. Open a terminal at the root folder of your laravel application and execute the following command:

$ php artisan migrate:install
Nice! Now we're ready to do some migrating!

Once that’s done we’ll create the migration file that will create our entries table:

$ php artisan migrate:make create_entries_table
Migration created successfully!

Laravel will generate a new migration file (mine is named 2013_01_04_103348_create_entries_table.php) in the app\database\migrations folder. Let’s edit this file and define the schema for our entries table:

<?php

use Illuminate\Database\Migrations\Migration;

class CreateEntriesTable extends Migration {

        /**
         * Run the migrations.
         */
        public function up()
        {
        Schema::create('entries', function($t) {
          $t->increments('id');
          $t->string('username', 50);
          $t->string('email', 100);
          $t->text('comment');
          $t->timestamps();
        });
        }

        /**
         * Reverse the migrations.
         */
        public function down()
        {
                Schema::drop('entries');
        }
}

The up() method contains what you want to do to the database when you execute the migration. It will normally contain an array of column definitions to be created on the table. In our case, it contains the schema for our entries table. The down() method will contain what you want to do when you rollback or “undo” the migration.

Let’s switch to the terminal and execute the migration to actually create the table in the database:

$ php artisan migrate
Migrated: 2013_01_04_103348_create_entries_table

If you check your database, you’ll find Laravel has created the entries table for us:

CREATE TABLE `entries` (
        `id` INT(11) NOT NULL AUTO_INCREMENT,
        `username` VARCHAR(50) NOT NULL,
        `email` VARCHAR(100) NOT NULL,
        `comment` TEXT NOT NULL,
        `created_at` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
        `updated_at` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
        PRIMARY KEY (`id`)
);

Now we will create a Laravel data model class that represents the table described above. Our database table is called “entries”, so we will call our model class “Entry”. Let’s create a new PHP file app/models/Entry.php and paste the following code:

<?php

class Entry extends Eloquent {

        /**
         * The database table used by the model.
         *
         * @var string
         */
        protected $table = 'entries';

}

Taking a look at this file shows there isn’t much there. However, the functionality of our Entry class is slightly deceiving, because this model inherits all the functionality built into the Laravel Eloquent class. Your Entry model controls everything that goes in and out of the entries table we created. The Eloquent parent class that Entry inherits from is a high-level database abstraction layer. This layer is known as an “object relational mapper” because it maps each model directly to a database table.

Let’s give this application a face by creating the view template file. In Laravel, all template files are stored in the app/views folder. Laravel comes equipped with its own templating engine called the Blade templating engine. Blade templates files have the extension .blade.php. Let’s create a new file named home.blade.php in the app/views folder:

<HTML>
<HEAD>
    <TITLE>Laravel Guestbook</TITLE>
</HEAD>
<BODY>
    @foreach($entries as $entry)
      <p>{{ $entry->comment }}</p>
      <p>Posted on {{ $entry->created_at->format('M jS, Y') }}  by
         <a href="mailto:{{ $entry->email }}">{{ $entry->username}}</a>
      </p><hr />
    @endforeach

    <form action="/" method="POST">
        <table border="0">
            <tr>
                <td>Name</td>
                <td><input type="text" name="frmName" value="" size="30" maxlength="50"></td>
            </tr>
            <tr>
                <td>Email</td>
                <td><input type="text" name="frmEmail" value="" size="30" maxlength="100"></td>
            </tr>
            <tr>
                <td>Comment</td>
                <td><textarea name="frmComment" rows="5" cols="30"></textarea></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" name="submit" value="submit">
                    <input type="reset" name="reset" value="reset"></td>
            </tr>
        </table>
    </form>
</BODY>
</HTML>

This is essentially the same HTML code we wrote earlier in the plain PHP version. The only difference is that, here we’re using Laravel Blade template syntax e.g. @foreach($entries as $entry). As you may have guessed, @foreach is just like the foreach operator that we use in plain PHP to iterate over an array. In this case we’re iterating through all the guestbook entries in the database. {{ $entry->comment }} is the Blade syntax for PHP’s echo keyword. In this example, {{ $entry->comment }} is literally translated into <?php echo $entry->comment; ?>

Of course all the data that we are iterating through aren’t directly available in the view, we will have to pass the data to the view using the controller which will render this view. And that’s what were going to do next.

Let’s create a controller class to handle the business logic of our guestbook app. Since the primary responsibility of this controller is to store and retrieve records from the entries table, we’ll name it EntriesController. Laravel requires that you place all your controller classes in the app/controllers folder. Let’s create a new file named app/controllers/EntriesController.php and paste the following skeleton code:

<?php

class EntriesController extends BaseController {

        # Handles "GET /" request
        public function getIndex()
        {
        }

        # Handles "POST /"  request
        public function postIndex()
        {
        }

}

Controllers in Laravel usually descend from the BaseController super-class. By convention, controller method names in Laravel have the following syntax:

Name of the HTTP verb (in lowercase) + Name of the action (in Pascal-case)

For instance, if we want to handle the request GET /page, our controller method will be named getPage(). Likewise, if we wanted to handle POST /email and PUT /file our controller methods will named postEmail() and putFile() respectively.

Returning to our guestbook sample app, we want our controllers to respond to GET / and POST / requests; in other words, we wish to handle reqests for the “index” page. Therefore, we have named our controller methods getIndex() and postIndex() respectively.

Let’s first implement the controller for the HTTP GET / request:

public function getIndex()
{
        return View::make('home')->with('entries', Entry::all());
}

The above code snippet loads a view template named “home” and passes a variable named $entries to the template. Laravel automatically maps the “home” view to the app/views/home.blade.php template file. The $entries variable contains all rows from our entries database table. Recall that the table is represented by our Entry model class; the Entry::all() method retrieves all records from the database table it represents. Laravel then transforms the “home” template to a fully functional HTML web page and returns it to the user’s browser.

Now that we have a functioning HTML form, let’s implement the POST / controller:

public function postIndex()
{
    // get form input data
    $entry = array(
        'username' => Input::get('frmName'),
        'email'    => Input::get('frmEmail'),
        'comment'  => Input::get('frmComment'),
    );

    // save the guestbook entry to the database
    Entry::create($entry);

    return Redirect::to('/');
}

We’ll step through each segment of the code:

$entry = array(
    'username' => Input::get('frmName'),
    'email'    => Input::get('frmEmail'),
    'comment'  => Input::get('frmComment'),
);

Inside the method, we first have to get what the user has inputted in the HTML form. Laravel allows as to do that using the Input::get('form_field_name') method where the form_field_name is the name of the input field. For instance, we have an input field named “frmName” in our HTML form: <input type="text" name="frmName" />. The Input::get('frmName') method will return whatever data the user had entered in that textbox. In the above snippet, we gather all relevant form data into the $entry array. Take note that the array keys above exactly match the column-names of our entries table.

Entry::create($entry);

Inserting Eloquent models into your tables couldn’t be easier. We call the Eloquent::create() method which will insert a new record into the database for us - this method will extract the column-names from the associative array and automatically populate all database columns. Contrast the above line of code with the tedious SQL query and the multiple MySql API methods we had to write in our previous plain PHP guestbook app.

return Redirect::to('/');

After all work is done, we return a HTTP 302 redirect response to send the user back to our homepage; essentially, we’re transferring the request to the getIndex() controller.

Here’s the app/controllers/EntriesController.php file in its entirety:

<?php

class EntriesController extends BaseController {

        public function getIndex()
        {
                return View::make('home')
                        ->with('entries', Entry::all());
        }

        public function postIndex()
        {
            // get form input data
            $entry = array(
                'username' => Input::get('frmName'),
                'email' => Input::get('frmEmail'),
                'comment' => Input::get('frmComment'),
            );

            // save the guestbook entry to the database
            Entry::create($entry);

            return Redirect::to('/');
        }

}

We’re almost done with our application. We must let Laravel know about our EntriesController. Let’s open up the file app/routes.php and register our controller class by pasting the following code:

<?php

Route::controller('EntriesController', '/');

Here, we register our controller with the index URL (“/”).

It’s a good idea to generate PHP autoload files at this stage. We will use the composer command-line utility to do that:

$ composer dump-autoload

That’s it! We have just created our first Laravel application. Here’s the Laravel-powered guestbook app in it’s full glory:

Again, don’t worry too much about the details; just get a feel for the overall design of the app. The primary thing to note in the Laravel-powered application is the separation of concerns:

  • Our database table is now represented by a PHP class. This class is called a “model”. Using it, you can create, retrieve, update and delete records (“CRUD”) in your database using simple PHP methods rather than writing tedious SQL statements.

  • The EntriesController controller specifies which response is returned for a given URL pattern. In our case, the HTTP GET / request will be handled by the getIndex() method, and HTTP POST / request will be handled by the postIndex() controller. Our application logic is contained within these two controllers.

  • The app/views/home.blade.php file is an HTML view template that describes the design of the page. It uses the Laravel Blade template language with basic logic statements - e.g., @foreach($entries as $entry)

Taken together, these pieces loosely follow a design pattern called Model-View-Controller (MVC). Simply put, MVC is way of developing software so that the code for defining and accessing data (the model) is separate from request-routing logic (the controller), which in turn is separate from the user interface (the view). (We shall discuss MVC in more depth in a later chapter.)

A key advantage of such an approach is that components are loosely coupled. Each distinct piece of a Laravel web application has a single key purpose and can be changed independently without affecting the other pieces. For instance:

  • a developer can change the URL for a given part of the application without affecting the underlying implementation.
  • a designer can change a page’s HTML without having to touch the PHP code that renders it.
  • a database administrator can rename a database table and specify the change in a single location, rather than having to search and replace through dozens of files.

If all this code were interspersed through a single HTML-PHP hybrid file that contained the HTML view as well as all the model and controller (routing) data, it would not be nearly as manageable and maintainable. Design patterns such as MVC are created to make a developer’s life easier. This is where web frameworks like Laravel score over plain PHP.

The Laravel Advantage

Laravel embraces a general development philosophy that sets a high priority on creating maintainable code. By following some simple guidelines, you should be able to keep a rapid pace of development and be free to change your code with little fear of breaking existing functionality. Laravel achieves this by adopting several proven web development patterns and best practices.

Single Responsibility Pattern

The Laravel MVC architecture is great from a developer’s perspective because it separates each component of a web application into an isolated code base that is easily managed without having to worry about breaking other parts of your application.

Convention over configuration

Laravel defines the directory structure of your application for you and sets a series of conventions for naming files, classes, and database tables. It takes advantage of these conventions to tie together your application without a lot of configuration. You may initially be concerned about the idea of Laravel telling you how to structure your application. Worry not! You’ll quickly notice that not having to make these judgements yourself actually speeds up development time and creates a more consistent code base between different teams members and projects. By choosing smart defaults, Laravel allows you to focus on the functionality of your application and develop powerful web applications quickly, with code that is clean and easy to maintain.

Don’t-Repeat-Yourself (DRY)

Laravel promotes the DRY principle. Functionality is written cleanly once, and only once. Laravel provides an environment that makes it easy to consolidate shared code between different components of your application.

Unit testing

Laravel gives much importance to testing. Writing code is always done in parallel with tests to ensure the code works as intended and will continue to work when things around it change.