HTTP-відповіді
Створення відповідей
Рядки та масиви
Усі маршрути й контролери мають повертати відповідь, яку буде надіслано до браузера користувача. Laravel пропонує кілька різних способів повертати відповіді. Найпростіша відповідь - це рядок, повернений із маршруту чи контролера. Фреймворк автоматично перетворить його на повноцінну HTTP-відповідь:
Route::get('/', function () {
return 'Hello World';
});
Окрім рядків, ви можете повертати з маршрутів і контролерів масиви. Фреймворк автоматично перетворить масив на JSON-відповідь:
Route::get('/', function () {
return [1, 2, 3];
});
А ви знали, що з маршрутів чи контролерів можна також повертати колекції Eloquent? Вони автоматично перетворяться на JSON. Спробуйте!
Об'єкти відповідей
Зазвичай ви не повертатимете з дій маршрутів лише прості рядки чи масиви. Натомість ви повертатимете повноцінні екземпляри 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',
]);
Ви можете вилучити конкретні заголовки з вихідної відповіді методом withoutHeader:
return response($content)->withoutHeader('X-Debug');
return response($content)->withoutHeader(['X-Debug', 'X-Powered-By']);
Middleware керування кешем
Laravel містить middleware cache.headers, який дозволяє швидко задати заголовок Cache-Control для групи маршрутів. Директиви слід передавати у вигляді «snake case» відповідної директиви cache-control, розділяючи їх крапкою з комою. Якщо в списку директив указано etag, як ідентифікатор ETag автоматично буде встановлено MD5-хеш вмісту відповіді:
Route::middleware('cache.headers:public;max_age=30;s_maxage=300;stale_while_revalidate=600;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::expire('name');
Cookie та шифрування
За замовчуванням, завдяки middleware Illuminate\Cookie\Middleware\EncryptCookies, усі cookie, згенеровані Laravel, шифруються й підписуються, тож клієнт не може їх змінити чи прочитати. Якщо ви хочете вимкнути шифрування для частини cookie вашого застосунку, скористайтеся методом encryptCookies у файлі bootstrap/app.php:
->withMiddleware(function (Middleware $middleware): void {
$middleware->encryptCookies(except: [
'cookie_name',
]);
})
Загалом шифрування cookie ніколи не слід вимикати, адже це наражає ваші cookie на потенційне розкриття даних і підробку на боці клієнта.
Перенаправлення
Відповіді-перенаправлення є екземплярами класу Illuminate\Http\RedirectResponse і містять потрібні заголовки для перенаправлення користувача на іншу адресу. Створити екземпляр RedirectResponse можна кількома способами. Найпростіший - скористатися глобальним хелпером redirect:
Route::get('/dashboard', function () {
return redirect('/home/dashboard');
});
Іноді вам може знадобитися перенаправити користувача на попередню сторінку - наприклад, коли надіслана форма виявилася недійсною. Це робиться глобальною функцією-хелпером back. Оскільки ця можливість використовує сесію, переконайтеся, що маршрут, який викликає back, входить до групи middleware web:
Route::post('/user/profile', function () {
// Validate the request...
return back()->withInput();
});
Перенаправлення до іменованих маршрутів
Коли ви викликаєте хелпер redirect без параметрів, повертається екземпляр Illuminate\Routing\Redirector, що дозволяє викликати на ньому будь-який метод. Наприклад, щоб згенерувати RedirectResponse до іменованого маршруту, скористайтеся методом route:
return redirect()->route('login');
Якщо ваш маршрут має параметри, передайте їх другим аргументом методу route:
// For a route with the following URI: /profile/{id}
return redirect()->route('profile', ['id' => 1]);
Заповнення параметрів через моделі Eloquent
Якщо ви перенаправляєте до маршруту з параметром «ID», який заповнюється з моделі Eloquent, ви можете передати саму модель. Ідентифікатор буде вилучено автоматично:
// For a route with the following URI: /profile/{id}
return redirect()->route('profile', [$user]);
Якщо ви хочете налаштувати значення, що потрапляє в параметр маршруту, вкажіть колонку у визначенні параметра (/profile/{id:slug}) або перевизначте метод getRouteKey у своїй моделі Eloquent:
/**
* Get the value of the model's route key.
*/
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');
Перенаправлення з флеш-даними сесії
Перенаправлення на нову адресу та запис флеш-даних до сесії зазвичай виконуються одночасно. Типово це роблять після успішного виконання дії, записуючи до сесії повідомлення про успіх. Для зручності ви можете створити екземпляр RedirectResponse і записати дані до сесії одним плинним ланцюжком методів:
Route::post('/user/profile', function () {
// ...
return redirect('/dashboard')->with('status', 'Profile updated!');
});
Після перенаправлення користувача ви можете показати збережене повідомлення із сесії. Наприклад, за допомогою синтаксису Blade:
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
Перенаправлення з вхідними даними
Метод withInput екземпляра RedirectResponse дозволяє записати вхідні дані поточного запиту до сесії перед перенаправленням користувача. Зазвичай це роблять, коли користувач натрапив на помилку валідації. Щойно вхідні дані записано до сесії, ви можете легко отримати їх під час наступного запиту, щоб заново заповнити форму:
return back()->withInput();
Інші типи відповідей
Хелпер response дозволяє генерувати інші типи екземплярів відповідей. Коли його викликають без аргументів, повертається реалізація контракту Illuminate\Contracts\Routing\ResponseFactory. Цей контракт надає кілька корисних методів для генерації відповідей.
Відповіді-представлення
Якщо вам потрібен контроль над статусом і заголовками відповіді, але водночас потрібно повернути представлення як її вміст, скористайтеся методом 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.
Файлові відповіді
Метод file дозволяє показати файл - як-от зображення чи PDF - безпосередньо в браузері користувача замість того, щоб починати завантаження. Цей метод приймає абсолютний шлях до файлу першим аргументом і масив заголовків другим:
return response()->file($pathToFile);
return response()->file($pathToFile, $headers);
Потокові відповіді
Передаючи дані клієнту в міру їх генерації, ви можете значно зменшити споживання пам'яті й покращити швидкодію - особливо для дуже великих відповідей. Потокові відповіді дозволяють клієнту почати обробку даних ще до того, як сервер завершить їх надсилання:
Route::get('/stream', function () {
return response()->stream(function (): void {
foreach (['developer', 'admin'] as $string) {
echo $string;
ob_flush();
flush();
sleep(2); // Simulate delay between chunks...
}
}, 200, ['X-Accel-Buffering' => 'no']);
});
Для зручності, якщо замикання, передане методу stream, повертає генератор, Laravel автоматично скидатиме буфер виводу між рядками, які повертає генератор, а також вимкне буферизацію виводу Nginx:
Route::post('/chat', function () {
return response()->stream(function (): Generator {
$stream = OpenAI::client()->chat()->createStreamed(...);
foreach ($stream as $response) {
yield $response->choices[0];
}
});
});
Споживання потокових відповідей
Потокові відповіді можна споживати за допомогою npm-пакета stream від Laravel, який надає зручний API для роботи з відповідями та потоками подій Laravel. Щоб почати, встановіть пакет @laravel/stream-react, @laravel/stream-vue або @laravel/stream-svelte:
npm install @laravel/stream-react
npm install @laravel/stream-vue
npm install @laravel/stream-svelte
Далі скористайтеся useStream, щоб споживати потік подій. Після вказання URL потоку хук автоматично оновлюватиме data об'єднаною відповіддю в міру її надходження від вашого застосунку Laravel:
import { useStream } from "@laravel/stream-react";
function App() {
const { data, isFetching, isStreaming, send } = useStream("chat");
const sendMessage = () => {
send({
message: `Current timestamp: ${Date.now()}`,
});
};
return (
<div>
<div>{data}</div>
{isFetching && <div>Connecting...</div>}
{isStreaming && <div>Generating...</div>}
<button onClick={sendMessage}>Send Message</button>
</div>
);
}
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";
const { data, isFetching, isStreaming, send } = useStream("chat");
const sendMessage = () => {
send({
message: `Current timestamp: ${Date.now()}`,
});
};
</script>
<template>
<div>
<div>{{ data }}</div>
<div v-if="isFetching">Connecting...</div>
<div v-if="isStreaming">Generating...</div>
<button @click="sendMessage">Send Message</button>
</div>
</template>
<script>
import { useStream } from "@laravel/stream-svelte";
const stream = useStream("chat");
const sendMessage = () => {
stream.send({
message: `Current timestamp: ${Date.now()}`,
});
};
</script>
<div>
<div>{$stream.data}</div>
{#if $stream.isFetching}
<div>Connecting...</div>
{/if}
{#if $stream.isStreaming}
<div>Generating...</div>
{/if}
<button onclick={sendMessage}>Send Message</button>
</div>
Коли ви надсилаєте дані назад у потік через send, активне з'єднання з потоком скасовується перед надсиланням нових даних. Усі запити надсилаються як JSON-запити POST.
Оскільки хук
useStreamробитьPOST-запит до вашого застосунку, потрібен дійсний CSRF-токен. Найпростіший спосіб його надати - додати його через meta-тег у head макета вашого застосунку.
Другий аргумент, переданий useStream, - це об'єкт опцій, яким можна налаштувати поведінку споживання потоку. Типові значення цього об'єкта наведено нижче:
import { useStream } from "@laravel/stream-react";
function App() {
const { data } = useStream("chat", {
id: undefined,
initialInput: undefined,
headers: undefined,
csrfToken: undefined,
onResponse: (response: Response) => void,
onData: (data: string) => void,
onCancel: () => void,
onFinish: () => void,
onError: (error: Error) => void,
});
return <div>{data}</div>;
}
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";
const { data } = useStream("chat", {
id: undefined,
initialInput: undefined,
headers: undefined,
csrfToken: undefined,
onResponse: (response: Response) => void,
onData: (data: string) => void,
onCancel: () => void,
onFinish: () => void,
onError: (error: Error) => void,
});
</script>
<template>
<div>{{ data }}</div>
</template>
<script>
import { useStream } from "@laravel/stream-svelte";
const stream = useStream("chat", {
id: undefined,
initialInput: undefined,
headers: undefined,
csrfToken: undefined,
onResponse: (response) => {},
onData: (data) => {},
onCancel: () => {},
onFinish: () => {},
onError: (error) => {},
});
</script>
<div>{$stream.data}</div>
onResponse спрацьовує після успішної початкової відповіді з потоку, і в колбек передається сирий Response. onData викликається для кожного отриманого фрагмента - у колбек передається поточний фрагмент. onFinish викликається, коли потік завершився, а також коли під час циклу отримання чи читання виникає помилка.
За замовчуванням під час ініціалізації запит до потоку не робиться. Ви можете передати потоку початкові дані опцією initialInput:
import { useStream } from "@laravel/stream-react";
function App() {
const { data } = useStream("chat", {
initialInput: {
message: "Introduce yourself.",
},
});
return <div>{data}</div>;
}
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";
const { data } = useStream("chat", {
initialInput: {
message: "Introduce yourself.",
},
});
</script>
<template>
<div>{{ data }}</div>
</template>
<script>
import { useStream } from "@laravel/stream-svelte";
const stream = useStream("chat", {
initialInput: {
message: "Introduce yourself.",
},
});
</script>
<div>{$stream.data}</div>
Щоб скасувати потік вручну, скористайтеся методом cancel, який повертає хук:
import { useStream } from "@laravel/stream-react";
function App() {
const { data, cancel } = useStream("chat");
return (
<div>
<div>{data}</div>
<button onClick={cancel}>Cancel</button>
</div>
);
}
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";
const { data, cancel } = useStream("chat");
</script>
<template>
<div>
<div>{{ data }}</div>
<button @click="cancel">Cancel</button>
</div>
</template>
<script>
import { useStream } from "@laravel/stream-svelte";
const stream = useStream("chat");
</script>
<div>
<div>{$stream.data}</div>
<button onclick={() => stream.cancel()}>Cancel</button>
</div>
Щоразу, коли використовується хук useStream, генерується випадковий id для ідентифікації потоку. Він надсилається на сервер із кожним запитом у заголовку X-STREAM-ID. Коли ви споживаєте той самий потік із кількох компонентів, ви можете читати з нього й писати до нього, надавши власний id:
// App.tsx
import { useStream } from "@laravel/stream-react";
function App() {
const { data, id } = useStream("chat");
return (
<div>
<div>{data}</div>
<StreamStatus id={id} />
</div>
);
}
// StreamStatus.tsx
import { useStream } from "@laravel/stream-react";
function StreamStatus({ id }) {
const { isFetching, isStreaming } = useStream("chat", { id });
return (
<div>
{isFetching && <div>Connecting...</div>}
{isStreaming && <div>Generating...</div>}
</div>
);
}
<!-- App.vue -->
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";
import StreamStatus from "./StreamStatus.vue";
const { data, id } = useStream("chat");
</script>
<template>
<div>
<div>{{ data }}</div>
<StreamStatus :id="id" />
</div>
</template>
<!-- StreamStatus.vue -->
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";
const props = defineProps<{
id: string;
}>();
const { isFetching, isStreaming } = useStream("chat", { id: props.id });
</script>
<template>
<div>
<div v-if="isFetching">Connecting...</div>
<div v-if="isStreaming">Generating...</div>
</div>
</template>
<!-- App.svelte -->
<script>
import { useStream } from "@laravel/stream-svelte";
import StreamStatus from "./StreamStatus.svelte";
const stream = useStream("chat");
</script>
<div>
<div>{$stream.data}</div>
<StreamStatus id={stream.id} />
</div>
<!-- StreamStatus.svelte -->
<script>
import { useStream } from "@laravel/stream-svelte";
let { id } = $props();
const stream = useStream("chat", { id });
</script>
<div>
{#if $stream.isFetching}
<div>Connecting...</div>
{/if}
{#if $stream.isStreaming}
<div>Generating...</div>
{/if}
</div>
Потокові JSON-відповіді
Якщо вам потрібно передавати JSON-дані поступово, скористайтеся методом streamJson. Він особливо корисний для великих наборів даних, які треба поступово надсилати браузеру у форматі, що легко розбирається JavaScript:
use App\Models\User;
Route::get('/users.json', function () {
return response()->streamJson([
'users' => User::cursor(),
]);
});
Хук useJsonStream ідентичний хуку useStream, за винятком того, що він намагатиметься розібрати дані як JSON після завершення потоку:
import { useJsonStream } from "@laravel/stream-react";
type User = {
id: number;
name: string;
email: string;
};
function App() {
const { data, send } = useJsonStream<{ users: User[] }>("users");
const loadUsers = () => {
send({
query: "taylor",
});
};
return (
<div>
<ul>
{data?.users.map((user) => (
<li>
{user.id}: {user.name}
</li>
))}
</ul>
<button onClick={loadUsers}>Load Users</button>
</div>
);
}
<script setup lang="ts">
import { useJsonStream } from "@laravel/stream-vue";
type User = {
id: number;
name: string;
email: string;
};
const { data, send } = useJsonStream<{ users: User[] }>("users");
const loadUsers = () => {
send({
query: "taylor",
});
};
</script>
<template>
<div>
<ul>
<li v-for="user in data?.users" :key="user.id">
{{ user.id }}: {{ user.name }}
</li>
</ul>
<button @click="loadUsers">Load Users</button>
</div>
</template>
<script>
import { useJsonStream } from "@laravel/stream-svelte";
const stream = useJsonStream("users");
const loadUsers = () => {
stream.send({
query: "taylor",
});
};
</script>
<div>
<ul>
{#if $stream.data?.users}
{#each $stream.data.users as user (user.id)}
<li>{user.id}: {user.name}</li>
{/each}
{/if}
</ul>
<button onclick={loadUsers}>Load Users</button>
</div>
Потоки подій (SSE)
Метод eventStream дозволяє повернути потокову відповідь із подіями, надісланими сервером (SSE), із типом вмісту text/event-stream. Метод eventStream приймає замикання, яке має віддавати відповіді в потік у міру їх появи:
Route::get('/chat', function () {
return response()->eventStream(function () {
$stream = OpenAI::client()->chat()->createStreamed(...);
foreach ($stream as $response) {
yield $response->choices[0];
}
});
});
Якщо ви хочете налаштувати ім'я події, віддавайте екземпляр класу StreamedEvent:
use Illuminate\Http\StreamedEvent;
yield new StreamedEvent(
event: 'update',
data: $response->choices[0],
);
Споживання потоків подій
Потоки подій можна споживати за допомогою npm-пакета stream від Laravel, який надає зручний API для роботи з потоками подій Laravel. Щоб почати, встановіть пакет @laravel/stream-react, @laravel/stream-vue або @laravel/stream-svelte:
npm install @laravel/stream-react
npm install @laravel/stream-vue
npm install @laravel/stream-svelte
Далі скористайтеся useEventStream, щоб споживати потік подій. Після вказання URL потоку хук автоматично оновлюватиме message об'єднаною відповіддю в міру надходження повідомлень від вашого застосунку Laravel:
import { useEventStream } from "@laravel/stream-react";
function App() {
const { message } = useEventStream("/chat");
return <div>{message}</div>;
}
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";
const { message } = useEventStream("/chat");
</script>
<template>
<div>{{ message }}</div>
</template>
<script>
import { useEventStream } from "@laravel/stream-svelte";
const eventStream = useEventStream("/chat");
</script>
<div>{$eventStream.message}</div>
Другий аргумент, переданий useEventStream, - це об'єкт опцій, яким можна налаштувати поведінку споживання потоку. Типові значення цього об'єкта наведено нижче:
import { useEventStream } from "@laravel/stream-react";
function App() {
const { message } = useEventStream("/stream", {
eventName: "update",
onMessage: (message) => {
//
},
onError: (error) => {
//
},
onComplete: () => {
//
},
endSignal: "</stream>",
glue: " ",
});
return <div>{message}</div>;
}
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";
const { message } = useEventStream("/chat", {
eventName: "update",
onMessage: (message) => {
// ...
},
onError: (error) => {
// ...
},
onComplete: () => {
// ...
},
endSignal: "</stream>",
glue: " ",
});
</script>
<script>
import { useEventStream } from "@laravel/stream-svelte";
const eventStream = useEventStream("/chat", {
eventName: "update",
onMessage: (event) => {
//
},
onError: (error) => {
//
},
onComplete: () => {
//
},
endSignal: "</stream>",
glue: " ",
replace: false,
});
</script>
Потоки подій можна також споживати вручну через об'єкт EventSource на фронтенді вашого застосунку. Метод eventStream автоматично надішле в потік оновлення </stream>, коли потік завершиться:
const source = new EventSource('/chat');
source.addEventListener('update', (event) => {
if (event.data === '</stream>') {
source.close();
return;
}
console.log(event.data);
});
Щоб налаштувати останню подію, яку надсилають у потік, передайте екземпляр StreamedEvent в аргумент endStreamWith методу eventStream:
return response()->eventStream(function () {
// ...
}, endStreamWith: new StreamedEvent(event: 'update', data: '</stream>'));
Потокові завантаження
Іноді ви можете захотіти перетворити рядкову відповідь певної операції на завантажуваний файл, не записуючи її вміст на диск. У такому разі скористайтеся методом streamDownload. Він приймає колбек, ім'я файлу та необов'язковий масив заголовків:
use App\Services\GitHub;
return response()->streamDownload(function () {
echo GitHub::api('repo')
->contents()
->readme('laravel', 'laravel')['contents'];
}, 'laravel-readme.md');
Макроси відповідей
Якщо ви хочете визначити власну відповідь, яку можна повторно використовувати в різних маршрутах і контролерах, скористайтеся методом macro фасаду Response. Зазвичай цей метод варто викликати з методу boot одного із сервіс-провайдерів вашого застосунку - наприклад, App\Providers\AppServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Response::macro('caps', function (string $value) {
return Response::make(strtoupper($value));
});
}
}
Функція macro приймає ім'я першим аргументом і замикання другим. Замикання макроса виконуватиметься під час виклику імені макроса на реалізації ResponseFactory чи хелпері response:
return response()->caps('foo');