oskarstark/symfony-http-responder
Lightweight Symfony bundle that streamlines building HTTP responses by wrapping common response patterns in a simple responder layer. Helps keep controllers thin and consistent when returning JSON, redirects, views, files, and other responses across your app.
Installation
composer require oskarstark/symfony-http-responder
(Note: Since this is a Symfony package, ensure Laravel’s symfony/http-foundation is installed.)
First Use Case: JSON Response
use OskarStark\SymfonyResponder\JsonResponder;
public function show(User $user)
{
return (new JsonResponder())->respond([
'data' => $user->toArray(),
]);
}
First Use Case: View Rendering
use OskarStark\SymfonyResponder\ViewResponder;
public function index()
{
return (new ViewResponder('users.index'))->with([
'users' => User::all(),
]);
}
First Use Case: File Download
use OskarStark\SymfonyResponder\FileResponder;
public function download()
{
return (new FileResponder(storage_path('app/report.pdf')))
->setContentDisposition('attachment; filename="report.pdf"');
}
Where to Look First
src/Responder for available responders.app()->make() or dependency injection for responders.Register responders in AppServiceProvider:
public function register()
{
$this->app->bind(JsonResponder::class, function () {
return new JsonResponder();
});
}
Then inject into controllers:
public function __construct(private JsonResponder $jsonResponder) {}
return (new JsonResponder())
->setData(['status' => 'success'])
->setStatusCode(200)
->respond();
Use responders in middleware to standardize responses:
public function handle($request, Closure $next)
{
$response = $next($request);
if ($response instanceof JsonResponder) {
$response->setHeader('X-Custom', 'Header');
}
return $response;
}
public function handleRequest()
{
if ($this->isApiRequest()) {
return (new JsonResponder())->respond($data);
}
return (new ViewResponder('view.name'))->with($data);
}
Extend responders to integrate with Laravel’s Response facade:
use Illuminate\Support\Facades\Response;
class LaravelJsonResponder extends JsonResponder
{
public function respond($data)
{
return Response::json($data, $this->statusCode);
}
}
Wrap responders in try-catch blocks:
try {
return (new JsonResponder())->respond($this->service->fetchData());
} catch (\Exception $e) {
return (new JsonResponder())
->setData(['error' => $e->getMessage()])
->setStatusCode(500)
->respond();
}
Response objects, not Laravel’s. To convert:
use Illuminate\Support\Facades\Response;
$symfonyResponse = (new JsonResponder())->respond($data);
return Response::make($symfonyResponse->getContent(), $symfonyResponse->getStatusCode(), $symfonyResponse->headers->all());
LaravelJsonResponder (as shown above) to avoid manual conversion.200. Always explicitly set it for non-success responses:
(new JsonResponder())->setStatusCode(404)->respond(['error' => 'Not found']);
ViewResponder uses Symfony’s Templating component, which may not auto-detect Laravel’s view paths. Specify the full path:
(new ViewResponder('resources/views/users.index'))->with($data);
ViewResponder to resolve Laravel views:
class LaravelViewResponder extends ViewResponder
{
public function __construct(string $view)
{
parent::__construct(view()->getPath().'/'.$view);
}
}
(new FileResponder($filePath))
->setCache([
'public' => true,
'max_age' => 3600,
's_maxage' => 3600,
]);
Storage facade to generate temporary URLs for private files.DateTime, resources) may not serialize correctly. Use json_encode with JSON_UNESCAPED_UNICODE or cast to arrays:
(new JsonResponder())->respond([
'date' => $user->created_at->toIso8601String(),
]);
$response = (new JsonResponder())->respond($data);
dd($response->headers->all());
class V2JsonResponder extends JsonResponder
{
public function respond($data)
{
$data['api_version'] = '2.0';
return parent::respond($data);
}
}
$responder = $this->createMock(JsonResponder::class);
$responder->method('respond')->willReturn(new SymfonyResponse());
Response vs. Laravel Response differences.How can I help you explore Laravel packages today?