An alternative application workflow in Laravel 4: Part 2

10 Aug 2013

Laravel 4 provides a huge number of features, not the least of which is providing you the freedom to work however suits you best. In my previous article, An alternative application workflow in Laravel 4, we looked at a different approach for medium-large applications. This can also apply to small applications if you really like to segregate your work, but its value isn’t as evident, nor is it as necessary.

In this article, we’re going to look at this approach in more depth (by popular demand) and do the following:

  • Go through each step in detail, using our knowledge of PHP and Laravel’s Artisan command line tool
  • Setup 2 packages that will talk to one another (I’m going to avoid the admin package in the previous article as it won’t really add any value to this article)
  • And finally, I’ll give you a complete codebase to look at as a demonstrable package on my github account!

So let’s get on with it, but first a recap.

The data package

If you remember from my last article, the data package was basically there to setup our models and repositories and be used as the glue between the other packages for data requests. This could be Eloquent models, 3rd party service requests (such as API consumption), or even filesystem handling. Basically - anything related to data (though this could certainly be broken down further if required). It would also include the migrations the application(s) would need.

The API package

Our API package is going to handle data requests from clients. That is, it will provide access points (routes) to other API consumers. Perhaps it might be our admin package, or maybe someone has written an iPhone app and wants some data from your service. In any case, the access required and data provided will be the same. In this example, Api package will therefore be rather light-weight. In this article, we’ll look at providing two resources:

  1. Posts and
  2. Categories

Our API package will also follow RESTful conventions.

Now that we’ve covered that, let’s get started on actually putting our application together!

Setting up

First and foremost, we need a new copy of Laravel 4 in a new project directory - so go ahead and do that. For more information, check out the Laravel installation guide. Once it’s setup, we’re going to create two new packages:

  1. Data
  2. Api

To accomplish this, I’ll use the workbench tools provided by artisan:

1 php artisan workbench KirkBushell/Data --resources
2 php artisan workbench KirkBushell/Api --resources

This creates two new packages within our workbench folder: Data and Api. And both of these are housed within the vendor’s folder. In my case, that’s KirkBushell. Because our Api will depend on the Data package for data access, we’ll start with writing the code for that package first.

Writing the code

It’s important that we remember when we’re working in this fashion why we’re doing it. A big push in PHP lately, especially within the Laravel world - is a proper separation of concerns. This means writing code and packages that are really only concerned with that code or package’s particular role. In the case of our Data package, that means managing migrations, defining models and repositories, and anything else that may refer to common data requests. For our Api package, that means anything referring to api-based requests, such as controllers, our routing for the api, and probably authentication as well.

I want to highlight here also that I will not be going through -all- the code necessary for a full working solution. That’s a lot of verbosity for very little benefit. So, we’re going to touch on the necessary requirements to link it all up and nothing more.

Home is where the data is

In order to get our Data package rolling, we’re going to setup two domains of business logic:

  1. Models and
  2. Repositories

Models we will be using to define how our schema looks and relates to other models and our repositories will be used as the conduit through which data requests will be made. Let’s start by setting up our Post and Category models, which will reside within the Models directory of our Data package’s source. For me, this meant my model code resided at: workbench/kirk-bushell/data/src/KirkBushell/Data/Models:

1 // Post.php
2 namespace KirkBushell\Data\Models;
3 
4 class Post extends \Eloquent {
5 	public function category() {
6 		return $this->belongsTo( '\KirkBushell\Data\Category' );
7 	}
8 }

and for our Category:

1 // Category.php
2 namespace KirkBushell\Data\Models;
3 
4 class Category extends \Eloquent {
5 	public function posts() {
6 		return $this->hasMany( '\KirkBushell\Data\Post' );
7 	}
8 }

Now that we have two models that we can work with, it’s time to setup our repositories - which as we discussed earlier - will allow other parts of our application to query for data. Let’s start with our posts repository. For conciseness, I’m only going to define the all() methods for both repositories. This article is to help define the overall application structure, not individual methods.

Let’s get these repositories underway, by adding the two files below to our Data\Repositories folder.

 1 // PostRepository.php
 2 namespace KirkBushell\Data\Repositories;
 3 
 4 use KirkBushell\Data\Models\Post;
 5 
 6 class PostRepository {
 7 	public function all() {
 8 		return Post::all();
 9 	}
10 }

And now for our CategoryRepository…

 1 // CategoryRepository.php
 2 namespace KirkBushell\Data\Repositories;
 3 
 4 use KirkBushell\Data\Models\Category;
 5 
 6 class CategoryRepository {
 7 	public function all() {
 8 		return Category::with( 'posts' )->get();
 9 	}
10 }

For a very basic system, we now have the relevant code requirements for our Data package and we can start working with this with other packages. I’ll leave out migrations for now as I do not think that it is particularly important for this article. However, I will state that you can easily add migrations to your data package (I would) for all data schema requirements that you want shared between packages/projects.

Building your Laravel 4 API

Let’s quickly recap what our Api package is for. Considering our Data package is purely for data access and control, then we need some means of being able to get to that data. This can be achieved by setting up routes, which will take incoming requests and point it to the correct controllers, whom will instantiate and work with our repositories for required data.

Routes

Before we can actually talk to the controllers, we need to tell our application what routes will be available for those controllers. We’re only going to setup 1 route per controller in this example - a route that in both circumstances will fetch all records. Let’s do that now. My routes file resides at api/src/KirkBushell/Api/routes.php, but you can put it wherever you want within the Api package.

1 Route::get( 'posts', [ 'uses' => 'PostsController@getIndex' ] );
2 Route::get( 'categories', [ 'uses' => 'CategoriesController@getIndex' ] );

Pretty straight-forward, right? There’s one problem. This file never gets loaded. To resolve that, let’s update our boot() method within our ApiServiceProvider.

1 public function boot()
2 {
3 	$this->package('kirk-bushell/api');
4 
5 	require 'routes.php';
6 }

What this does, is that when our package is loaded - it will also include the routes.php file, which tells Laravel what routes we’re going to support.

At the moment, our routes are very straight-forward. There’s no special routing rules for subdomains or subfolders - but that is pretty easy to set it up, should we need it.

Now that we have our routes setup and configured correctly - let’s move onto the controllers.

Api controllers

Our controllers act as the customer service desk for our data. Any request for data in our application, or any other service - goes through the request/response lifecycle, which makes a stop at the controller phase. In this example, we’re going to create two controllers - one for posts, the other for categories, which will simply ask the data repositories for the relevant data. Easy enough I think - let’s get it done. We’re going to set these controllers up in our api package, but not within the KirkBushell/Api location. We’re going to set it up directly under KirkBushell, so that our package structure is very similar to our app/ folder.

So, make sure you create a controllers folder under src and add the relevant path to your composer.json file within the autoload -> classmap property of the json object. In short, it should look like this:

1 "autoload": {
2     "classmap": [
3         "src/controllers",
4         "src/migrations"
5     ],
6     "psr-0": {
7         "KirkBushell\\Api": "src/"
8     }
9 },

This just tells composer and laravel that controllers should get automatically loaded as part of the classmap configuration we have for the package. Now, let’s add our Posts controller!

 1 use KirkBushell\Data\Repositories\PostRepository;
 2 
 3 class PostsController {
 4 	private $repository;
 5 
 6 	public function __construct( PostRepository $repository ) {
 7 		$this->repository = $repository;
 8 	}
 9 
10 	public function getIndex() {
11 		return $this->repository->all();
12 	}
13 }

Our PostsController does two things so far - it defines a constructor that tells anything instantiating it that it requires a $repository object that must be a PostRepository (we’ll define this rule in a bit). It’s also including the Data package’s PostRepository class to help define this dependency. It will be this object (as you can see in the getIndex method) that we will use to query for data. For conciseness, I won’t go over the categories controller here, but if you’re interested you can see it in the Alternative Laravel 4 application workflow repository I have setup on my github account.

There’s one piece missing (again). Currently, Laravel 4 has no way of knowing what PostRepository is. We’ll need to define that. And we’ll do that when our data package starts up. Remember the boot() method we updated in our Api package to include the routes? Well, we’re going to do something similar. Let’s go back to our Data package and tell Laravel what this class is and where it can find it. Open up your DataServiceProvider in the data package, and make the following change to the boot() method:

1 public function boot()
2 {
3 	$this->package('kirk-bushell/data');
4 
5 	$this->app->singleton( 'PostRepository', function() {
6 		return new KirkBushell\Data\Repositories\PostRepository;
7 	});
8 }s

Seen this before? If not, what we’re doing is telling Laravel 4, via our Data package that whenever the PostRepository class is called, we want it to use the KirkBushell\Data\Repositories\PostRepository class. Also, because we’ve defined it as a singleton - it will only ever do this once.

So there you have it - we’ve setup two separate packages who maintain their own sets of code to deal with certain requirements.

I want to briefly mention that at this point I have skipped a lot of other code and files we could be doing to tidy this up, such as implementing Repository interfaces, extra files for managing dependencies (such as App bindings), and just generally good practices. However, I felt that this may complicate the process and concept I want to communicate and so have removed those concepts. If you’d like to read more on what I’ve just mentioned, you can watch the following video from Jeffrey Way, which was his talk at the first Laracon:

In addition, Taylor Otwell’s book, From apprentice to artisan is a fantastic resource for learning some really good practices in Laravel 4 and programming in general.

Update: Updated app singleton call as per kapil’s suggestion

Tagged: php, laravel, packages
comments powered by Disqus