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

Питання

Найпопулярніші питання з реальних Laravel/PHP співбесід для всіх рівнів

121 питань

MVC (Model-View-Controller) - це архітектурний патерн, який розділяє застосунок на три шари, аби логіка обробки даних, бізнес-правила та відображення не змішувались між собою. Laravel побудований навколо MVC, тож кожному шару відповідає своя директорія.

Три складові:

  • Model - відповідає за дані та бізнес-логіку: спілкування з базою, зв'язки, валідаційні правила домену. У Laravel моделі розширюють Eloquent і лежать у app/Models.
  • View - відповідає лише за відображення. У Laravel це Blade-шаблони у resources/views; вони не повинні містити складної логіки.
  • Controller - приймає HTTP-запит, координує роботу моделей, готує дані й повертає View або відповідь. Лежать у app/Http/Controllers.

Як це працює разом: запит потрапляє на маршрут (routes/web.php), той викликає метод контролера. Контролер звертається до моделі за даними й передає їх у view:

class PostController extends Controller
{
    // Route Model Binding: $post резолвиться автоматично
    public function show(Post $post)
    {
        // контролер координує: бере дані з моделі й віддає у view
        return view('posts.show', ['post' => $post]);
    }
}

Навіщо: розділення відповідальностей спрощує тестування, підтримку й командну роботу - верстку можна змінити, не чіпаючи запити до БД, і навпаки. Laravel розширює класичний MVC сервісним контейнером, middleware та провайдерами.

Докладніше в документації: Архітектура та структура

Міграція - це контроль версій для структури бази даних. Замість того щоб писати SQL вручну й узгоджувати його між розробниками, ви описуєте схему PHP-кодом, який комітиться в git і застосовується однаково в усіх середовищах.

Міграції зберігаються в директорії database/migrations. Кожен файл - це клас із двома методами:

  • up() - описує зміни, які треба застосувати (створити таблицю, додати колонку, індекс).
  • down() - описує зворотну операцію для відкату (видалити таблицю чи колонку).

Створити міграцію:

php artisan make:migration create_posts_table

Згенерований файл заповнюють у методі up():

public function up(): void
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('body');
        $table->timestamps();
    });
}

public function down(): void
{
    Schema::dropIfExists('posts');
}

Основні команди:

  • php artisan migrate - застосувати нові міграції.
  • php artisan migrate:rollback - відкотити останній «батч».
  • php artisan migrate:fresh --seed - перестворити всі таблиці й засіяти даними.

Навіщо: будь-який розробник на новій машині отримує ідентичну схему БД однією командою, а зміни структури проходять через code review разом із кодом.

Докладніше в документації: Міграції

Маршрутизація визначає, як застосунок відповідає на запити: вона прив'язує URL та HTTP-метод до конкретної дії - замикання або методу контролера.

Маршрути оголошуються у файлах директорії routes:

  • web.php - веб-інтерфейс із сесіями, cookie та CSRF-захистом.
  • api.php - stateless API (без сесій, з префіксом /api).
  • console.php - команди та розклад.

Laravel підтримує методи для кожного HTTP-дієслова - Route::get, post, put, patch, delete, а також Route::match([...]) і Route::any:

Route::get('/posts', [PostController::class, 'index']);
Route::post('/posts', [PostController::class, 'store']);
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');

// Групи: спільні middleware, префікс, простір імен
Route::middleware('auth')->prefix('admin')->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
});

Ключові можливості:

  • {post} - параметр маршруту; його можна одразу резолвити в модель (Route Model Binding).
  • ->name('posts.show') - іменований маршрут для генерації URL через route('posts.show', $post).
  • Route::resource(...) - одразу 7 RESTful-маршрутів для CRUD.

Переглянути всі зареєстровані маршрути: php artisan route:list.

Докладніше в документації: Маршрутизація

Eloquent - це ORM (object-relational mapping) Laravel, реалізація патерну Active Record. Кожній таблиці відповідає модель (зазвичай у app/Models), рядок таблиці - це екземпляр моделі, а робота з даними виглядає як робота зі звичайними PHP-об'єктами замість написання SQL.

Створити модель (за конвенцією однина: Post → таблиця posts):

php artisan make:model Post -mf   # одразу з міграцією та фабрикою

Базові операції (CRUD):

$post = Post::create(['title' => 'Привіт']); // create
$post = Post::find(1);                       // read
$post->update(['title' => 'Оновлено']);      // update
$post->delete();                             // delete

Post::where('is_published', true)->latest()->get();

Що дає Eloquent понад Query Builder:

  • Зв'язки - hasOne, hasMany, belongsTo, belongsToMany, поліморфні.
  • Аксесори/мутатори та касти атрибутів (наприклад, дати, enum, JSON).
  • Події моделі та обзервери (creating, saved, deleted).
  • Scopes для перевикористання умов запитів.

Eloquent побудований поверх Query Builder, тож ті самі методи (where, orderBy, join) доступні і на моделях.

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

Обидва методи виконують запит, але повертають різне:

  • get() повертає колекцію (Illuminate\Database\Eloquent\Collection) усіх відповідних моделей. Якщо нічого не знайдено - порожню колекцію, а не null. Підходить, коли треба перебрати кілька записів.
  • first() повертає одну першу модель або null, якщо нічого не знайдено. Підходить, коли очікуєш один запис.
$posts = Post::where('active', true)->get();   // Collection (0..N моделей)
$post  = Post::where('slug', $slug)->first();  // Post|null

foreach ($posts as $post) { /* ... */ }        // get() - ітеруємо
echo $post?->title;                            // first() - перевіряємо на null

Споріднені методи:

  • find($id) - пошук за первинним ключем.
  • firstOrFail() / findOrFail() - як first()/find(), але кидають ModelNotFoundException (HTTP 404), якщо запис відсутній.
  • pluck('email') - колекція значень одного стовпця.
  • value('email') - одне скалярне значення з першого рядка.

Підсумок: get() - багато рядків (колекція), first() - один рядок (модель або null).

Докладніше в документації: Eloquent: отримання моделей

Blade - це вбудований шаблонізатор Laravel. Шаблони мають розширення .blade.php, лежать у resources/views і компілюються у звичайний PHP-код, який кешується, - тож у рантаймі оверхеду майже немає. На відміну від чистого PHP, Blade дає лаконічний синтаксис і автоматичне екранування.

Основні можливості:

{{-- Вивід зі змінних (екранується автоматично) --}}
<h1>{{ $post->title }}</h1>

{{-- Директиви замість PHP-конструкцій --}}
@if ($user)
    Вітаємо, {{ $user->name }}
@endif

@foreach ($posts as $post)
    <li>{{ $post->title }}</li>
@endforeach
  • {{ $value }} - екранує вивід (захист від XSS); {!! $html !!} виводить «сирий» HTML без екранування.
  • Директиви: @if, @foreach, @forelse, @auth, @can, @csrf, @vite.
  • Наслідування шаблонів через @extends/@section/@yield - спільний layout без дублювання.
  • Компоненти <x-alert /> - багаторазові елементи UI зі слотами та props.

Навіщо: чистіший і безпечніший за вкладений PHP, з повторним використанням розмітки.

Докладніше в документації: Blade-шаблони

Файл .env лежить у корені проєкту й зберігає налаштування, специфічні для середовища, та секрети: доступи до БД, API-ключі, APP_KEY, режим APP_ENV. Ідея в тому, що той самий код працює в різних середовищах (локально, staging, продакшен) лише завдяки різним .env.

APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=mysql
DB_PASSWORD=secret
STRIPE_KEY=sk_test_...

Ключові правила:

  • .env не комітиться в git (він у .gitignore) - кожен розробник і сервер має власний. Натомість комітять .env.example як шаблон без секретів.
  • Значення зчитуються хелпером env('KEY', 'default'), але викликати env() слід лише у файлах config/.
  • У застосунку звертайтесь через config('services.stripe.key'), а не env(...) напряму: після php artisan config:cache (оптимізація на проді) виклики env() поза конфігом повертають null.

Навіщо: секрети не потрапляють у код/репозиторій, а конфігурацію легко змінювати під середовище без редагування коду.

Докладніше в документації: Конфігурація

CSRF (Cross-Site Request Forgery) - це атака, коли сторонній сайт змушує браузер автентифікованого користувача надіслати небажаний запит на ваш застосунок, використовуючи його активну сесію (наприклад, прихована форма, що переказує гроші).

Як Laravel захищає: для кожної активної сесії генерується унікальний CSRF-токен. Middleware ValidateCsrfToken перевіряє цей токен для всіх «небезпечних» методів - POST, PUT, PATCH, DELETE. Запити без валідного токена відхиляються з кодом 419.

У формах додають директиву @csrf, яка вставляє прихований інпут із токеном:

<form method="POST" action="/profile">
    @csrf
    <input name="email" type="email">
    <button>Зберегти</button>
</form>

Для AJAX токен передають у заголовку X-CSRF-TOKEN (зазвичай із <meta name="csrf-token">):

fetch('/profile', {
    method: 'POST',
    headers: { 'X-CSRF-TOKEN': token },
});

GET-запити токена не потребують (вони мають бути безпечними й не змінювати стан). Для stateless API на токенах (Sanctum) CSRF не застосовується.

Докладніше в документації: CSRF-захист

Контролер - це клас, що групує логіку обробки запитів. Створюють його генератором Artisan; файл з'являється в app/Http/Controllers.

php artisan make:controller PostController              # порожній
php artisan make:controller PostController --resource   # 7 CRUD-методів
php artisan make:controller PostController --model=Post # з type-hint моделі
php artisan make:controller Api/PostController --api    # без create/edit
php artisan make:controller PhotoController --invokable # один метод __invoke

Згенерований resource-контролер містить методи, що відповідають RESTful-конвенції:

class PostController extends Controller
{
    public function index() {}                  // GET  /posts
    public function create() {}                 // GET  /posts/create
    public function store(Request $request) {}  // POST /posts
    public function show(Post $post) {}         // GET  /posts/{post}
    public function edit(Post $post) {}         // GET  /posts/{post}/edit
    public function update(Request $request, Post $post) {} // PUT/PATCH
    public function destroy(Post $post) {}      // DELETE
}

Прапорець --resource поєднується з Route::resource('posts', PostController::class), яка реєструє всі ці маршрути одним рядком.

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

Middleware - це шар фільтрації HTTP-запитів, що «обгортає» обробку: код може виконатися до того, як запит дійде до контролера, і/або після формування відповіді. Уявіть це як серію «застав», крізь які проходить кожен запит.

Створити middleware:

php artisan make:middleware EnsureUserIsActive

Логіка - у методі handle(); ключове - викликати $next($request), щоб передати запит далі конвеєром:

public function handle(Request $request, Closure $next): Response
{
    if (! $request->user()?->is_active) {
        return redirect('login'); // перервати конвеєр
    }

    return $next($request); // пропустити далі
}

Реєстрація та призначення (у Laravel 11+ - у bootstrap/app.php), застосування до маршрутів:

Route::get('/dashboard', ...)->middleware('auth');
Route::middleware(['auth', 'verified'])->group(fn () => ...);

Типові вбудовані middleware: auth (автентифікація), throttle (обмеження частоти), verified (підтверджений email), signed (підписані URL). Middleware - основа для авторизації, логування, CORS, локалізації.

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

Обидва виводять значення в зручному вигляді через Symfony VarDumper:

  • dump($var) - друкує значення й продовжує виконання.
  • dd($var) - «dump and die»: друкує й зупиняє виконання.
dump($user);     // подивитись і йти далі
dd($request->all()); // подивитись і зупинитись

Споріднене: dump() для ланцюжків (->dump() на колекції/запиті), ray() (пакет), Log::debug() для логів замість виводу на екран.

Докладніше в документації: Хелпери (dump/dd)

Seeding - це процес наповнення бази даних початковими або тестовими даними. Laravel робить це через класи-сидери, які лежать у директорії database/seeders. Зручно для довідників (країни, ролі), демо-контенту та локальної розробки чи тестів.

Створити сидер:

php artisan make:seeder PostSeeder

Логіку вставки описують у методі run() - або через фабрики, або напряму через Query Builder:

class PostSeeder extends Seeder
{
    public function run(): void
    {
        Post::factory()->count(50)->create(); // через фабрику

        // або напряму
        DB::table('roles')->insert([
            ['name' => 'admin'],
            ['name' => 'user'],
        ]);
    }
}

Реєстрація: сидери викликають у DatabaseSeeder::run(), щоб запускати їх разом:

public function run(): void
{
    $this->call([RoleSeeder::class, PostSeeder::class]);
}

Запуск:

  • php artisan db:seed - виконує DatabaseSeeder.
  • php artisan db:seed --class=PostSeeder - конкретний сидер.
  • php artisan migrate:fresh --seed - перестворити БД і засіяти.

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

Валідація перевіряє вхідні дані за набором правил перш ніж їх використати. Laravel має багату систему правил і кілька способів валідації.

1. Метод validate() прямо в контролері (найпростіше):

$validated = $request->validate([
    'title' => ['required', 'string', 'max:255'],
    'email' => ['required', 'email', 'unique:users,email'],
    'age'   => ['nullable', 'integer', 'min:18'],
]);

Якщо перевірка не пройдена, Laravel автоматично:

  • для веб-запитів - робить редирект назад зі старими даними та помилками в сесії (доступні через $errors у Blade);
  • для API (запит очікує JSON) - повертає відповідь 422 зі структурою { "message": ..., "errors": {...} }.

2. FormRequest - для складнішої логіки правила й авторизацію виносять в окремий клас:

php artisan make:request StorePostRequest
public function rules(): array
{
    return ['title' => ['required', 'max:255']];
}

Це розвантажує контролер. Є десятки вбудованих правил (required, email, unique, exists, confirmed, date), власні правила та умовна валідація.

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

Mass Assignment - це присвоєння групи атрибутів моделі з масиву (наприклад, Model::create($request->all())). Вразливість виникає, коли користувач підкидає неочікувані поля (скажімо, is_admin).

Захист - білий або чорний список у моделі:

protected $fillable = ['title', 'body']; // дозволено лише ці
// або
protected $guarded = ['id', 'is_admin']; // заборонено ці

Найкраща практика: не передавати $request->all(), а валідувати й передавати $request->validated().

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

Eloquent підтримує всі поширені типи зв'язків між таблицями, кожен оголошується методом на моделі:

  • One To One - hasOne / belongsTo. Приклад: UserProfile.
  • One To Many - hasMany / belongsTo. Приклад: Post → багато Comment.
  • Many To Many - belongsToMany через проміжну (pivot) таблицю. Приклад: UserRole.
  • Has One/Many Through - доступ до віддаленого зв'язку через проміжну модель.
  • Polymorphic - morphTo / morphMany: модель належить кільком типам (наприклад, Comment може належати і Post, і Video).

Оголошення зв'язку:

class Post extends Model
{
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }
}

class Comment extends Model
{
    public function post(): BelongsTo
    {
        return $this->belongsTo(Post::class);
    }
}

Використання:

$post->comments;               // колекція коментарів
$comment->post->title;         // зворотний бік
Post::with('comments')->get(); // eager loading проти N+1

Завжди завантажуйте потрібні зв'язки через with(), щоб уникнути проблеми N+1.

Докладніше в документації: Зв’язки Eloquent