Мощный Illuminate Request

Illuminate Request

Когда я впервые познакомился с Symfony, то меня поразил компонент symfony/http-foundation. Думаю, это один из самых важных пакетов современного PHP-приложения. Он заполняет лакуны основных PHP-функций, предоставляя дружественный объектно-ориентированный интерфейс для запросов и ответов.

В Laravel объекты Illuminate Request и Response используют компонент HTTP Foundation через наследование и предоставляют отличный удобный API поверх основных классов.

В этой статье мы рассмотрим некоторые из методов объекта Illuminate Request.

Класс Illuminate\Http\Request предлагает функционал в трёх трейтах, которые мы и рассмотрим:

  • InteractsWithContentTypes
  • InteractsWithInput
  • InteractsWithFlashData

Каких-то откровений и уникальных знаний здесь не будет, но, уверен, что вы откроете для себя один-два полезных метода. И, я надеюсь, что новички, которые не очень хорошо еще знают фреймворк смогут изучить эти важные классы и оценить все удобства 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 или же вдохновили глубже изучить эти классы.

Для того, чтобы больше узнать об объектах запроса и ответа, рекомендуем ознакомиться со следующими ресурсами:

Автор: Paul Redmond
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.