diff --git a/Readme.md b/Readme.md index 848aba7..7963c3a 100644 --- a/Readme.md +++ b/Readme.md @@ -2,5 +2,124 @@ [![coverage report](https://git.rinsvent.ru/symfony/bundles/request-bundle/badges/master/coverage.svg)](https://git.rinsvent.ru/symfony/bundles/request-bundle/-/commits/master) Request bundle -= +=== +Bundle умеет конвертировать request в DTO +Полученная DTO валидируется. +В случае ошибок выполнение прекращается и возвращаются ошибки. +В случае успеха DTO присваиваются в атрибуты и доступны в методе контроллера. + +### Пример запроса +```json +{ + "signin": { + "transport": "phone", + "value": "8888888888", + "code": "password" + } +} +``` +### Пример DTO +```php +namespace App\DTO\Request; + +use Rinsvent\Data2DTO\Attribute\HandleTags; +use Rinsvent\Data2DTO\Attribute\VirtualProperty; +use Symfony\Component\Validator\Constraints as Assert; +use App\Validator as ProjectAssert; + +use Rinsvent\Data2DTO\Attribute\PropertyPath; +use Rinsvent\Data2DTOBundle\Service\Transformer\Request\Headers\Header; +use Rinsvent\Data2DTOBundle\Service\Transformer\Request\Headers\UserAgent; +use Rinsvent\Data2DTOBundle\Service\Transformer\Request\Server\Ip; + +#[HandleTags(method: 'getTags')] +class SigninRequest +{ + #[Assert\NotBlank(message: "error.transport.empty")] + public string $transport; + + #[Assert\NotBlank(message: "error.value.empty")] + #[Assert\Email(message: "error.email.wrong", groups: "email")] + #[ProjectAssert\Phone(message: "error.phone.wrong", groups: "phone")] + public string $value; + + #[Assert\NotBlank(message: "error.code.empty")] + public string $code; + #[VirtualProperty] + public Device $device; + + public function getTags(array $data, array $tags) + { + $transport = $data['transport'] ?? null; + if ($transport) { + $tags[] = $transport; + } + return $tags; + } +} + +class Device +{ + #[Assert\NotBlank(message: "error.device.id.empty")] + #[Header(property: 'X-Device-Id')] + public string $deviceId; + + #[Assert\NotBlank(message: "error.device.source.empty")] + #[Header(property: 'X-Source')] + public string $source; + + #[Assert\NotBlank(message: "error.device.ip.empty")] + #[Ip] + public ?string $ip = null; + + #[Assert\NotBlank(message: "error.device.user_agent.empty")] + #[UserAgent] + public string $userAgent; +} + ``` +### Использование +```php +namespace App\Controller; + +use App\Form\Type\User\DTO\SigninRequest; +use App\Service\Entity\UserService; +use Rinsvent\RequestBundle\Annotation\RequestDTO; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; + +class UserController extends AbstractController +{ + public function __construct( + private UserService $us + ) {} + + #[Route('/v1/signin', name: 'signin', methods: ['POST'])] + #[RequestDTO(className: SigninRequest::class, jsonPath: '$.signin')] + public function signin(SigninRequest $signinRequest) + { + $signinResponse = $this->us->signin($signinRequest); + + return new JsonResponse( + [], + Response::HTTP_OK, + [ + 'X-Access-Token' => $signinResponse->getAccessToken(), + 'X-Refresh-Token' => $signinResponse->getRefreshToken(), + ] + ); + } + + // Вариант с несколькими DTO + #[Route('/v1/signin', name: 'signin', methods: ['POST'])] + #[RequestDTO(className: SigninRequest::class, jsonPath: '$.signin')] + #[RequestDTO(className: Device::class)] + public function signin2(SigninRequest $signinRequest, Device $device) + { + $signinResponse = $this->us->signin2($signinRequest, $device); + return new JsonResponse(); + } +} +``` diff --git a/src/Annotation/RequestDTO.php b/src/Annotation/RequestDTO.php index dde2e9c..347d2ef 100644 --- a/src/Annotation/RequestDTO.php +++ b/src/Annotation/RequestDTO.php @@ -2,10 +2,7 @@ namespace Rinsvent\RequestBundle\Annotation; -/** - * todo переделать на https://symfony.com/doc/current/components/property_access.html#usage - */ -#[\Attribute] +#[\Attribute(\Attribute::IS_REPEATABLE)] class RequestDTO { public function __construct(