Skip to content

Instantly share code, notes, and snippets.

@denistouch
Last active December 30, 2022 06:34
Show Gist options
  • Save denistouch/f99a44016317835c0eb26456c2142e3c to your computer and use it in GitHub Desktop.
Save denistouch/f99a44016317835c0eb26456c2142e3c to your computer and use it in GitHub Desktop.
Разбор query параметров в модель при помощи атрибутов в php 8
<?php
namespace App\Controller;
use App\Attribute\QueryString;
use App\Attribute\RequestBody;
use App\Entity\Lesson;
use App\Entity\Studio;
use App\Model\ErrorResponse;
use App\Model\LessonChangeRequest;
use App\Model\LessonCreateRequest;
use App\Model\LessonItem;
use App\Model\LessonListRequest;
use App\Model\LessonListResponse;
use App\Model\OkResponse;
use App\Model\RepeatRequest;
use App\Service\AccessService;
use App\Service\LessonService;
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Annotations as OA;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Translation\TranslatableMessage;
class LessonController extends BaseController
{
public function __construct(
private readonly LessonService $lessonService,
private readonly AccessService $accessService,
) {
}
/**
* Получение списка
* @OA\Tag(name="Занятия")
* @OA\Parameter(in="query",name="timezone", description="Временная зона", required=true)
* @OA\Parameter(in="query",name="startDate", description="Дата начала интервала", required=true)
* @OA\Parameter(in="query",name="endDate", description="Дата конца интервалла", required=true)
* @OA\Response(
* response=200,
* description="Список занятий",
* @Model(type=LessonListResponse::class)
* )
* @OA\Response(
* response="404",
* description="Студия не найдена",
* @Model(type=ErrorResponse::class)
* )
*/
#[Route(path: '/api/v1/lessons/for-studio/{studio}', methods: ['GET'])]
#[ParamConverter('studio', options: ['mapping' => ['studio' => 'code']])]
public function getLessons(?Studio $studio, #[QueryString] LessonListRequest $request): Response
{
if (!$this->accessService->canAccessAsManager($this->getUser(), $studio) &&
!$this->accessService->canAccessAsTeacher($this->getUser(), $studio)) {
throw new NotFoundHttpException(new TranslatableMessage('studio.not_found'));
}
return $this->json($this->lessonService->getByRequestAndStudio($request, $studio));
}
}
<?php
namespace App\Exception;
use RuntimeException;
use Symfony\Component\Translation\TranslatableMessage;
use Throwable;
class QueryParameterConvertException extends RuntimeException
{
public function __construct(Throwable $previous = null)
{
parent::__construct(new TranslatableMessage('query.exception'), 0, $previous);
}
}
<?php
namespace App\ArgumentResolver;
use App\Attribute\QueryString;
use App\Exception\QueryParameterConvertException;
use App\Exception\ValidationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class QueryParametersResolver implements ArgumentValueResolverInterface
{
public function __construct(
private readonly ObjectNormalizer $normalizer,
private readonly ValidatorInterface $validator
) {
}
public function supports(Request $request, ArgumentMetadata $argument): bool
{
return count($argument->getAttributes(QueryString::class, ArgumentMetadata::IS_INSTANCEOF)) > 0;
}
public function resolve(Request $request, ArgumentMetadata $argument): iterable
{
try {
$model = $this->normalizer->denormalize($request->query->all(),$argument->getType());
} catch (\Throwable $throwable) {
throw new QueryParameterConvertException($throwable);
}
$errors = $this->validator->validate($model);
if (count($errors) > 0) {
throw new ValidationException($errors);
}
yield $model;
}
}
<?php
namespace App\Attribute;
use Attribute;
#[Attribute(Attribute::TARGET_PARAMETER)]
class QueryString
{
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment