Building a Laravel 4 RESTful API - Part 2
In my first article in this series: “Is your REST API at REST?” we discussed the very basic structure of our REST API. We covered some important foundation concepts, and structure. Hopefully you have played around with this a bit since the last article, and have a pretty good feeling for how this works.
Now, in the first article we covered our URI end points, and how Laravel’s Resource Controller’s manage that for us, including how to register the routes for those end points. We created an Eloquent model for our snacks table, and returned a response using json.
In this article I’ll cover the following:
- Using Laravel’s response model to wrap your responses in http headers with the correct codes.
- Creating a Code library in Laravel
Proper Responses:
In our first article we simply returned the results by doing
Return json_encode(Snacks::all());
Now this works, is fairly simple, and fits on one line. So that’s great. But what if we needed to return an error message, or a not found message, or permission denied message? Well, sending back a 200 ok response in these situations isn’t really what we want to do in a REST API.
The power of REST really comes from HTTP. Using HTTP as our transport protocol, and using the functionality already built into HTTP allows us some flexibility, and also a standard to adhere to, which in turns makes our API somewhat predictable.
If we return a response and wrap that in a 200 OK header, then the receiving end will assume the request was successful. Well, that may not be the case in our example from the first article; in fact it’s entirely likely it won’t be the case. So to solve that we need a way to send a formatted header back with the correct HTTP code to identify what kind of response we’re returning.
A quick overview of a few HTTP codes, and there meanings are below, however for a full understanding reference the w3.org documentation: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
- 200 OK – Request has succeeded. The information returned with the response is dependent on the method used in the request.
- 201 Created – The request has been fulfilled and resulted in a new resource being created.
- 202 Accepted – The request has been accepted for processing, but the processing has not been completed.
- 3xx Redirection – The service has been moved, and you’re being redirected. This is typically going to require the user agent to take further action to fulfill the request. There are a specific 300 codes for specific redirect issues. See docs
- 400 Bad Request – Request not understood due to malformed syntax
- 401 Unauthorized – The request requires authentication
Laravel has a handy library called Response. This library will automatically format your response with the proper HTTP code in the header response. You can also define other header information, such as cache-control responses.
Here is an example of a response:
//store results from DB into $data, should be an array of objects if there is a response
$data = Snacks::all();
//Check if result is an array, and also check to make sure the first object in the array
//is an object from the DB with an actual record.
if(is_array($data) && isset($data[0]->id)){
$response = Response::make($data, 200);
}else {
$response = Response::make($data, 404);
}
//Set content-type in header
$response->header('Content-Type', 'application/json');
//set cache-control to one hour in header
$response->header('Cache-Control', 'max-age=3600');
//return the response to the user agent
return $response;
So the first thing we do is use the Eloquent Model we created in the first article to retrieve all the records for Snacks. Now, we should be getting an array of objects, where the objects are the individual records. If there is an error or there are no results Laravel will still return an array but it won’t have the actual data we want so just checking to make sure an array was returned isn’t enough, we also need to make sure it’s an array of record objects and to do that we check to make sure it has an ->id
value on it. If the record is an object with ->id
on it then it’s from our DB and we can wrap the response in a HTTP 200 OK header response. If it's either not an object or doesn't have the ->id
, we should return a 404 no records found response.
Before we return the response, I also want to make sure the response is in JSON format, so we’re going to add a ‘Content-Type: application/json’ header value, and also because I don’t expect this data to change very quickly, we’re going to throw an hour long cache-control on the response. If the same user-agent makes the same request a bunch of times we can instruct them to use there local cache or a caching service if one is available. Cache Control is an entire series on it’s own, but this is a bare bones approach that is the minimum you should be doing.
Code Library:
Laravel is commonly referred to as an MVC (Model, View, Controller) framework, but it’s important to realize that MVC is a very vague guideline. It’s also a guideline that Laravel doesn’t strictly try to confine you too. In fact, the idea of a Model in Laravel is very different then other MVC languages. When we use Models in Laravel we’re typically using Eloquent Models which gives us an abstraction layer for interacting with our DB. Because Laravel 4 is built around the design principles of a true object oriented programming language they leverage a lot of powerful design patterns, including the name spacing features found in PHP 5.3.2 +. This means that we can use name spacing to organize our code. Which free's us from having to put all of our Eloquent Models in the /app/models directory and our controllers in the /app/controller directory.
To move our code around in Laravel we have to do a few things in order.
- We have to create the new directory structure
- We have to make sure we give our classes in the new directory structure the correct namespace
- We have to make sure we register the new directory structure with laravel and composer
- We have to make sure composer updates it’s autoload list for laravel, otherwise the classes won’t load, and you will end up with a class not found error
- We need to reference the class using the ‘use namespace/ClassName’.
Creating the directory structure, can be what ever you want as long as it’s under the /app folder.
If you want to put code in the /vendor directory you should read up on how to build packages in Laravel 4. Packages have different requirements and they go in the vendor directory.
-
For my API I create the following directory structure
/app /lib /API /Resource /v1 /Entity Snacks.php ResourceAPI.php ResourceAPIInterface.php
-
The namespace for ResourceAPI.php would be
namespace API/Resource/v1/;
right below the<?php
statement.
The class name would be:ResourceAPI
-
Then in
/app/start/global.php
I addapp_path().'/lib',
to the end of the addDirectories array.We also need to add
"app/lib"
to the end of theautoload: { classmap: [] }
array incomposer.json
at the root of your laravel project. -
Before Laravel will actually see the new classes you have to do a few things. In your terminal, at the root of your project, type:
composer dump-autoload
Next, and this is optional, but I recommend typing t he following:
php artisan dump-autoload
The first command, tells composer to rebuild it’s autoloader list which Laravel uses to lazy load classes. In other words the class won’t be loaded until you actually need it which is useful in terms of keeping our programs as lightweight as possible.
The second command, tells Laravel to do something similar, but also optimizes it’s autoload list. The second one isn’t necessary, but may improve performance.
And to reference that class in another class, we would do the below above that classes’s Class declaration:
use API/Resource/v1/ResourceAPI;
Once we do the above in another class we can simply type ResourceAPI
and have access to that class’s code.
Now you can put your code in this structure, or any structure as long as you follow the same patterns, and keep your site organized. In the next article when we talk about version control with Inversion of Control Containers, and Dependency Injection, we’ll see how this structure, especially with Interfaces will give us a lot of flexibility in creating versions of our API.