#Создание ответов
#Строки и массивы
Все маршруты и контроллеры должны возвращать ответ, который будет отправлен обратно в браузер пользователя. Laravel предоставляет несколько способов возвращать ответы. Самый простой ответ — это возврат строки из маршрута или контроллера. Фреймворк автоматически преобразует строку в полноценный HTTP-ответ:
Route::get('/', function () {
return 'Hello World';
});
Помимо возврата строк из маршрутов и контроллеров, вы также можете возвращать массивы. Фреймворк автоматически преобразует массив в JSON-ответ:
Route::get('/', function () {
return [1, 2, 3];
});
Знаете ли вы, что также можно возвращать Eloquent коллекции из маршрутов или контроллеров? Они автоматически преобразуются в JSON. Попробуйте!
#Объекты Response
Обычно вы не будете возвращать простые строки или массивы из действий маршрутов. Вместо этого вы будете возвращать полноценные экземпляры Illuminate\Http\Response или представления.
Возврат полного экземпляра Response позволяет настраивать HTTP-статус и заголовки ответа. Экземпляр Response наследуется от класса Symfony\Component\HttpFoundation\Response, который предоставляет множество методов для построения HTTP-ответов:
Route::get('/home', function () {
return response('Hello World', 200)
->header('Content-Type', 'text/plain');
});
#Модели и коллекции Eloquent
Вы также можете возвращать модели и коллекции Eloquent ORM напрямую из маршрутов и контроллеров. В этом случае Laravel автоматически преобразует модели и коллекции в JSON-ответы с учётом скрытых атрибутов модели:
use App\Models\User;
Route::get('/user/{user}', function (User $user) {
return $user;
});
#Добавление заголовков к ответам
Учтите, что большинство методов ответа поддерживают цепочку вызовов, что позволяет удобно строить экземпляры ответов. Например, вы можете использовать метод header для добавления нескольких заголовков к ответу перед отправкой пользователю:
return response($content)
->header('Content-Type', $type)
->header('X-Header-One', 'Header Value')
->header('X-Header-Two', 'Header Value');
Или вы можете использовать метод withHeaders, чтобы передать массив заголовков, которые будут добавлены к ответу:
return response($content)
->withHeaders([
'Content-Type' => $type,
'X-Header-One' => 'Header Value',
'X-Header-Two' => 'Header Value',
]);
#Middleware для управления кэшированием
В Laravel есть middleware cache.headers, который позволяет быстро установить заголовок Cache-Control для группы маршрутов. Директивы должны передаваться в формате snake_case, соответствующем директивам cache-control, и разделяться точкой с запятой. Если в списке директив указан etag, то автоматически будет установлен MD5-хэш содержимого ответа в качестве идентификатора ETag:
Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function () {
Route::get('/privacy', function () {
// ...
});
Route::get('/terms', function () {
// ...
});
});
#Добавление cookie к ответам
Вы можете добавить cookie к исходящему экземпляру Illuminate\Http\Response с помощью метода cookie. В этот метод нужно передать имя, значение и количество минут, в течение которых cookie считается действительным:
return response('Hello World')->cookie(
'name', 'value', $minutes
);
Метод cookie также принимает несколько дополнительных аргументов, которые используются реже. Обычно они имеют то же назначение и смысл, что и аргументы для нативной PHP-функции setcookie:
return response('Hello World')->cookie(
'name', 'value', $minutes, $path, $domain, $secure, $httpOnly
);
Если вы хотите гарантировать отправку cookie с исходящим ответом, но у вас ещё нет экземпляра этого ответа, вы можете использовать фасад Cookie для "поставки в очередь" cookie, которые будут добавлены к ответу при его отправке. Метод queue принимает аргументы для создания экземпляра cookie. Эти cookie будут добавлены к исходящему ответу перед отправкой в браузер:
use Illuminate\Support\Facades\Cookie;
Cookie::queue('name', 'value', $minutes);
#Создание экземпляров cookie
Если вы хотите создать экземпляр Symfony\Component\HttpFoundation\Cookie, который можно будет добавить к ответу позже, используйте глобальный хелпер cookie. Этот cookie не будет отправлен клиенту, пока не будет прикреплён к экземпляру ответа:
$cookie = cookie('name', 'value', $minutes);
return response('Hello World')->cookie($cookie);
#Раннее удаление cookie
Вы можете удалить cookie, установив его истёкшим с помощью метода withoutCookie исходящего ответа:
return response('Hello World')->withoutCookie('name');
Если у вас ещё нет экземпляра исходящего ответа, вы можете использовать метод expire фасада Cookie для удаления cookie:
Cookie::expire('name');
#Cookie и шифрование
По умолчанию все cookie, создаваемые Laravel, шифруются и подписываются, чтобы клиент не мог их изменить или прочитать. Если вы хотите отключить шифрование для части cookie, создаваемых вашим приложением, используйте свойство $except middleware App\Http\Middleware\EncryptCookies, который находится в директории app/Http/Middleware:
/**
* Имена cookie, которые не должны шифроваться.
*
* @var array
*/
protected $except = [
'cookie_name',
];
#Редиректы
Редирект-ответы — это экземпляры класса Illuminate\Http\RedirectResponse, которые содержат необходимые заголовки для перенаправления пользователя на другой URL. Существует несколько способов создать экземпляр RedirectResponse. Самый простой — использовать глобальный хелпер redirect:
Route::get('/dashboard', function () {
return redirect('home/dashboard');
});
Иногда нужно перенаправить пользователя на предыдущую страницу, например, если отправленная форма содержит ошибки. Для этого используйте глобальную функцию back. Поскольку эта функция использует сессию, убедитесь, что маршрут, вызывающий back, использует группу middleware web:
Route::post('/user/profile', function () {
// Проверка запроса...
return back()->withInput();
});
#Редирект на именованные маршруты
Если вызвать хелпер redirect без параметров, будет возвращён экземпляр Illuminate\Routing\Redirector, что даёт возможность вызывать любые методы этого экземпляра Redirector. Например, чтобы создать RedirectResponse для именованного маршрута, можно использовать метод route:
return redirect()->route('login');
Если маршрут принимает параметры, передайте их вторым аргументом методу route:
// Для маршрута с URI: /profile/{id}
return redirect()->route('profile', ['id' => 1]);
#Заполнение параметров через модели Eloquent
Если вы редиректите на маршрут с параметром "ID", который заполняется из модели Eloquent, можно передать саму модель. ID будет извлечён автоматически:
// Для маршрута с URI: /profile/{id}
return redirect()->route('profile', [$user]);
Если хотите настроить значение, которое будет подставлено в параметр маршрута, можно указать колонку в определении параметра маршрута (/profile/{id:slug}) или переопределить метод getRouteKey в вашей модели Eloquent:
/**
* Получить значение ключа маршрута модели.
*/
public function getRouteKey(): mixed
{
return $this->slug;
}
#Редирект на действия контроллеров
Вы также можете создавать редиректы на действия контроллеров. Для этого передайте контроллер и имя действия методу action:
use App\Http\Controllers\UserController;
return redirect()->action([UserController::class, 'index']);
Если маршруту контроллера нужны параметры, передайте их вторым аргументом методу action:
return redirect()->action(
[UserController::class, 'profile'], ['id' => 1]
);
#Редирект на внешние домены
Иногда нужно сделать редирект на домен вне вашего приложения. Для этого вызовите метод away, который создаёт RedirectResponse без дополнительного кодирования URL, валидации или проверки:
return redirect()->away('https://www.google.com');
#Редирект с флеш-данными сессии
Редирект на новый URL и флеш-данные в сессии обычно выполняются одновременно. Обычно это делается после успешного действия, когда вы добавляете сообщение об успехе в сессию. Для удобства можно создать RedirectResponse и добавить флеш-данные в цепочке вызовов:
Route::post('/user/profile', function () {
// ...
return redirect('dashboard')->with('status', 'Профиль обновлён!');
});
После редиректа вы можете вывести флеш-сообщение из сессии. Например, с помощью синтаксиса Blade:
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
#Редирект с сохранением ввода
Вы можете использовать метод withInput у RedirectResponse, чтобы сохранить данные текущего запроса в сессии перед редиректом пользователя. Обычно это делается при ошибках валидации. После сохранения данных вы можете легко получить их в следующем запросе для заполнения формы:
return back()->withInput();
#Другие типы ответов
Хелпер response можно использовать для создания других типов ответов. Если вызвать response без аргументов, будет возвращена реализация контракта Illuminate\Contracts\Routing\ResponseFactory contract. Этот контракт предоставляет несколько полезных методов для создания ответов.
#Ответы с представлениями
Если вам нужно контролировать статус и заголовки ответа, но при этом вернуть представление в качестве содержимого, используйте метод view:
return response()
->view('hello', $data, 200)
->header('Content-Type', $type);
Конечно, если не нужно передавать кастомный HTTP-статус или заголовки, можно использовать глобальный хелпер view.
#JSON-ответы
Метод json автоматически устанавливает заголовок Content-Type в application/json и преобразует переданный массив в JSON с помощью функции PHP json_encode:
return response()->json([
'name' => 'Abigail',
'state' => 'CA',
]);
Если нужно создать JSONP-ответ, используйте метод json вместе с методом withCallback:
return response()
->json(['name' => 'Abigail', 'state' => 'CA'])
->withCallback($request->input('callback'));
#Загрузка файлов
Метод download можно использовать для формирования ответа, который принудительно заставит браузер пользователя скачать файл по указанному пути. Метод download принимает имя файла в качестве второго аргумента, которое определяет имя файла, видимое пользователю при скачивании. Наконец, вы можете передать массив HTTP-заголовков в качестве третьего аргумента методу:
return response()->download($pathToFile);
return response()->download($pathToFile, $name, $headers);
Symfony HttpFoundation, управляющий загрузками файлов, требует, чтобы имя скачиваемого файла было в ASCII.
#Потоковая загрузка
Иногда нужно превратить строковый ответ операции в скачиваемый файл без записи содержимого на диск. Для этого используйте метод streamDownload. Он принимает callback, имя файла и необязательный массив заголовков:
use App\Services\GitHub;
return response()->streamDownload(function () {
echo GitHub::api('repo')
->contents()
->readme('laravel', 'laravel')['contents'];
}, 'laravel-readme.md');
#Ответы с файлами
Метод file позволяет отображать файл, например изображение или PDF, прямо в браузере пользователя, а не скачивать его. В качестве первого аргумента передаётся абсолютный путь к файлу, вторым — массив заголовков:
return response()->file($pathToFile);
return response()->file($pathToFile, $headers);
#Макросы ответов
Если вы хотите определить собственный ответ, который можно переиспользовать в разных маршрутах и контроллерах, используйте метод macro фасада Response. Обычно этот метод вызывают из метода boot одного из сервис-провайдеров приложения, например, App\Providers\AppServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Загрузка сервисов приложения.
*/
public function boot(): void
{
Response::macro('caps', function (string $value) {
return Response::make(strtoupper($value));
});
}
}
Метод macro принимает имя макроса первым аргументом и замыкание вторым. Замыкание макроса будет выполнено при вызове макроса через реализацию ResponseFactory или хелпер response:
return response()->caps('foo');