--- title: "Improvements of the new Responsable interface in Laravel" image: /images/responsable.png thumbnail: /images/thumbnail-responsable.png description: "Laravel 5.5 will ship with a new feature: the Responsable interface. Why not add some sugar to it?" lang: en website: dev --- The new `Responsable` interface in Laravel is really awesome, it allows us to simplify our controllers with custom responses objects. But one of the complexity with responses is the content negotiation and [the new feature](https://github.com/laravel/framework/commit/c0c89fd73cebf9ed56e6c5e69ad35106df03d9db) doesn't help us on this. How could we solve this problem? --- ### The new feature First, let's look at how Laravel uses the `Responsable` interface in the Router. ```php if ($response instanceof Responsable) { $response = $response->toResponse(); } if ($response instanceof PsrResponseInterface) { $response = (new HttpFoundationFactory)->createResponse($response); } elseif (! $response instanceof SymfonyResponse && ($response instanceof Arrayable || $response instanceof Jsonable || $response instanceof ArrayObject || $response instanceof JsonSerializable || is_array($response))) { $response = new JsonResponse($response); } elseif (! $response instanceof SymfonyResponse) { $response = new Response($response); } ``` The only method on the `Respondable` interface is the `toResponse` method. Then, if the response is `Jsonable` or something similar, Laravel will use the `JsonResponse` which `json_encode` all the data. ### Adding Content Negotiation There is no sign of Content-Negotiation in this method. So if I need to build a response in JSON or in HTML depending of the type the request, I will have some conditions in my controller: ```php public function index() { $users = User::all(); if (request()->wantsJson()) { return new UsersJsonResponse($users); } return new UsersHtmlResponse($users); } ``` Not too bad, thanks to the awesome `wantsJson()` method, but it could be better (especially if my controller is not a simple database query): ```php public function index() { $users = User::all(); return UsersResponse($users); } ``` Better! Now let's dive in the `UsersResponse`: ```php class UsersResponse implements Responsable { public $users; public function __construct($users) { $this->users = $users; } public function toResponse() { if (request()->wantsJson()) { return $this->users; } return view('users.index', ['users' => $this->users]); } } ``` ### Avoiding conditionals with polymorphism It's often a good thing to replace conditionals by polymorphism. I would rather prefer this response with a new `NegociateContent` trait: ```php class UsersResponse implements Responsable { use NegociateContent; public $users; public function __construct($users) { $this->users = $users; } public function toJsonResponse() { return $this->users; } public function toHtmlResponse() { return view('users.index', ['users' => $this->users]); } } ``` Or this one which extends a new `BaseResponse`: ```php class UsersResponse extends BaseResponse { public $users; public function __construct($users) { $this->users = $users; } public function toJsonResponse() { return $this->users; } public function toHtmlResponse() { return view('users.index', ['users' => $this->users]); } } ``` The `NegociateContent` or the `BaseResponse` implementations are really straightforward. We can add more checks and content type if needed of course. ```php class BaseResponse implements Respondable { public function toResponse() { if (request()->wantsJson()) { return $this->toJsonResponse(); } return $this->toHtmlResponse(); } } ``` ### Let's try to build the same `view` helper as in mailables We can even simplify the `toHtmlResponse` method with the same concept as in `Mailable`. As you may know, in mailables, all public properties are directly accessible in the mail view. ```php public $users; public function toHtmlResponse() { return $this->view('users.index'); } ``` I stole this code from the [`Illuminate\Mail\Mailable`](https://github.com/laravel/framework/blob/master/src/Illuminate/Mail/Mailable.php#L223) class in the framework (I just refactored it to collections). ```php public function view($view, $overrides = []) { return collect((new ReflectionClass($this))->getProperties(ReflectionProperty::IS_PUBLIC)) ->reject(function($property) { return $property->getDeclaringClass()->getName() == self::class; }) ->mapWithKeys(function($property) { return [$property->getName(), $property->getValue($this)]; }) ->merge($overrides) ->pipe(function($data) use($view) { return view($view, $data); }); } ``` ### Conclusion I think these few lines of code really improve the `Responsable` interface. I really like the `Mailable` reflection as it feels really natural to me. I may try to pull request the framework to add these features if people like them so don't hesitate to send me a message [on Twitter \@ThibaudDauce](https://twitter.com/ThibaudDauce) or [by mail (thibaud@dauce.fr)](mailto:thibaud@dauce.fr) to tell me what you think!