fenix007 / request-dto-bundle
This Symfony Bundle provides request objects support for Symfony controller actions
Installs: 6
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 3
Type:symfony-bundle
Requires
- php: ^7.4 | ^8.0
- symfony/dependency-injection: ^5.0
- symfony/http-kernel: ^5.0
- symfony/property-access: ^5.0
- symfony/property-info: ^5.0
- symfony/serializer: ^5.0
- symfony/validator: ^5.0
Requires (Dev)
- ext-json: *
- ext-simplexml: *
- doctrine/annotations: ^1.10
- symfony/framework-bundle: ^5.0
- symfony/test-pack: ^1.0
- symfony/var-dumper: ^5.0
Suggests
- doctrine/annotations: For support @Assert annotations
Conflicts
- symfony/framework-bundle: <5.0.0
This package is not auto-updated.
Last update: 2024-11-22 10:48:15 UTC
README
RequestDtoBundle
This Symfony Bundle provides request objects support for Symfony controller actions.
Installation
Require the bundle with composer:
composer require nelexa/request-dto-bundle
Examples of using
To specify an object as an argument of a controller action, an object must implement one of 4 interfaces:
\Nelexa\RequestDtoBundle\Dto\QueryObjectInterface
query parameters for GET or HEAD request methods.\Nelexa\RequestDtoBundle\Dto\RequestObjectInterface
request parameters for POST, PUT or DELETE request methods (ex. Content-Type: application/x-www-form-urlencoded) or query parameters for GET and HEAD request methods.\Nelexa\RequestDtoBundle\Dto\RequestBodyObjectInterface
for POST, PUT, DELETE request body contents (ex. Content-Type: application/json).\Nelexa\RequestDtoBundle\Dto\ConstructRequestObjectInterface
for mapping a request for a data transfer object in the class constructor.
Create request DTO:
use Nelexa\RequestDtoBundle\Dto\RequestObjectInterface; use Symfony\Component\Validator\Constraints as Assert; class UserRegistrationRequest implements RequestObjectInterface { /** @Assert\NotBlank() */ public ?string $login = null; /** * @Assert\NotBlank() * @Assert\Length(min="6") */ public ?string $password = null; /** * @Assert\NotBlank() * @Assert\Email() */ public ?string $email = null; }
Use in the controller:
<?php declare(strict_types=1); use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Validator\ConstraintViolationListInterface; class AppController extends AbstractController { /** * @Route("/sign-up", methods={"POST"}) */ public function registration( UserRegistrationRequest $userRegistrationRequest, ConstraintViolationListInterface $errors ): Response { $data = ['success' => $errors->count() === 0]; if ($errors->count() > 0){ $data['errors'] = $errors; } else{ $data['data'] = $userRegistrationRequest; } return $this->json($data); } }
If you declare an argument with type \Symfony\Component\Validator\ConstraintViolationListInterface
as nullable, then if there are no errors, it will be null
.
... /** * @Route("/sign-up", methods={"POST"}) */ public function registration( UserRegistrationRequest $userRegistrationRequest, ?ConstraintViolationListInterface $errors ): Response { return $this->json( [ 'success' => $errors === null, 'errors' => $errors, ] ); } ...
If the argument \Symfony\Component\Validator\ConstraintViolationListInterface
is not declare, then the exception \Nelexa\RequestDtoBundle\Exception\RequestDtoValidationException
will be thrown, which will be converted to the json
or xml
format.
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class AppController extends AbstractController{ /** * @Route("/sign-up", methods={"POST"}) */ public function registration(UserRegistrationRequest $userRegistrationRequest): Response { return $this->json(['success' => true]); } }
Send POST request:
curl 'https://127.0.0.1/registration' -H 'Accept: application/json' -H 'Content-Type: application/x-www-form-urlencoded' --data-raw 'login=johndoe'
Response:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content response:
{ "type": "https://tools.ietf.org/html/rfc7807", "title": "Validation Failed", "detail": "password: This value should not be blank.\nemail: This value should not be blank.", "violations": [ { "propertyPath": "password", "title": "This value should not be blank.", "parameters": { "{{ value }}": "null" }, "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" }, { "propertyPath": "email", "title": "This value should not be blank.", "parameters": { "{{ value }}": "null" }, "type": "urn:uuid:c1051bb4-d103-4f74-8988-acbcafc7fdc3" } ] }
Construct DTO from Request (version 1.1.0+)
use Nelexa\RequestDtoBundle\Dto\ConstructRequestObjectInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\ConstraintViolationListInterface; class ExampleDTO implements ConstructRequestObjectInterface { /** @Assert\Range(min=1) */ private int $page; /** * @Assert\NotBlank * @Assert\Regex("~^\d{10,13}$~", message="Invalid phone number") */ private string $phone; public function __construct(Request $request) { $this->page = $request->request->getInt('p', 1); // sanitize phone number $phone = (string) $request->request->get('phone'); $phone = preg_replace('~\D~', '', $phone); $this->phone = (string) $phone; } public function getPage(): int { return $this->page; } public function getPhone(): string { return $this->phone; } } class AppController extends AbstractController { public function exampleAction( ExampleDTO $dto, ConstraintViolationListInterface $errors ): Response { $data = [ 'page' => $dto->getPage(), 'phone' => $dto->getPhone(), 'errors' => $errors, ]; return $this->json($data, $errors->count() === 0 ? 200 : 400); } }
Changelog
Changes are documented in the releases page.
License
The MIT License (MIT). Please see LICENSE for more information.