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

Middle: питання на співбесіді з теми «Екосистема Laravel»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

3 питання

Скоп - сутність, для якої обчислюється прапорець. За замовчуванням це автентифікований користувач, але скопом може бути будь-що: команда, організація, тенант.

Перевірка для конкретного скопу:

Feature::for($user->team)->active('billing-v2');

Визначення з урахуванням скопу:

use App\Models\Team;

Feature::define('billing-v2', function (Team $team) {
    if ($team->created_at->isAfter('2026-01-01')) {
        return true; // нові команди одразу на новому білінгу
    }

    return Lottery::odds(1 / 100); // старі - поступово
});

Чому це важливо: якщо функція змінює спільні дані команди (білінг, налаштування), прапорець на рівні користувача дасть різну поведінку для колег з однієї команди. Скоп має відповідати межі, в якій функція має бути узгодженою.

Скоп за замовчуванням можна змінити, щоб не писати for() щоразу:

Feature::resolveScopeUsing(fn ($driver) => Auth::user()?->team);

Багатші значення - прапорець може повертати не лише булеве значення:

Feature::define('purchase-button', fn (User $user) => Arr::random([
    'blue-sapphire',
    'seafoam-green',
    'tart-orange',
]));

$color = Feature::value('purchase-button');
@feature('purchase-button', 'seafoam-green')
    <!-- варіант B -->
@endfeature

Так роблять A/B-тести: кожен користувач стабільно отримує свій варіант (значення зберігається), а аналітика порівнює конверсію.

Керування вручну:

Feature::for($team)->activate('billing-v2');
Feature::deactivate('billing-v2');          // для поточного скопу
Feature::activateForEveryone('billing-v2'); // оновити всі збережені значення

Гостьовий скоп: якщо користувач не автентифікований, скоп - null. Визначення має приймати nullable-тип (?User $user), інакше Pennant поверне false, не викликаючи замикання.

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

Проблема: щоб показувати помилки під полем одразу при введенні, правила валідації часто дублюють на фронтенді (Zod, Yup, VeeValidate). Тепер є два набори правил, які розходяться: unique:users,email на фронтенді взагалі не перевірити.

Precognition дозволяє фронтенду «запитати» сервер: «чи пройшли б ці дані валідацію?» - без виконання самої дії.

Як це працює:

  1. фронтенд надсилає запит на той самий маршрут зі спеціальним заголовком Precognition: true;
  2. middleware HandlePrecognitiveRequests запускає лише middleware маршруту й валідацію (Form Request), але не викликає контролер;
  3. відповідь - 204 (усе гаразд) або 422 з помилками;
  4. фронтенд показує помилки під полями.

Бекенд:

use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;

Route::post('/users', [UserController::class, 'store'])
    ->middleware([HandlePrecognitiveRequests::class]);

Правила лишаються в одному місці - у StoreUserRequest.

Фронтенд (Vue, React чи Alpine):

<script setup>
import { useForm } from 'laravel-precognition-vue';

const form = useForm('post', '/users', { name: '', email: '' });
</script>

<template>
    <input v-model="form.email" @change="form.validate('email')" />
    <div v-if="form.invalid('email')">{{ form.errors.email }}</div>
    <button :disabled="form.processing" @click="form.submit()">Зберегти</button>
</template>

validate('email') перевіряє лише це поле - помилки інших, ще не заповнених полів не показуються.

На що зважати:

  • побічні ефекти в middleware: прекогнітивний запит проходить через middleware маршруту. Middleware, що рахує взаємодії чи пише аудит, має пропускати такі запити: $request->isPrecognitive();
  • авторизація в authorize() Form Request теж виконується - це правильно, але враховуйте вартість;
  • навантаження: кожна зміна поля - запит до сервера. Для дорогих правил (зовнішні API) варто подумати, чи потрібна жива перевірка;
  • файли: за замовчуванням файли під час прекогнітивної валідації не надсилаються, щоб не завантажувати їх повторно.

Inertia 2.3+ має вбудовану підтримку Precognition у формах, тож окремий пакет там не потрібен.

Докладніше в документації: Precognition: жива валідація

Laravel Prompts - бібліотека гарних інтерактивних полів у терміналі: текст із плейсхолдером і валідацією, вибір зі списку, пошук, мультивибір, підтвердження, прогрес-бар, таблиці. Вона вбудована в Laravel і використовується в його власних командах (make:model, install:api).

Основні промпти:

use function Laravel\Prompts\{text, select, multiselect, confirm, search, spin};

$name = text(
    label: 'Назва проєкту',
    placeholder: 'acme-shop',
    required: true,
    validate: ['name' => 'alpha_dash|max:40'],
);

$plan = select('Тариф', ['free' => 'Free', 'pro' => 'Pro'], default: 'free');

$userId = search(
    label: 'Власник',
    options: fn (string $value) => User::where('email', 'like', "{$value}%")
        ->limit(10)->pluck('email', 'id')->all(),
);

$result = spin(fn () => $this->provision($name), 'Створюю середовище...');

Валідація приймає правила Laravel чи замикання, що повертає текст помилки. Користувач бачить помилку під полем і виправляє введене, а не отримує виняток наприкінці.

Форми - кілька промптів з можливістю повернутися на попередній крок:

$data = form()
    ->text('Ім\'я', required: true, name: 'name')
    ->password('Пароль', validate: ['password' => 'min:8'], name: 'password')
    ->confirm('Надіслати запрошення?', name: 'invite')
    ->submit();

Прогрес для довгих операцій:

progress(label: 'Імпорт', steps: $rows, callback: fn ($row) => $this->import($row));

Що важливо для команд, які запускають і люди, і скрипти:

  • неінтерактивний режим: у CI чи з --no-interaction промпти не можуть питати. Значення мають приходити з аргументів і опцій, а промпт - лише запасний варіант, коли аргумент не передано. Для цього в Laravel є інтерфейс PromptsForMissingInput;
  • Windows без WSL - Prompts автоматично переходить на запасну реалізацію Symfony Console;
  • тести: $this->artisan('project:create')->expectsQuestion('Назва проєкту', 'acme')->assertSuccessful() працює і з Prompts.

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