ro0nl/http-responder

A Symfony HTTP responder


Keywords
symfony, http, responder, adr, symfony-bundle, controller, domain-driven-design, php
License
MIT

Documentation

Symfony HTTP Responder

Build status Latest Stable Version

ADR implemented in a nutshell. A viable alternative for AbstractController, or most base controllers really.

use ro0NL\HttpResponder\Bridge\Twig\Template;
use ro0NL\HttpResponder\Responder;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;

class SomeHttpAction
{
    public function __invoke(Request $request, Responder $responder): Response
    {
        return $responder->respond(new Template('home.html.twig'));
    }
}

We call it Composition over Inheritance.

Installation

composer require ro0nl/http-responder

Enable the Symfony Bundle

return [
    // ...
    ro0NL\HttpResponder\Bundle\HttpResponderBundle::class => ['all' => true],
];

Using the framework, a default view listener invokes the responder out-of-the-box. You may update your controller actions as such:

use ro0NL\HttpResponder\Respond\Respond;

// ...

public function __invoke(Request $request): Respond
{
    return new Template('home.html.twig');
}

Create a Providing Responder

Provide custom respond types using the ProvidingResponder.

use ro0NL\HttpResponder\ProvidingResponder;
use ro0NL\HttpResponder\Respond\Respond;
use Symfony\Component\HttpFoundation\Response;

class MyRespond implements Respond
{
}

class MyProvidingResponder extends ProvidingResponder
{
    protected function getProviders(): iterable
    {
        yield MyRespond::class => function (MyRespond $respond): Response {
            return new Response('hello world');
        };
    }
}

Any type of ProvidingResponder service is automatically tagged with http_responder to be made available in the main responder.

In case your responder implements the Responder interface but serves as a provider it should be tagged manually.

Create a Decorating Responder

Add behaviors to the main responder using a decorator.

use ro0NL\HttpResponder\Responder;
use ro0NL\HttpResponder\Respond\Respond;
use Symfony\Component\HttpFoundation\Response;

class MyDecoratingResponder implements Responder
{
    private $responder;

    public function __construct(Responder $responder)
    {
        $this->responder = $responder;
    }

    public function respond(Respond $respond): Response
    {
        // provide a response and ignore/override default behaviors
        if ($respond instanceof SpecialRespond) {
            return new Response('special');
        }

        // provide the initial response with default behaviors
        $response = $this->responder->respond($respond);

        // apply some generic behavior

        if ($respond instanceof MyRespond) {
            // apply some specific behavior
        }

        return $response;
    }
}

The bundle's main service identifier is http_responder and is aliased to its corresponding interface.

Comparison Table

  AbstractController Responder
get() ✔️ (use DI)
has() ✔️ (use DI)
generateUrl() ✔️ ✔️
forward() ✔️ ✔️ (todo)
redirect() ✔️ ✔️
redirectToRoute() ✔️ ✔️
json() ✔️ ✔️ (todo Serializer support)
file() ✔️ ✔️
addFlash() ✔️ ✔️
isGranted() ✔️ (use Security service)
denyAccessUnlessGranted() ✔️ (use code / Security firewall)
renderView() ✔️ (use Twig service)
render() ✔️ ✔️
stream() ✔️ ✔️
createNotFoundException() ✔️ (use throw)
createAccessDeniedException() ✔️ (use throw)
createForm() ✔️ (use Form service)
createFormBuilder() ✔️ (use Form service)
getDoctrine() ✔️ (use Doctrine service)
getUser() ✔️ (use Security service)
isCsrfTokenValid() ✔️ (use Csrf service)
dispatchMessage() ✔️ (use Messenger service)
addLink() ✔️ ✔️

Contributing

See CONTRIBUTING.md