Skip to content

Instantly share code, notes, and snippets.

@cierzniak
Created June 22, 2023 11:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cierzniak/5ce0449980d0212747dab3d4b134326a to your computer and use it in GitHub Desktop.
Save cierzniak/5ce0449980d0212747dab3d4b134326a to your computer and use it in GitHub Desktop.
Symfony custom Argument Resolver
<?php
declare(strict_types=1);
namespace App\Http\Event;
use App\Http\Exception\JsonMalformedException;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
#[AsEventListener(event: KernelEvents::REQUEST, priority: 100)]
final class JsonTransformerEvent
{
public function __invoke(RequestEvent $event): void
{
$request = $event->getRequest();
if ('json' !== $request->getContentTypeFormat() || '' === $request->getContent()) {
return;
}
$request->request->replace(self::transformJsonBody($request));
}
/** @return array<array-key, mixed> */
private static function transformJsonBody(Request $request): array
{
try {
// TODO Use Symfony Serializer component
$data = json_decode($request->getContent(), true, flags: JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new JsonMalformedException($e);
}
if (null === $data || false === is_array($data)) {
return [];
}
return $data;
}
}
<?php
namespace App\Http\Request;
use Symfony\Component\HttpFoundation\Request;
interface RequestInterface
{
public static function fromRequest(Request $request): self;
}
<?php
declare(strict_types=1);
namespace App\Http\ArgumentResolver;
use App\Http\Exception\ModelValidationException;
use App\Http\Exception\RequestResolverException;
use App\Http\Request\RequestInterface;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\Validator\Validator\ValidatorInterface;
#[AutoconfigureTag(name: 'controller.argument_value_resolver', attributes: ['priority' => 150])]
final class RequestResolver implements ValueResolverInterface
{
public function __construct(
private readonly ValidatorInterface $validator,
) {}
public function resolve(Request $request, ArgumentMetadata $argument): array
{
/** @var RequestInterface $class */
$class = $argument->getType();
if (false === is_subclass_of($class, RequestInterface::class)) {
return [];
}
try {
$model = $class::fromRequest($request);
} catch (\TypeError $e) {
throw new RequestResolverException($e);
}
$this->validate($model);
return [$model];
}
private function validate(RequestInterface $model): void
{
$violations = $this->validator->validate($model);
if ($violations->count()) {
throw new ModelValidationException($violations);
}
}
}
<?php
declare(strict_types=1);
namespace App\Http\Action;
use App\Http\Request\SlugRequest;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Annotation\Route;
#[AsController, Route('/news/{slug}', methods: Request::METHOD_GET)]
final class SampleControllerAction
{
public function __invoke(SlugRequest $slugRequest): JsonResponse
{
// TODO Do whatever you want. You'r slug is available as getter
$data = $this->repository->findBySlug($slugRequest->getSlug());
return new JsonResponse(['data' => $data], 200);
}
}
<?php
declare(strict_types=1);
namespace App\Http\Request;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Constraints as Assert;
final class SlugRequest implements RequestInterface
{
public function __construct(
#[Assert\NotBlank]
private readonly string $slug,
) {}
public static function fromRequest(Request $request): RequestInterface
{
return new self($request->attributes->get('slug', ''));
}
public function getSlug(): string
{
return $this->slug;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment