Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

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>

Як це працює:

  1. перше завантаження - звичайний HTML-відгук: кореневий шаблон з даними сторінки в JSON;
  2. переходи через <Link> - запит XHR із заголовком X-Inertia. Сервер бачить його й повертає JSON - назву компонента й props;
  3. Inertia підміняє компонент сторінки без перезавантаження.

Чого в Inertia немає й не треба: клієнтського роутера (Vue Router), API для сторінок, сторів для серверних даних (дані приходять props).

Коли Inertia добре підходить: застосунок і фронтенд роблять одна команда, все в одному репозиторії Laravel - адмінки, SaaS, кабінети.

Коли ні: API потрібен ще й для мобільного застосунку чи сторонніх клієнтів; фронтенд і бекенд розробляють і деплоять незалежно.

Inertia 3 (актуальна версія) прибрала залежність від Axios (власний HTTP-клієнт), спростила SSR у розробці й вимагає Laravel 11+ та PHP 8.2+. Стартові набори Laravel з Vue побудовані саме на Inertia.

Докладніше в документації: 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').

Докладніше в документації: Inertia: відповіді

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.

Докладніше в документації: Inertia: форми

Звичайний <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".

Докладніше в документації: Inertia: посилання