Junior: питання на співбесіді з теми «Inertia й Laravel»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
4 питання
Inertia - «клей» між серверним фреймворком (Laravel) і фронтенд-фреймворком (Vue, React, Svelte). Вона дає досвід SPA, але без окремого API.
Класична SPA з API:
- Laravel - REST/JSON API, автентифікація токенами чи Sanctum;
- Vue - власний роутер, стор, запити до API, обробка помилок;
- дві кодові бази, дублювання валідації, контрактів, маршрутів.
Inertia:
- маршрути, контролери, middleware, авторизація, валідація - звичайні в Laravel;
- контролер замість Blade-шаблону повертає Vue-компонент сторінки з даними:
public function index()
{
return Inertia::render('Users/Index', [
'users' => User::query()->latest()->paginate(20),
]);
}
<!-- resources/js/pages/Users/Index.vue -->
<script setup>
defineProps({ users: Object });
</script>
Як це працює:
- перше завантаження - звичайний HTML-відгук: кореневий шаблон з даними сторінки в JSON;
- переходи через
<Link>- запит XHR із заголовкомX-Inertia. Сервер бачить його й повертає JSON - назву компонента й props; - Inertia підміняє компонент сторінки без перезавантаження.
Чого в Inertia немає й не треба: клієнтського роутера (Vue Router), API для сторінок, сторів для серверних даних (дані приходять props).
Коли Inertia добре підходить: застосунок і фронтенд роблять одна команда, все в одному репозиторії Laravel - адмінки, SaaS, кабінети.
Коли ні: API потрібен ще й для мобільного застосунку чи сторонніх клієнтів; фронтенд і бекенд розробляють і деплоять незалежно.
Inertia 3 (актуальна версія) прибрала залежність від Axios (власний HTTP-клієнт), спростила SSR у розробці й вимагає Laravel 11+ та PHP 8.2+. Стартові набори Laravel з Vue побудовані саме на Inertia.
Inertia::render() приймає назву компонента сторінки й масив props - даних, які отримає компонент.
use Inertia\Inertia;
public function show(Post $post)
{
return Inertia::render('Posts/Show', [
'post' => PostResource::make($post->load('author')),
'comments' => $post->comments()->latest()->limit(20)->get(['id', 'body', 'created_at']),
'canEdit' => request()->user()?->can('update', $post) ?? false,
]);
}
<script setup>
const props = defineProps({
post: Object,
comments: Array,
canEdit: Boolean,
});
</script>
<template>
<h1>{{ post.title }}</h1>
<button v-if="canEdit">Редагувати</button>
</template>
Як перетворюються дані:
- моделі й колекції - через
toArray()(з урахуванням$hiddenі$appends); - API Resources - через
toResponse(), тожPostResourceдає контрольований формат; - замикання (
fn () => ...) обчислюються лише тоді, коли prop справді потрібен (часткові перезавантаження); - назва компонента може бути backed enum - для типобезпечних посилань на сторінки.
Головне правило безпеки: усі props видно в браузері. Вони потрапляють у JSON сторінки, який будь-хто бачить у вихідному коді чи вкладці Network. Модель, передана цілком, віддасть усі свої поля: email, телефон, службові прапорці, хеш токена, якщо його забули сховати.
Тому:
- передавати лише потрібні поля:
->only(['id', 'name']),get(['id', 'title'])чи API Resource; - не покладатися на те, що «Vue це не показує» - не показаний у шаблоні prop все одно є в JSON;
- права передавати готовими булевими значеннями (
canEdit), а перевіряти - на сервері в кожній дії.
Сторінка без контролера: Route::inertia('/about', 'About').
useForm - помічник Inertia для форм: зберігає дані, стан відправки, помилки валідації й прогрес завантаження.
<script setup>
import { useForm } from '@inertiajs/vue3';
const form = useForm({
title: '',
body: '',
cover: null,
});
function submit() {
form.post('/posts', {
preserveScroll: true,
onSuccess: () => form.reset(),
});
}
</script>
<template>
<form @submit.prevent="submit">
<input v-model="form.title">
<p v-if="form.errors.title">{{ form.errors.title }}</p>
<textarea v-model="form.body" />
<input type="file" @input="form.cover = $event.target.files[0]">
<progress v-if="form.progress" :value="form.progress.percentage" max="100" />
<button :disabled="form.processing">Опублікувати</button>
</form>
</template>
Що дає useForm:
- методи відправки:
get,post,put,patch,delete; form.errors- помилки валідації Laravel заповнюються автоматично;form.processing- чи йде відправка (захист від подвійного кліку);form.isDirty- чи змінювали форму;wasSuccessful,recentlySuccessful(дві секунди після успіху - для «Збережено»);reset(),clearErrors(),resetAndClearErrors(),defaults();transform()- змінити дані перед відправкою.
Файли. Якщо в даних є файл, Inertia сама перетворить їх на FormData. Але для put/patch з файлами PHP не розбирає multipart - відправляють post з _method: 'put'.
Валідація на сервері - звичайна:
public function store(Request $request)
{
$validated = $request->validate(['title' => 'required|max:255', 'body' => 'required']);
// при помилці Laravel поверне назад, а Inertia покладе помилки у form.errors
}
Корисне: useForm('CreatePost', {...}) з ключем зберігає дані форми в історії браузера - повернувшись «Назад», користувач не втратить введене; паролі виключають через dontRemember('password').
Простіша альтернатива - компонент <Form action="/posts" method="post">, що працює як звичайна HTML-форма з полями за атрибутом name.
Звичайний <a href> завантажує сторінку повністю: HTML, CSS, JavaScript, ініціалізація Vue. <Link> з @inertiajs/vue3 перехоплює клік і робить Inertia-візит: запит повертає лише JSON з назвою компонента й props, а сторінка підміняється без перезавантаження.
<script setup>
import { Link } from '@inertiajs/vue3';
</script>
<template>
<Link href="/posts">Блог</Link>
<Link :href="`/posts/${post.id}`" prefetch>{{ post.title }}</Link>
</template>
Можливості:
- інші HTTP-методи:
<Link href="/logout" method="post" as="button">Вийти</Link>
<Link :href="`/posts/${post.id}`" method="delete" as="button">Видалити</Link>
Для не-GET методів варто рендерити кнопку (as="button"): посилання, що виконує POST, порушує очікування доступності й «відкрити в новій вкладці».
- дані запиту:
:data="{ status: 'draft' }"; preserve-scroll- не прокручувати на початок (фільтри, пагінація посеред сторінки);preserve-state- зберегти локальний стан компонента (введене в поле пошуку);replace- замінити запис в історії, а не додати;:only="['users']"- часткове перезавантаження лише потрібних props;prefetch- завантажити дані заздалегідь при наведенні (75 мс за замовчуванням),prefetch="click"- на натисканні,prefetch="mount"- одразу; кеш на 30 секунд (cache-for).
Активне посилання - порівняння з $page.url чи usePage().url.
Чого не робити:
<Link>на зовнішні сайти - для них звичайний<a>;<Link>на маршрути Laravel, що повертають не Inertia-відповідь (файл для завантаження, сторінку без Inertia) - Inertia покаже модальне вікно з HTML. Для таких адрес - звичайне посилання абоInertia::location()на сервері;- вихід з облікового запису через GET - лише
method="post".