Creating a Lumen Package

Lumen packages are a great way to incorporate some pre-made functionality into your app. They work just like Laravel packages, but when creating one for Lumen, one needs to change a couple things to make it work.

Luckily it's quite simple to update a package to be used between Laravel and Lumen. Here we'll create a Lumen package from scratch. The package will have a very simple service to request posts from some Instagram tag.

If you're unfamiliar with connecting applications to Instagram, or with Lumen/Laravel in general, you might want to get up to par on that before following this tutorial.

Structure

First let's setup the folder structure and setup a namespace for our package. Lumen follows the the PSR4 standard, and we shall comply. A classic path name structure is the <owner>/<package-name> structure. In this case i'm going to set it up as cloudoki/instagrab.

Lumen doesn't ship with a /packages folder, so let's create that first, and then the package's folders. On your api root folder do:

$ mkdir packages
$ cd packages
$ mkdir cloudoki
$ cd cloudoki
$ mkdir instagrab

Create a package.json

Now we'll need to explain what our package is and allow it to be available for download to the public, as well as define any dependencies the package might have.
Do a:

$ composer init

And fill in all the necessary information, mine looks like this:

{
    "name": "cloudoki/instagrab",
    "description": "A package to get instagram images.",
    "license": "MIT",
    "authors": [
        {
            "name": "Rui Molefas",
            "email": "rui@cloudoki.com"
        }
    ],
    "minimum-stability": "dev",
    "require": {}
}

Now, we'll need to let Lumen autoload our package. For that, open the composer.json file on the root of your Lumen app. You'll notice a psr4 section on it, with just:

"psr-4": {
    "App\\": "app/"
}

Add your package to it, like so:

"psr-4": {
    "App\\": "app/",
    "Cloudoki\\InstaGrab\\": "packages/cloudoki/instagrab"
}

And update the autoload with:

$ composer dumpautoload

Thus, generating a new namespace for your package.

Create a Service Provider

Laravel's service providers are what's gluing your app together with anything you might need, from middleware to events or any other dependencies your app might need. This app will be very low on dependencies so we are keeping the Service Provider very thin. For starter's it will be empty, I will fill it later.

In a Laravel install one could create the Service Provider through:

php artisan make:provider InstaGrabServiceProvider  

but Lumen's super slim artisan doesn't come with a make:provider command. No worries, we'll just create the file inside the package folder. Create a InstaGrabServiceProvider.php file with:

<?php 

namespace Cloudoki\InstaGrab;

use Illuminate\Support\ServiceProvider;

class InstaGrabServiceProvider extends ServiceProvider {

   /**
    * Bootstrap the application services.
    *
    * @return void
    */
   public function boot()
   {
      //
   }

   /**
    * Register the application services.
    *
    * @return void
    */
   public function register()
   {
      //
  }

}

Notice the namespace usage on top. For now, our Service Provider will stay like this.

Create a Controller

Our package's Controller is where all the magic will happen. Create a InstaGrabController.php file inside your package's folder, right next to your Service Provider, with:

<?php 

namespace Cloudoki\InstaGrab;

use App\Http\Controllers\Controller;

class InstaGrabController extends Controller {

  public function __construct() {
    //
  }

  /**
  * Requests Instagram pics.
  *
  * @return Response
  */
  public static function grabInstagram()
  { 
    $client_id  = env ('INSTAGRAM_CLIENTID');
    $tag        = env ('GLOBAL_TAG');

    $params     = array('client_id='.$client_id);
    $url        = "https://api.instagram.com/v1/tags/".$tag."/media/recent?";

    $result     = json_decode(file_get_contents($url. implode('&', $params)));
    $data       = $result->data;

    return $data;
  }
}

This Controller has a function grabInstagram() that will, in a nutshell, get all the recent posts of a given tag. Notice the env() statement. This is the way to get the values from environment variables defined in the .env file. Since you don't have those defined yet, just add to the .env:

## grabInstagram
GLOBAL_TAG = 'tomorrowland';  
INSTAGRAM_CLIENTID = 'YOUR-APP-KEY';  

As you can see I used the tomorrowland tag for this app (it could have been anything else). Of corse you will need an Instagram App and APP KEY to access their API. If you don't know how to get one, check this tutorial out.

Hooking up an APP Controller with the package

Now let's put the package to the test. First let's create a controller on app/Http/Controllers called ApiController, just a generic name, with:

<?php

namespace App\Http\Controllers;

# Package
use Cloudoki\InstaGrab\InstaGrabController;  
use Laravel\Lumen\Routing\Controller as BaseController;

class ApiController extends BaseController  
{
    /**
     *    Get Instagram from InstaGrab
     */
    public function get ()
    {   
        # get
        return InstaGrabController::grabInstagram();
    }
}

Notice that we had to include InstaGrabController.
Create a route on app/Http/routes.php:

$app->get('/', 'ApiController@get');

And it should be done. Before trying, do a composer dumpautoload and then just check your root URL (on the browser) and you should hopefully see a pretty ugly (still counts as working) array with the latest Instagram posts for the given tag.

Create package routes

Although we have successfully integrated a package controller with our App's controller, our package could use it's own routers, meaning, a way to access the package's functionality without having to run it through another controller. This is not necessary per se, but can be useful for some packages. To do it, let's update the Service Provider to register our package in Lumen.
Create a routes.php file inside the package root folder, next to the Controller and the Service Provider:

<?php

$this->app->get('grab', 'cloudoki\instagrab\InstaGrabController@grabInstagram');

Then, update the Service Provider to include the package's routes and to tell the Ioc container all about the package's Controller:

/**
    * Register the application services.
    *
    * @return void
    */
   public function register()
   {
      //Register Our Package routes
      include __DIR__.'/routes.php';

      // Let Laravel Ioc Container know about our Controller
      $this->app->make('Cloudoki\InstaGrab\InstaGrabController'); 
  }

Update the bootstrap/app.php file adding the Service Provider to it. Find the place where to register Service Providers and add:

$app->register(Cloudoki\InstaGrab\InstaGrabServiceProvider::class);

Finally, run composer dumpautoload again, browse the URL http://yourvhost/grab in your browser, and you should see the Instagram post lists without having to go through any App Controller.

comments powered by Disqus