Когда я впервые познакомился с Symfony, то меня поразил компонент symfony/http-foundation. Думаю, это один из самых важных пакетов современного PHP-приложения. Он заполняет лакуны основных PHP-функций, предоставляя дружественный объектно-ориентированный интерфейс для запросов и ответов.
В Laravel объекты Illuminate Request и Response используют компонент HTTP Foundation через наследование и предоставляют отличный удобный API поверх основных классов.
В этой статье мы рассмотрим некоторые из методов объекта Illuminate Request.
Класс Illuminate\Http\Request предлагает функционал в трёх трейтах, которые мы и рассмотрим:
InteractsWithContentTypesInteractsWithInputInteractsWithFlashData
Каких-то откровений и уникальных знаний здесь не будет, но, уверен, что вы откроете для себя один-два полезных метода. И, я надеюсь, что новички, которые не очень хорошо еще знают фреймворк смогут изучить эти важные классы и оценить все удобства Illuminate Request.
Взаимодействие с типами контента
В течение жизненного цикла запроса/ответа иногда необходимо определить тип запроса через HTTP-заголовок Content-Type. Для этого InteractsWithContentTypes предоставляет нам много синтаксического сахара, вместо того чтобы разбирать запрос через HTTP foundation.
Например, мы можем использовать следующие JSON-методы для определения, запрашивается ли JSON. И мы получим однозначный boolean-ответ:
$request->wantsJson(); $request->expectsJson(); $request->isJson(); $request->acceptsJson();
Во- первых, метод isJson() проверяет наличие /json и +json в заголовке Content-Type. Он полезен для определения, отправил ли клиент JSON.
Разница между isJson() и wantsJson() заключается в следующих нюансах: wantsJson() определяет заголовок Accept при формировании правильного ответа на запрос, в то время как isJson() используется для определения формата текущего запроса.
Точно так же метод expectsJson() определяет, ожидает ли входящий запрос JSON-ответа: будет true, если это AJAX-запрос, не PJAX-запрос, и принимает любой тип содержимого ( */*).
Наконец, метод acceptptsJson() полезен для определения есть ли application/json в допустимых типах ответа. В этом методе используется основной метод accepts(), который принимает строку или массив с типами контента:
$request->accepts(['application/xml', 'text/xml']);
Последний метод, который я хотел бы упомянуть из этого трейта — это format(), который используется для определения формата ожидаемого ответа (с необязательным дефолтным значением):
// Определяет формат на основе запроса
// Возвращает json, если формат не найден
// среди допустимых типов контента
$request->format('json');
Существует еще несколько методов, с которыми вы можете ознакомиться посмотрев исходник трейта InteractsWithContentTypes.php.
Взаимодействие с Входными Данными
Удобство работы с HTTP Input — моя любимая часть Illuminate Request.
Основная рабочая лошадка трейта InteractsWithInput это метод input(). Он очень простой и я покажу его вам полностью, прежде чем мы начнём его использовать:
/**
* Получение элемента ввода из запроса
*
* @param string|null $key
* @param mixed $default
* @return mixed
*/
public function input($key = null, $default = null)
{
return data_get(
$this->getInputSource()->all() + $this->query->all(), $key, $default
);
}
Если заглянуть под капот, хелпер data_get(), в сочетании с этим методом, предоставляет универсальный комплекс для получения входных данных из различных источников:
- Ввод
ParameterBag - запрос
Request
Обычно этот метод используется для получения параметров POST|PUT|PATCH и получения данных из строки запроса:
$email = $request->input('email');
Основной класс Request также использует метод all() трейта InteractsWithInput для предоставления сокращенной версии вызова входных данных:
// Внутри вызывается $request->all() // и пытается получить входные данные из запроса $request->email
Еще один мой любимый метод в этом трейте это only(). Обычно я вычищаю всё, кроме данных, которые мне нужны и которые я потом сохраню:
// вариативные аргументы через func_get_args()
$request->only('email', 'password');
// или черех массив
$request->only(['email', 'password']);
С помощью only() я вношу в белый список нужные параметры запроса, чтобы избежать непреднамеренной утечки данных клиента.
Еще один полезный метод — это получение входных данных в виде boolean, для определения являются ли они true или false:
// Проверка opt_in на наличие логических типов значенией
// при дефолтном значении true
// Вернёт true если на входе: 1, true, on, yes
$request->boolean('opt_in', true);
У этого трейта есть полезные методы для работы с HTTP-заголовками:
// Полечение токена из заголовка авторизации
// Удаление префикса "Bearer", если он имеется
// то есть, "Authorization: Bearer 1234" вернёт "1234".
$request->bearerToken();
$request->hasHeader('X-Custom-Header');
$request->header('X-Custom-Header');
Эти методы позволяют избежать базовый запросов Symfony:
// Эквивалентно $request->header('X-Custom-Header');
$request->headers->get('X-Custom-Header');
// Эквивалентно $request->hasHeader('X-Custom-Header')
! is_null($request->headers->get('X-Custom-Header'));
Еще несколько полезных вспомогательных методов для работы с входными данными:
// Все данные кроме password
$request->except('password');
// Проверяем, есть ли в запросе ключ
$request->has('age');
// Проверяем, есть ли в запросе какой-либо их следующих ключей
$request->hasAny('age', 'height', 'weight');
// Получаем все ключ входных данных и файлов
$request->keys();
// Проверяем, отсутствуют ли в запросе следующие данные
$request->missing('age');
// Проверяем, заполненно ли поле
See if the input is a non-empty value
$request->filled('age');
// Проверяем, заполненны ли следующие поля
$request->anyFilled('age', 'height', 'weight');
// Получаем ?id=12345
$request->query('id');
// Получаем массив всех файлов запроса
$request->allFiles();
// Проверяем, есть ли загруженный файл для данного ключа
$request->hasFile('avatar');
// Получаем файл из запроса
$request->file('avatar');
Взаимодействие с данными сессии
Общей потребностью серверных приложениях является сохранение данных запроса в сессии, например при возникновении ошибок валидации. Передача данных в сессию гарантирует, что пользователь сможет исправить любые невалидные данные в форме и сохранить актуальные данные для использования в следующем запросе и ответе.
Возможно, вы знакомы с хелпером old() в blade-файлах, который заполняет поля сессионными данными из предыдущего запроса:
<input type="email" value="{{ old("email") }}" required />
Он использует метод old() в этом же трейте:
function old($key = null, $default = null)
{
return app('request')->old($key, $default);
}
Как получить данные из сессии?
Если вы еще новичок в Laravel, то сохранении данных в сессии может вам показаться волшебством, так как Validation делает это автоматически. При возникновении ошибок валидации фреймворк кидает исключение ValidationException, которое, в конечном итоге, сохраняет данные в сессии с помощью Response через обработчик исключений:
/**
* Преобразование исключения валидации в ответ
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Validation\ValidationException $exception
* @return \Illuminate\Http\Response
*/
protected function invalid($request, ValidationException $exception)
{
return redirect($exception->redirectTo ?? url()->previous())
->withInput(Arr::except($request->input(), $this->dontFlash))
->withErrors($exception->errors(), $exception->errorBag);
}
Обратите внимание, что $this->dontFlash гарантирует, что поля password и password_confirmation не будут сохранены в сессии.
В большинстве случаев валидатор позаботится о переносе данных в сессию, но вы также можете вручную сохранить в ней входные данные:
// сохраняет данные $request->input() в сессии $request->flash();
Еще раз подчеркну, что при использовании этого метода обязательно позаботьтесь о том, чтобы конфиденциальные данные не передавались в сессию пользователя. Более аккуратный способ, это метод flashOnly():
$request->flashOnly('email', 'first_name', 'last_name');
или наоборот:
$request->flashExcept('password', 'secret_input');
Макросы и остальное
Поскольку объект Illuminate Request наследует HTTP Foundation Request, то всё, что есть в в нём, вы можете использовать. Ниже я дам ссылку на документацию по компоненту, если вы захотите узнать об этом больше.
Кроме того, вы можете расширить объект Request с помощью макросов. Laravel делает это с помощью метода Request::validate():
Request::macro('validate', function (array $rules, ...$params) {
return validator()->validate($this->all(), $rules, ...$params);
});
Дополнительные материалы
Я надеюсь, что статья была полезна как для тех, кто еще мало знаком с фреймворком Laravel, и хочет открыть для себя полезные инструменты, надстроенные поверх основного HTTP-слоя. Так и для более опытных, которым мы показали несколько новых методов объекта Illuminate Request или же вдохновили глубже изучить эти классы.
Для того, чтобы больше узнать об объектах запроса и ответа, рекомендуем ознакомиться со следующими ресурсами:
- Компонент HttpFoundation — Документация Symfony
- HTTP-запросы — Документация Laravel
- Illuminate/Http at 8.x — laravel/framework — GitHub
Автор: Paul Redmond
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.
