/symfony-http-responder

Hey Responder 👋, please respond to me with a Symfony response

Primary LanguagePHPMIT LicenseMIT

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],
];

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