Symfony 4 продлил запрос на проверку

В настоящее время я работаю над проектом API. Я привык к Laravel и теперь мне нужно работать с Symfony. Я хочу использовать запрос, как Laravel, для проверки. Поэтому я расширяю класс Symfony\Component\HttpFoundation\Request. Там я сделал некоторую логику, чтобы проверить и очистить входящий запрос.

После этого я добавляю свой недавно созданный запрос в функцию store в контроллере. Но это дает мне ошибку:

Argument 1 passed to App\Controller\Ticket\TicketController::store() must be an instance of App\Validation\Ticket\TicketStoreRequest, instance of Symfony\Component\HttpFoundation\Request given,/vendor/symfony/http-kernel/HttpKernel.php on line 149 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalThrowableError(code: 0): Argument 1 passed to App\\Controller\\Ticket\\TicketController::store() must be an instance of App\\Validation\\Ticket\\TicketStoreRequest, instance of Symfony\\Component\\HttpFoundation\\Request given

После некоторого поиска в Google я нашел несколько вариантов.

  1. Добавьте каждое действие контроллера в service.yaml
  2. Создайте слушателя и добавьте проверку правильности маршрута.

Но все варианты требуют дополнительной информации о другом месте. Я надеюсь, что у кого-то есть идея получше.

2 ответа

Решение

Я нашел несколько подсказок, которые могут указать вам правильное направление:

Symfony поставляется с пятью определителями значений в компоненте HttpKernel:

...

RequestValueResolver

Внедряет текущий запрос, если подсказывает тип с помощью запроса или класса, расширяющего запрос.

см. https://symfony.com/doc/current/controller/argument_value_resolver.html

Затем страница переходит к описанию реализации пользовательского RequestAttributeValueResolver. Этот пользовательский распознаватель может быть зарегистрирован в вашем services.yml, Хотя в этом примере создается один класс для одного типа атрибута (пользователь), существуют способы создания более динамичной реализации.

В этом примере ArgumentMetadata у параметра есть метод $argument->getType() это должно содержать строковое представление типа, который проверяется на:

if (User::class !== $argument->getType()) {
    return false;
}

Ничто не мешает вам проверять массив поддерживаемых типов запросов. Этот массив может управляться как член класса в вашем пользовательском RequestValueResolver, Единственное требование к вашему заказу RequestValueResolver класс, это то, что supports() метод возвращает true для поддерживаемых типов запросов, и что resolve() функция возвращает экземпляр этого поддерживаемого типа запроса. Это должно быть просто, потому что оба метода передают "нужный" класс через ArgumentMetaData параметр.

В качестве альтернативы, вы можете реализовать RequestValueResolver для каждого пользовательского типа запроса, который вы хотите поддерживать, но это не очень элегантно.

Я не могу гарантировать, что это сработает, и я также не уверен в различиях между RequestAttributeValueResolver в примере и реализации пользовательских RequestValueResolver, но у меня есть ощущение, что это может сработать, с небольшим количеством смазки для локтя.

Для справки: https://api.symfony.com/4.1/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.html

Вот решение, которое развивается на моем другом примере. Это не так безопасно, как мой другой пример, но оно удовлетворяет вашим требованиям.

public function supports(Request $request, ArgumentMetadata $argument)
{    
    $desiredRequestClass = $argument->getType();

    return class_exists($desiredRequestClass);
}

public function resolve(Request $request, ArgumentMetadata $argument)
{
    $desiredRequestClass = $argument->getType();
    $customRequest = new $desiredRequestClass();

    $customRequest->createFromGlobals();
    // TODO: more initialization you might need.

    yield $customRequest;
}

Было бы целесообразно проверить, если $desiredRequestClass является потомком Request,

Другие вопросы по тегам