Відповіді — Laravel
Створення відповідей
Рядки та масиви
Усі маршрути й контролери мають повертати відповідь, яка буде надіслана назад до браузера користувача. Laravel надає кілька різних способів повертати відповіді. Найпростіша відповідь — це повернення рядка з маршруту або контролера. Фреймворк автоматично перетворить рядок на повноцінну HTTP-відповідь:
Route::get('/', function () {
return 'Hello World';
});
Окрім повернення рядків із ваших маршрутів і контролерів, ви також можете повертати масиви. Фреймворк автоматично перетворить масив на JSON-відповідь:
Route::get('/', function () {
return [1, 2, 3];
});
Примітка Чи знали ви, що з маршрутів або контролерів також можна повертати колекції Eloquent? Вони будуть автоматично перетворені на JSON. Спробуйте!
Обʼєкти відповідей
Зазвичай ви не обмежуєтеся поверненням простих рядків або масивів із дій ваших маршрутів. Натомість ви повертатимете повноцінні екземпляри Illuminate\Http\Response або представлення (view).
Повернення повноцінного екземпляра 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 Cache Control
Laravel містить middleware cache.headers, який можна використати, щоб швидко встановити заголовок Cache-Control для групи маршрутів. Директиви слід вказувати в «snake case»-еквіваленті відповідної директиви cache-control і розділяти крапкою з комою. Якщо в списку директив вказано etag, MD5-хеш вмісту відповіді буде автоматично встановлено як ідентифікатор ETag:
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 або withoutCookies вихідної відповіді:
return response('Hello World')->withoutCookie('name');
return response('Hello World')->withoutCookies([
'name',
'email',
'preferences',
]);
Якщо у вас ще немає екземпляра вихідної відповіді, ви можете скористатися методом expire фасаду Cookie, щоб завершити дію 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, які містять належні заголовки, потрібні для перенаправлення користувача на іншу 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', 'Profile updated!');
});
Після перенаправлення користувача ви можете відобразити записане повідомлення із сесії. Наприклад, використовуючи синтаксис Blade:
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
Перенаправлення з даними введення
Ви можете скористатися методом withInput, який надає екземпляр RedirectResponse, щоб записати дані введення поточного запиту в сесію перед перенаправленням користувача на нове місце. Зазвичай це робиться, якщо користувач натрапив на помилку валідації. Щойно дані введення записані в сесію, ви можете легко отримати їх під час наступного запиту, щоб повторно заповнити форму:
return back()->withInput();
Інші типи відповідей
Хелпер response може використовуватися для генерації інших типів екземплярів відповідей. Коли хелпер response викликається без аргументів, повертається реалізація контракту Illuminate\Contracts\Routing\ResponseFactory. Цей контракт надає кілька корисних методів для генерації відповідей.
Відповіді-представлення
Якщо вам потрібен контроль над статусом і заголовками відповіді, але водночас потрібно повернути представлення (view) як вміст відповіді, вам слід скористатися методом 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); // Імітуємо затримку між частинами...
}
}, 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:
# React
npm install @laravel/stream-react
# Vue
npm install @laravel/stream-vue
# Svelte
npm install @laravel/stream-svelte
Далі можна використати useStream для споживання потоку подій. Після того, як ви надасте URL свого потоку, хук автоматично оновлюватиме data конкатенованою відповіддю в міру того, як вміст повертається з вашого застосунку Laravel:
// React
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>
);
}
<!-- Vue -->
<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>
<!-- Svelte -->
<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-токен. Найпростіший спосіб надати CSRF-токен — включити його через meta-тег у head макета вашого застосунку.
Другий аргумент, що передається в useStream, — це обʼєкт опцій, за допомогою якого ви можете налаштувати поведінку споживання потоку. Значення за замовчуванням для цього обʼєкта показано нижче:
// React
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>;
}
<!-- Vue -->
<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>
<!-- Svelte -->
<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 викликається, коли потік завершився, а також коли під час циклу fetch / read виникає помилка.
За замовчуванням під час ініціалізації запит до потоку не робиться. Ви можете передати початкове корисне навантаження в потік за допомогою опції initialInput:
// React
import { useStream } from "@laravel/stream-react";
function App() {
const { data } = useStream("chat", {
initialInput: {
message: "Introduce yourself.",
},
});
return <div>{data}</div>;
}
<!-- Vue -->
<script setup lang="ts">
import { useStream } from "@laravel/stream-vue";
const { data } = useStream("chat", {
initialInput: {
message: "Introduce yourself.",
},
});
</script>
<template>
<div>{{ data }}</div>
</template>
<!-- Svelte -->
<script>
import { useStream } from "@laravel/stream-svelte";
const stream = useStream("chat", {
initialInput: {
message: "Introduce yourself.",
},
});
</script>
<div>{$stream.data}</div>
Щоб скасувати потік вручну, ви можете скористатися методом cancel, який повертає хук:
// React
import { useStream } from "@laravel/stream-react";
function App() {
const { data, cancel } = useStream("chat");
return (
<div>
<div>{data}</div>
<button onClick={cancel}>Cancel</button>
</div>
);
}
<!-- Vue -->
<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>
<!-- Svelte -->
<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:
// React
// 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>
);
}
<!-- Vue -->
<!-- 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>
<!-- Svelte -->
<!-- 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 після завершення передавання потоку:
// React
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>
);
}
<!-- Vue -->
<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>
<!-- Svelte -->
<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 може використовуватися для повернення потокової відповіді server-sent events (SSE) із типом вмісту text/event-stream. Метод eventStream приймає замикання, яке має yield-ити відповіді в потік у міру того, як вони стають доступними:
Route::get('/chat', function () {
return response()->eventStream(function () {
$stream = OpenAI::client()->chat()->createStreamed(...);
foreach ($stream as $response) {
yield $response->choices[0];
}
});
});
Якщо ви хочете налаштувати імʼя події, ви можете віддати (yield) екземпляр класу 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:
# React
npm install @laravel/stream-react
# Vue
npm install @laravel/stream-vue
# Svelte
npm install @laravel/stream-svelte
Далі можна використати useEventStream для споживання потоку подій. Після того, як ви надасте URL свого потоку, хук автоматично оновлюватиме message конкатенованою відповіддю в міру того, як повідомлення повертаються з вашого застосунку Laravel:
// React
import { useEventStream } from "@laravel/stream-react";
function App() {
const { message } = useEventStream("/chat");
return <div>{message}</div>;
}
<!-- Vue -->
<script setup lang="ts">
import { useEventStream } from "@laravel/stream-vue";
const { message } = useEventStream("/chat");
</script>
<template>
<div>{{ message }}</div>
</template>
<!-- Svelte -->
<script>
import { useEventStream } from "@laravel/stream-svelte";
const eventStream = useEventStream("/chat");
</script>
<div>{$eventStream.message}</div>
Другий аргумент, що передається в useEventStream, — це обʼєкт опцій, за допомогою якого ви можете налаштувати поведінку споживання потоку. Значення за замовчуванням для цього обʼєкта показано нижче:
// React
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>;
}
<!-- Vue -->
<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>
<!-- Svelte -->
<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
{
/**
* Ініціалізація будь-яких сервісів застосунку.
*/
public function boot(): void
{
Response::macro('caps', function (string $value) {
return Response::make(strtoupper($value));
});
}
}
Функція macro приймає імʼя першим аргументом і замикання другим аргументом. Замикання макроса буде виконано під час виклику імені макроса з реалізації ResponseFactory або з хелпера response:
return response()->caps('foo');
Виправити терміни, дописати розділ або взяти нову главу може кожен. Термінологію узгоджуємо в глосарії, щоб переклад лишався однорідним.