Created
June 22, 2023 11:57
-
-
Save cierzniak/5ce0449980d0212747dab3d4b134326a to your computer and use it in GitHub Desktop.
Symfony custom Argument Resolver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Request; | |
use Symfony\Component\HttpFoundation\Request; | |
interface RequestInterface | |
{ | |
public static function fromRequest(Request $request): self; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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