Implementing Custom Form Request with Lumen

florence
The Andela Way
Published in
5 min readMay 29, 2020

Form request is an elaborate way to perform more complex validation scenarios for your API. This becomes more important especially when you’re building your app to scale.

Form requests classes are custom request classes that contain validation logic.

Unfortunately, this is not supported in Lumen. The Lumen documentation explains why. Lumen instead provides a helper method $this->validate available within Routes closures and Controllers.

However, if you are building an API to scale, it may become important to have an elaborate and structured way to manage requests. How then can this be achieved with Lumen given this limitation? So, after much googling, there turns out to be a number of ways developers have tried in order to achieve this. There is a very good one I saw on medium that shows how to achieve this which is really good. The only problem I have with that is that it involves copying and pasting files (code as a matter of fact) from the vendor directory of a laravel app. You end up with tons of code you did not write! Not a huge fan 😶

Seeing that this approach is not one I want to take, how might I take advantage of what Lumen supports right out of the box yet implement a FormRequest like architecture for my app? Here’s how I went about it.

prerequisites:

Now open the app in your IDE/text editor, you should see a folder structure similar to this:

Because I want to build a Request architecture for requests to the API, I created a new directory within the Controllers directory named Requests.

For the sake of this example, I want to build a request class to handle certain request validations for Users. For example, a request validation class for creating a user or updating an existing user. So you can go ahead and create a User subdirectory within the Requests directory. Within this subdirectory, create a new class named StoreRequest.php

The purpose of these classes is to handle validation for the request being processed for this call simply using the helper that Lumen provides for validation. This approach also removes requests validation from the controller and passes it on to dedicated classes to handle thereby making your code decoupled, testable and maintainable.

namespace App\Http\Controllers\Requests\User;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class StoreRequest extends Controller
{
public function __construct(Request $request)
{
$this->validate(
$request, [
'name' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required|min:5'
]
);

parent::__construct($request);
}
}
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Requests\FormRequest;
use Laravel\Lumen\Routing\Controller as BaseController;

class Controller extends BaseController implements FormRequest
{
protected $service;
protected $params;
public $request;

public function __construct(Request $request)
{
$this->params = $request->all();
$this->request = $request;
}

/**
* Return the Request Object
*
*
@return \Illuminate\Http\Request
*/
public function getParams(): Request
{
return $this->request->replace($this->params);
}
}
namespace App\Http\Controllers\Requests;


interface FormRequest
{
public function getParams();
}

Usage:

Make a POST request to create a new user:

$router->post(‘users’, ‘UserController@store);

In the UserController.php class:

public function store(StoreRequest $request)
{
dd($request->getParams());
}

Explanation:

When a call is made to create a new user, the StoreRequest is executed first in the store method of the UserController. It is resolved from the service container and an instance of it is immediately available. Now, in the constructor of the StoreRequest class, the needed validation is executed. If any of the validation fails, a JSON response with the relevant error messages is returned. And upon success, the parent construct is called. The parent here is the Controller class available right out of the box upon a fresh installation of a lumen app. Now, let’s examine the small change I have made to this class. In the constructor, Illuminate/Http/Request is injected and the request parameters can be extracted. I have set my params property to the request parameters for further use in the service classes (I wont go into details with this, I have used service classes as a way to outsource the actual creation of resources to dedicated service classes). To drive the point home quickly, if the validation passes, then the requests parameters are extracted and set to a params property in the Controller which can further be retrieved through a public method called getParams() for further use in the app. For the sake of this tutorial we simply dd($request->getParams()).

Summary:

  • Avoid copying and pasting vendor code in an attempt to bring back FormRequest to Lumen.
  • Implement a custom FormRequest architecture that uses request validation helper provided out of the box by Lumen without reinventing the wheel.
  • Build a structure to handle form validations separate from the main controller thereby building decoupled code that is easy to test as a separate entity and maintainable.

Let’s write some tests:

<?php

namespace Integration\Requests\User;

use App\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Requests\FormRequest;
use App\Http\Controllers\Requests\User\StoreRequest;

class StoreRequestTest extends \TestCase
{

/**
*
@test
*/
public function it_is_an_instance_of_cookbook_form_request()
{
$request = new StoreRequest(new Request([
'name' => 'test',
'email' => 'test@mail.ca',
'password' => 'testpassword'
]));

$this->assertInstanceOf(FormRequest::class, $request);
}

/**
*
@test
*/
public function it_can_get_the_request_object()
{
$requestData = [
'name' => 'test X',
'email' => 'x@test.com',
'password' => '@34v_reT6543'
];

$storeRequest = new StoreRequest(new Request($requestData));

$this->assertInstanceOf(Request::class, $storeRequest->getParams());
$this->assertSame($requestData['name'], $storeRequest->getParams()->input('name'));
$this->assertSame($requestData['email'], $storeRequest->getParams()->input('email'));
$this->assertSame($requestData['password'], $storeRequest->getParams()->input('password'));
}

/**
*
@test
*/
public function it_throws_an_exception_if_the_request_is_empty()
{
$this->expectException(\Illuminate\Validation\ValidationException::class);

$storeRequest = new StoreRequest(new Request([]));
}
}

While this is not a comprehensive test, it gives you an idea of why this approach works.

Kindly drop a comment in the comments section.

UPDATE:
In order to catch failed validation error messages, you can catch that in the exceptions handler class like so:

public function render($request, Exception $e)
{
if ($e instanceof UnprocessibleEntityException) {
return response()->json([
'error' => $e->getMessage()
], $e->getCode());
}
}

--

--

florence
The Andela Way

#Mum #AndelaAlumni #Backend Engineer #Mentor Book a 15 minutes session with me free of charge https://calendly.com/okosunuzflorence/15min