Lumen API 04: List of Items

Yanuar Arifin
5 min readOct 17, 2019
Photo by Jakob Owens on Unsplash

Previously we have create a route to show one article using Resource class, in this one, we will reuse the Resource class to build our list of items. The route that we will build is like the index page in a CRUD function. In website settings, index page usually lists a kind same type of information, take an example of Instagram, if you open instagram home page, it will list posts of those you follow, or amazon, where it will list a number of items it sell, with pagination links below the item lists. What we will build is identical to that listing, but instead of returning page, we will return JSON data that could be used by front end, or maybe an ad banner on some website.

Let’s go.

What Condition This Route Will Work On?

Before we write the code, let us first make cases on how this route will work. So we could be certain that the function is working according to our specification.

  1. Normal Condition; there are items that could be listed so it returns the items
  2. The response format is correct.
  3. No Item; there are no item to be listed, so the data attribute is an empty array
  4. Client access out of bound page, will return 404 not found.

Create The Tests

Next step is to create the test functions to confirm the behavior. Add this Test to ItemFeatureTest.php

/**
* if there are items, it will return this correct format
*
* @return void
*/
public function testShowManyHasCorrectFormat()
{
factory(App\Item::class, 5)->create();
$jsonStructure = [
"data",
"links" => [
"first",
"prev",
"next",
"last",
],
"meta" => [
"current_page",
"from",
"last_page",
"path",
"per_page",
"to",
"total"
]
];
$this->get('/items')
->seeJsonStructure($jsonStructure);
}
/**
* if there are items, the data attribute will not empty
* because there are list of item there
*
* @return void
*/
public function testShowManyDataAttributeIsNotEmpty()
{
factory(App\Item::class, 5)->create();
$this->get("/items"); $response = $this->response->content(); $response = json_decode($response, true); $this->assertNotEmpty($response['data']);
}
/**
* get showAll when there's no data, the data array is empty
* but other attributes is there
*
* @return void
*/
public function testShowManyWithoutResultReturnsEmptyData()
{
$this->get('/items');
$response = $this->response->getContent(); $response = json_decode($response, true); $this->assertEmpty($response['data']);
}
/**
* accessing GET items with page number exceeding last page
* returns 404
*
* @return void
*/
public function testOutOfBoundPageReturn404()
{
factory(App\Item::class, 5)->create();
$this->get("/items/?page=5")
->assertResponseStatus(404);
}

Let’s test it to see the failures ;)

Test Result Before Code

Well, what do we expect, it still fail. Next we will work it out to make the tests pass.

Resource Class for Listing Collection

Previously when we create response to show one item, we use Resource class to do that, which make the code more manageable and more readable. Now, we will use combine that resource class and resource class for collection to make the response. Begin By Creating ItemCollection.php next to item resource class, and fill it with this code

<?phpnamespace App\Http\Resources;use Illuminate\Http\Resources\Json\ResourceCollection;class ItemCollection extends ResourceCollection
{
/**
* Turns Model to json response
*
* @return void
*/
public function toArray($request)
{
return $this->collection;
}
}
?>

So simple right? //explain why this works

Re-Route The Item Page

Next step is to reroute the item.local/items to ItemController.php, and we will make response logic there. Edit the $router->get('items/'...on route/web.php

$router->get('/items', ['as' => 'item.showAll', 'uses' => 'ItemController@showAll']);

This will tell Lumen to look at showAll() function in ItemController.php to process the request and get response.

Make The Controller Logic

In the ItemController create showAll() function and return ItemCollection parameterized with item collection.

/**
* show lists of items
*
* @return JSON
*/
public function showAll()
{
return new ItemCollection(Item::paginate());
}

This function basically query all the item record which returns collection, then this collection is fed to ItemCollection to populate & format the output. This is what you will see when you make a request

Inside data part of response
Result With Folded Data

The screenshot above is the format that ResourceCollection have when you return it with some data, it has links and meta attributes instead of only data attribute, and the data attribute is formatted in array instead of object like when we create the route to show one item.

The function of links and meta attributes is to provide API consumer to navigate and get more information about the response that the API gave. links attribute gave the consumer a way to make pagination or next request that it needs to make. The meta contains the information about pagination and item records status. Maybe it will not be shown when you make a front end, but surely those information is needed by developer who consume the API

Okay, let’s run our test again

You could see that we had 1 test failed, the testOutOfBoundPageReturns404() . Let’s celebrate that we have made more than half of the test passed, no need to be down because of those failed test, we will fix it.

What Goes Wrong?

Turns out that the ResourceCollection still returns HTTP code 200 with empty data, same as empty data. So let’s fix our test instead of our code. Change testOutOfBoundPageReturns404() from assertResponseStatus(404) to 200 and make sure the data part is empty which means you write one more test with same function as when you visit out of bound page. Let’s rename the testOutOfBoundPageReturns404() to testOutOfBoundPageReturnsEmptyData()

    /**
* accessing GET items with page number exceeding last page
* returns empty data
*
* @return void
*/
public function testOutOfBoundPageReturnEmptyData()
{
factory(App\Item::class, 5)->create();
$this->get("/items/?page=5"); $response = $this->response->getContent();
$response = json_decode($response, true);
$this->assertEmpty($response['data']);
}

Now if we test this wit vendor/bin/phpunit --testdox

Result of index tests

Alright, We have passed on all tests now.

Conclusion

In this quite short article, we have created a route to show list of items using eloquent and resource collection class. We took the advantage of resource class being the class that is specialized for API development. There are other alternative that you could refer to such as fractal, as in my experience Resource class still has its own limitation on helping us create API responses.

Next, we will create a route to add new record to our API

--

--