Advertisement
PHP

The Essentials of Creating Laravel Bundles

by

The Laravel PHP framework offers its bundles system to allow developers to redistribute useful packages of code, or to organize applications into several "bundles" of smaller applications.

In this tutorial, we will learn the ins and outs of creating and distributing bundles from scratch.

A Laravel bundle has access to all of the features that the framework offers to its host application, including routing, migrations, tests, views and numerous other useful features.

Here's a little secret, between us: the application folder of the Laravel source package is also a bundle, which Laravel refers to as the DEFAULT_BUNDLE.


When to Create a Bundle?

Before writing a new piece of code, I like to ask myself a few simple questions to determine whether it is appropriate for a bundle. Let me share this technique with you.

Could this code be useful to others?

If the answer to this question is yes, then I would first ensure that someone has not already created a similar bundle or package. Other than for learning purposes, it's pointless to recreate the wheel. If the other package is of a high enough standard to be used in your project, then use that instead and save yourself the time.

Secondly, I think about the code and decide whether or not it might be useful to users of other frameworks, or people not using a framework at all. If the code is not related to the Laravel framework and does not need to make use of Laravel's core classes, then I would create a Composer package instead. Composer packages are widely becoming the standard for sharing code that is not restricted to a single framework or project.

For more information on Composer, refer to the following links:

If the code could be useful to others, and is dependent upon the Laravel framework, then you have a good reason to create a new bundle.

Will I have to write this code again?

DRY is the name of the game.

If the code provides functionality that you write frequently, then it makes sense to create a bundle. DRY (Don't repeat yourself!) is the name of the game.

Could this code be considered a stand-alone application?

For example, you may be building a simple site that, amongst other features, has a blog component. The blog could be considered a separate application to be contained in a bundle for much greater organization of your project.

Another example would be an administrative section, or 'back-end' for your website. This section could easily be considered a separate component from the main application, and could instead be organized into one or more bundles.

Would this code fit into a single class?

If this is the case, you might consider writing a 'Library' instead. A library is a single class that contains reusable code. It can be added to a Laravel project easily by dropping the class into the application/libraries/ directory, which is auto loaded by default.


Creating a Bundle

Let's create a simple plug-in that interacts with the Gravatar service to offer a simple method for generating avatars of various sizes within our main application. We will also add the necessary functionality to enter an email address and avatar size, and preview the associated gravatar on the page.

Let's get started by creating a new directory within the /bundles directory of our project. We will call the directory and our bundle gravvy. Not gravy... gravvy.

Let's add gravvy to the bundles array within application/bundles.php so that we can test it as we go along. We will add an 'auto' => true option to the array so that the bundle will be started automatically, and any autoloader mappings we create will be available to the whole of Laravel.

return array(
    'docs' => array('handles' => 'docs'),
    'gravvy' => array(
        'auto' => true
    )
);

First, we will need to create a small library that will retrieve a user's avatar, using an email address. Create a new file within the root of the bundle, named gravvy.php. Let's create a class, called Gravvy with a static method, make(), to replicate the naming scheme used by Laravel's own libraries.

The make() method will accept two parameters: an email address and an integer to represent the size of the avatar to retrieve.

<?php

/**
 * Class to create Gravatar image elements.
 *
 * @author  You <you@you.com>
 */
class Gravvy
{
    /**
     * Create a new image element from an email address.
     * @param  string  $email The email address.
     * @param  integer $size  The avatar size.
     * @return string The source for an image element.
     */
    public static function make($email, $size = 32)
    {
        // convert our email into an md5 hash
        $email = md5($email);

        // return the image element
        return '<img src="http://www.gravatar.com/avatar/'
                    .$email.'?s='.$size;
    }
}

Bundle root directories aren't auto-loaded, so let's write a mapping so that Laravel knows where to find the 'Gravvy' class when it needs it.

When starting a bundle, Laravel looks for a file, named start.php, and executes it. So let's create one within our new bundle's directory to hold our auto-load mappings.

<?php

// /bundles/gravvy/start.php

Autoload::map(array(
    'Gravvy' => path('bundles').'/gravvy/gravvy.php'
));

Now Laravel will knows where to find the definition for our Gravvy class, and will load the source when it first needs it. Very efficient!

The path() method is a helper function, which returns the absolute path to useful folders used by Laravel. In this case, we are using it to retrieve the absolute path to the bundles directory.

Now that the we have our working Gravvy class, we could attempt to use it from within a controller to see if we get the expected output, but I think it would be more appropriate to write a unit test.

Just like the host application, unit tests are available from within the bundle. Let's create a tests folder within the bundle, and add a new file, called general.test.php.

<?php

class TestGeneral extends PHPUnit_Framework_TestCase {

    /**
     * Test that an avatars output appears as expected.
     *
     * @return void
     */
    public function testAvatarImageIsGenerated()
    {
        // start the gravvy bundle
        Bundle::start('gravvy');

        // check that the output matches the expected
        $this->assertEquals(Gravvy::make('thepunkfan@gmail.com'),
            '<img src="http://www.gravatar.com/avatar/fac3a58aaa455adbcb3f684ccff663b8?s=32" />');
    }

    /**
     * Test that an avatars output appears as expected when
     * specifying a custom avatar size.
     *
     * @return void
     */
    public function testAvatarImageIsGeneratedWithSize()
    {
        // start the gravvy bundle
        Bundle::start('gravvy');

        // check that the output matches the expected
        $this->assertEquals(Gravvy::make('thepunkfan@gmail.com', 64),
            '<img src="http://www.gravatar.com/avatar/fac3a58aaa455adbcb3f684ccff663b8?s=64" />');
    }

}

Above, we've written two PHPUnit tests: one to test the output of generating an avatar using an email, and another that also specifies an avatar size in pixels. You will notice that we call Bundle::start('gravvy') to manually start the bundle. This is because Laravel does not auto load bundles through the command line interface at present.

As a core team member, I'd like to point out that we intend to resolve this in a future version!

Let's use Artisan to run our PHPUnit tests by typing the test command and using the bundle name, gravvy, as a parameter.

php artisan test gravvy
PHPUnit Result

Great! Our tests have run successfully on the first try, and our ego hast grown - just a little!

Now that our Gravvy class has been tested, people can use it in their own applications! Let's take the bundle a step further and create a couple of simple pages to generate and preview gravatars. We can use this example to learn how the routing system handles bundles.

To begin, let's create a new 'preview' controller for our bundle. We will need to create a controllers directory within the bundle, and, within it, we'll add a new file: preview.php.

<?php

class Gravvy_Preview_Controller extends Controller
{
    /**
     * Show the preview avatar form.
     */
    public function action_form()
    {
        // load the form view
        return View::make('gravvy::form');
    }

    /**
     * Show the resulting avatar.
     */
    public function action_preview()
    {
        // load the preview view
        return View::make('gravvy::preview');
    }
}

The controller name must be prefixed with the bundle name, and appended with _Controller - as with normal controllers.

We could create some routes to map our controller actions to sensible URIs, but wouldn't it be better if we could let the user of our bundle decide on the base URI to use? It would? Let's do that then!

By adding a 'handles' => 'gravvy' key-value pair to the bundles configuration array, we can allow the user to change it without altering the code of the bundle itself. Here's the resulting configuration in application/bundles.php.

return array(

    'docs' => array('handles' => 'docs'),
    'gravvy' => array(
        'auto'      => true,
        'handles'   => 'gravvy'
    )

);

Now we can use the (:bundle) place-holder in our routes, which will be replaced with the value of the handles option. Let's create a routes.php file within the root of our bundles and add some routes.

Route::get('(:bundle)/form', 'gravvy::preview@form');
Route::post('(:bundle)/preview', 'gravvy::preview@preview');

We have the route GET gravvy/form which is mapped to the form action of the Preview controller, and POST gravvy/preview which is mapped to the preview action of the Preview controller.

Let's create the associated views for our controller actions; you can make them as complex and pretty as you like, but I am going to keep them simple. First, create a views folder within the bundle, just like with the application directory.

<!-- /bundles/gravvy/views/form.blade.php -->

<form action="{{ URL::to_action('gravvy::preview@preview') }}" method="POST">
    <p><label for="name">Email Address:</label></p>
    <p><input type="text" name="email" /></p>
    <p><label for="name">Avatar Size:</label></p>
    <p><input type="text" name="size" /></p>
    <p><input type="submit" value="Preview!" /></p>
</form>

Now that we have a form that will submit an email and size field to the preview@preview controller/action pair, let's create a preview page for the generated avatar; we'll use an attribute, named $element, to hold its source.

<!-- /bundles/gravvy/views/preview.blade.php -->

<p>{{ $element }}</p>
<p>{{ HTML::link\_to\_action('gravvy::preview@form', '< Go Back!') }}</p>

Now we must alter the preview action to make use of the data submitted from the form.

/**
 * Show the resulting avatar.
 */
public function action_preview()
{
    // get data from our form
    $email  = Input::get('email');
    $size   = Input::get('size');

    // generate the avatar
    $avatar = Gravvy::make($email, $size);

    // load the preview view
    return View::make('gravvy::preview')
        ->with('element', $avatar);
}

We retrieve the POST data and use it to create our avatar. We must also add a with() method to the View::make() chain to allow for the element to be used within the view.

We can finally test our avatar previewing system! Take a look at the /gravvy/form URI and give it a go! Everything works as expected.

Gravvy Views

This may not be the best way to organize your bundle, but it does highlight some of the useful things that are possible. Have fun creating your own bundles, and be sure to consider publishing them on the bundles website.


Publishing a Bundle

Once your bundle is in a functional state, you may want to consider listing it within the Laravel Bundles Directory. Let's run through the process of submitting a new bundle.

First, you will need to have a GitHub account, and have your bundle versioned within a public repository. GitHub offers free accounts with an unlimited number of public repositories; you will find their sign up form here.

If you are new to version control with Git, I suggest reading the great series of Git articles right here on Nettuts+.

Once you have your account and code in order, make sure that the latest version of your bundle can be found within the 'master' branch, and that the root of your bundle (where the start.php would be) is the root of the repository, rather than a subdirectory.

Next visit the Laravel Bundles Directory website, and sign in using your GitHub credentials.

github login

Now click the 'Submit a Bundle' button, select your bundle repository from the drop down menu and hit the 'Continue' button.

The sign up form is quite straight forward, but here are some 'gotchas' that you may not spot.

Name

Name is a the lowercase keyword that is used to install your application. It needs to be a short but accurate word to describe your bundle.

Summary / Description

These fields can contain markdown format content. So feel free to copy the content from your GitHub README.md file.

Dependencies / Tags

Use the comma button on your keyboard to separate tags and dependencies. The dependencies field should contain the short install keyword for the bundle that exists as a dependency for the bundle you are submitting.

Active

The Active field simply determines whether or not the bundle will be displayed to other users. You are still able to install inactive bundles by their install keyword for testing purposes. Set this field to 'Yes' only when you are happy for other people to use your bundle.

Once you click the 'Save' button, your bundle has been submitted, and, if marked as 'Active', will appear in the bundle listings. You can always edit your bundle listing at a later date.


Finding Bundles

Bundles that have been shared with the Laravel community are listed in the Bundles directory at http://bundles.laravel.com.

You can browse bundles by category, or use the search feature to find the bundle you're looking for. Once you have found a bundle that meets your requirements, take a look at the 'Installation' tab of the bundle's profile to find the install keyword.


Installing a Bundle

Once you have the install keyword for a bundle, you can install it from the base of your project using the 'Artisan' command line interface, and it's bundle:install command. For example..

php artisan bundle:install bob

Artisan will consult the bundles API to retrieve the path to the bundles GitHub repository, and the repositories of all its dependencies. It will then download source packages directly from GitHub, and extract them to the /bundles directory for you.

You will need to manually add the bundle name to the array within application/bundles.php for the bundle to become enabled.

return array(
    'docs' => array('handles' => 'docs'),
    'bob'
);

In some situations, you might need to add extra information to this array entry to facilitate auto starting, or directing certain routes to the bundle. The author will have provided this extra information in the bundles description, if that is the case.


Thanks for reading and enjoy creating your own bundles with Laravel! If you'd like to learn more about Laravel, be sure to pick up my book!

Related Posts
  • Code
    PHP
    Building a Customer Management App Using AngularJS and LaravelLaravel 4 auth retina preview
    When creating a single-page app we should use some kind of framework to do some of the job for us, so we can focus on the actual functionality. AngularJS fits here perfectly, because features like dynamic dependency injection and bi-directional data binding are just great. Sometimes we also require some kind of server. If you've chosen PHP then Laravel may be your best option, as it's easy to work with and pretty powerful.Read More…
  • Code
    PHP
    Authentication With Laravel 4Laravel 4 auth retina preview
    Authentication is required for virtually any type of web application. In this tutorial, I'd like to show you how you can go about creating a small authentication application using Laravel 4. We'll start from the very beginning by creating our Laravel app using composer, creating the database, loading in the Twitter Bootstrap, creating a main layout, registering users, logging in and out, and protecting routes using filters. We've got a lot of code to cover, so let's get started!Read More…
  • Code
    PHP
    25 Laravel Tips and TricksCode
    There was a period of time, not too long ago, when PHP and its community were, for lack of better words, hated. Seemingly, the headline joke of every day was one that related to how terrible PHP was. Let's see, what new PHP-slamming blog article will be posted today?Read More…
  • Code
    PHP
    Laravel 4: A Start at a RESTful API (Updated)Laravel api 400
    RESTful API's are hard! There are a lot of aspects to designing and writing a successful one. For instance, some of the topics that you may find yourself handling include authentication, hypermedia/HATEOS, versioning, rate limits, and content negotiation. Rather than tackling all of these concepts, however, let's instead focus on the basics of REST. We'll make some JSON endpoints behind a basic authentication system, and learn a few Laravel 4 tricks in the process.Read More…
  • Code
    PHP
    Your One-Stop Guide to Laravel CommandsLaravel commands
    In this day and age, it's quite normal for a developer to have an understanding of consoles, and how to issue basic commands. But what if you could code your own custom commands to improve your workflow? If we look back to Laravel 3, you might remember that it offered tasks. Tasks were extremely helpful, but still came up short for more complex operations. Thankfully, Laravel 4 packs a beefed up Artisan that will make your life as a developer so much easier!Read More…
  • Code
    PHP
    Build Your First Admin Bundle for LaravelLaravel bundles
    It's hard to deny the fact that the PHP community is excited for Laravel 4. Among other things, the framework leverages the power of Composer, which means it's able to utilize any package or script from Packagist. In the meantime, Laravel offers "Bundles", which allow us to modularize code for use in future projects. The bundle directory is full of excellent scripts and packages that you can use in your applications. In this lesson, I'll show you how to build one from scratch!Read More…