No suggestions or feedback? Man, tough crowd. ;)
Removing Controller Boilerplate & Maintaining SRP
So, based on a separate conversation on whether the FormRequest class in Laravel 4.3 is mixing responsibilities too much, I wanted to suggest an alternative that removes the same boilerplate code from controllers without mixing responsibilities.
Every controller function called as part of a route is actually done through through the controller's callAction() function. This means that in our base controller class, we can override that function to provide some of the same boilerplate code in the FormRequest class, specifically:
- Redirecting back in response to a Validation Exception
- Displaying a 403 page in response to an Authorization Failure
This is an implementation very similar to what I'm currently using in production:
class BaseController extends Controller
{
public function callAction($method, $params)
{
$ajax = Request::isAjax();
try {
return parent::callAction($method, $params);
} catch(ValidationException $exception) {
return $this->handleValidationException($exception, $ajax);
} catch(AuthException $exception) {
return $this->handleAuthException($exception, $ajax);
}
}
protected function handleValidationException(ValidationException $exception, $ajax = false)
{
if($ajax) {
return Response::json(array('errors' => $exception->errors()), 422);
} else {
return Redirect::back()->withInput()->withErrors($exception->errors());
}
}
protected function handleAuthException(AuthException $exception, $ajax = false)
{
if(Request::isAjax()) {
return Response::json($exception->getMessage(), 403);
} else {
App::abort(403);
}
}
}
Now, your extending controllers only need to handle the success case, and validation and auth exceptiosn will automatically be taken care of by the base controller:
<?php
public class FooController extends BaseController
{
public function __construct(FooService $foos)
{
$this->foos = $foos;
}
public function store()
{
//throws ValidationException on failure
$foo = $this->foos->create(Input::all());
return Redirect::route('foos.show', $foo->id);
}
public function update($id)
{
//throws ValidationException on failure
//throws AuthException if the current user does not have access to a the relevant resource
$foo = $this->foos->findAndUpdate($id, Input::all());
return Redirect::route('foos.show', $foo->id);
}
}
Please or to participate in this conversation.