Laravel

26 вопросов

1 Что такое жизненный цикл Laravel приложения?
  1. public/index.php - точка входа, загружает Composer autoload
  2. Создание Application instance (IoC container)
  3. HTTP Kernel получает Request
  4. Middleware pipeline (глобальные middleware)
  5. Router определяет маршрут
  6. Route middleware
  7. Controller обрабатывает запрос
  8. Response проходит обратно через middleware
  9. Ответ отправляется клиенту
  10. Terminate middleware (after response)

Service Providers загружаются при создании Application - они регистрируют сервисы, маршруты, middleware, event listeners.

Открыть отдельно →
2 Что такое Service Container?

Service Container (IoC Container) - ядро Laravel. Управляет зависимостями и их внедрением:

// Регистрация
app()->bind(PaymentGateway::class, StripeGateway::class);
app()->singleton(Logger::class, fn() => new FileLogger('/logs'));

// Auto-resolution (без явной регистрации)
class UserController {
    public function __construct(
        private UserService $service, // Laravel сам создаст
    ) {}
}

// Контекстная привязка
app()->when(PhotoController::class)
     ->needs(FileSystem::class)
     ->give(S3FileSystem::class);

Auto-resolution использует Reflection для анализа конструктора и автоматического создания зависимостей.

Открыть отдельно →
3 Что такое Service Provider?

Service Provider - центральное место для регистрации сервисов, привязок, событий, middleware:

class PaymentServiceProvider extends ServiceProvider {
    public function register(): void {
        // Привязки в контейнер
        $this->app->singleton(PaymentGateway::class, StripeGateway::class);
    }

    public function boot(): void {
        // После регистрации всех провайдеров
        // Маршруты, события, команды
        $this->loadRoutesFrom(__DIR__.'/../routes/payment.php');
        $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
    }
}

Провайдеры регистрируются в config/app.php. register() - только привязки. boot() - любая логика (все сервисы уже доступны).

Открыть отдельно →
4 Что такое Facade?

Facade - статический интерфейс к сервисам из контейнера:

// Фасад
Cache::get('key');

// Эквивалент
app('cache')->get('key');

// Под капотом:
class Cache extends Facade {
    protected static function getFacadeAccessor(): string {
        return 'cache'; // ключ в контейнере
    }
}
// __callStatic() перехватывает вызов и делегирует реальному объекту

Фасады удобны для быстрой разработки, но скрывают зависимости. Для тестируемости предпочитайте DI через конструктор. Real-time facades: use Facades\App\Services\PaymentService;

Открыть отдельно →
5 Что такое middleware?
class AuthMiddleware {
    public function handle(Request $request, Closure $next): Response {
        if (!auth()->check()) {
            return redirect('/login');
        }
        return $next($request); // передать следующему
    }

    // After middleware
    public function terminate(Request $request, Response $response): void {
        // После отправки ответа клиенту
        Log::info('Request completed', ['url' => $request->url()]);
    }
}

Типы: глобальные (каждый запрос), route middleware (для конкретных маршрутов), middleware groups (web, api). Регистрация в bootstrap/app.php (Laravel 11+).

Открыть отдельно →
6 Что такое Eloquent ORM?

Eloquent - ORM в Laravel, реализующий паттерн Active Record:

class User extends Model {
    protected $fillable = ['name', 'email'];
    protected $casts = ['email_verified_at' => 'datetime'];

    public function posts(): HasMany {
        return $this->hasMany(Post::class);
    }
}

// CRUD
$user = User::create(['name' => 'John', 'email' => 'j@test.com']);
$user = User::find(1);
$user = User::where('email', 'j@test.com')->first();
$user->update(['name' => 'Jane']);
$user->delete();

// Query Builder
User::where('age', '>', 18)->orderBy('name')->paginate(15);
Открыть отдельно →
7 Чем Active Record отличается от Data Mapper?

Active Record (Eloquent) - модель = запись в таблице. Модель содержит и данные, и логику работы с БД:

$user = User::find(1);    // модель знает как загрузить себя
$user->name = 'Jane';
$user->save();             // и как сохранить

Data Mapper (Doctrine) - модель не знает о БД. Отдельный маппер (EntityManager) управляет персистенцией:

$user = $entityManager->find(User::class, 1);
$user->setName('Jane');
$entityManager->flush();   // маппер сохраняет изменения

Active Record проще, Data Mapper - чище (модель не зависит от инфраструктуры). Для сложного домена Data Mapper предпочтительнее.

Открыть отдельно →
8 Какие типы связей есть в Eloquent?
class User extends Model {
    public function phone(): HasOne { return $this->hasOne(Phone::class); }
    public function posts(): HasMany { return $this->hasMany(Post::class); }
    public function roles(): BelongsToMany { return $this->belongsToMany(Role::class); }
}

class Post extends Model {
    public function user(): BelongsTo { return $this->belongsTo(User::class); }
    public function tags(): MorphToMany { return $this->morphToMany(Tag::class, 'taggable'); }
}

class Country extends Model {
    public function posts(): HasManyThrough {
        return $this->hasManyThrough(Post::class, User::class);
    }
}

Типы: hasOne, hasMany, belongsTo, belongsToMany (M:N через pivot), hasManyThrough, morphOne/morphMany/morphToMany (полиморфные).

Открыть отдельно →
9 Что такое eager loading? Как решить N+1?
// N+1 проблема
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name; // запрос на каждый пост!
}

// Eager loading - решение
$posts = Post::with('author')->get();            // 2 запроса
$posts = Post::with(['author', 'tags'])->get();   // 3 запроса
$posts = Post::with('author.profile')->get();     // вложенные

// Lazy eager loading (после загрузки)
$posts->load('comments');

// Подсчет без загрузки
$posts = Post::withCount('comments')->get();
echo $post->comments_count;

// Предотвращение N+1 (Laravel 11+)
Model::preventLazyLoading(); // Exception при lazy load
Открыть отдельно →
10 Что такое scope в Eloquent?
class User extends Model {
    // Local scope - вызывается явно
    public function scopeActive(Builder $query): Builder {
        return $query->where('status', 'active');
    }

    public function scopeOlderThan(Builder $query, int $age): Builder {
        return $query->where('age', '>', $age);
    }
}

// Использование
User::active()->olderThan(18)->get();

// Global scope - применяется ко всем запросам
class ActiveScope implements Scope {
    public function apply(Builder $builder, Model $model): void {
        $builder->where('is_active', true);
    }
}
// User::withoutGlobalScope(ActiveScope::class)->get();
Открыть отдельно →
11 Что такое Accessor и Mutator?
// Laravel 9+ (Attribute casting)
class User extends Model {
    protected function firstName(): Attribute {
        return Attribute::make(
            get: fn(string $value) => ucfirst($value),       // accessor
            set: fn(string $value) => strtolower($value),    // mutator
        );
    }

    // Виртуальный атрибут
    protected function fullName(): Attribute {
        return Attribute::make(
            get: fn() => "{$this->first_name} {$this->last_name}",
        );
    }

    // Casts (встроенные)
    protected $casts = [
        'options' => 'array',
        'birthday' => 'date',
        'is_admin' => 'boolean',
        'status' => StatusEnum::class,
    ];
}
Открыть отдельно →
12 Что такое soft delete?
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model {
    use SoftDeletes; // добавляет deleted_at
}

$post->delete();         // устанавливает deleted_at (не удаляет из БД)
$post->forceDelete();    // реальное удаление
$post->restore();        // восстановление

Post::all();              // без удаленных
Post::withTrashed()->get(); // все, включая удаленные
Post::onlyTrashed()->get(); // только удаленные

Миграция: $table->softDeletes(); добавляет nullable deleted_at timestamp.

Открыть отдельно →
13 Что такое миграции?
// Создание: php artisan make:migration create_users_table
class CreateUsersTable extends Migration {
    public function up(): void {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->timestamps();
            $table->softDeletes();
            $table->index(['name', 'email']);
        });
    }

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

// php artisan migrate
// php artisan migrate:rollback
// php artisan migrate:fresh --seed

Миграции - version control для базы данных. Каждая миграция выполняется один раз (трекинг в таблице migrations).

Открыть отдельно →
14 Что такое Form Request?
// php artisan make:request CreateUserRequest
class CreateUserRequest extends FormRequest {
    public function authorize(): bool {
        return $this->user()->can('create', User::class);
    }

    public function rules(): array {
        return [
            'name'  => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'age'   => 'required|integer|min:18',
        ];
    }

    public function messages(): array {
        return ['email.unique' => 'Этот email уже занят'];
    }
}

// Контроллер
public function store(CreateUserRequest $request): JsonResponse {
    $validated = $request->validated(); // уже валидировано
    $user = User::create($validated);
    return response()->json($user, 201);
}
Открыть отдельно →
15 Что такое API Resource?
class UserResource extends JsonResource {
    public function toArray(Request $request): array {
        return [
            'id'    => $this->id,
            'name'  => $this->name,
            'email' => $this->email,
            'posts' => PostResource::collection($this->whenLoaded('posts')),
            'links' => ['self' => route('users.show', $this)],
        ];
    }
}

// Использование
return new UserResource($user);
return UserResource::collection(User::paginate(15));

Resources - трансформационный слой между моделями и JSON-ответами API. Контролирует, какие данные и в каком формате отдаются клиенту.

Открыть отдельно →
16 Что такое Policy и Gate?
// Gate - простое правило авторизации
Gate::define('update-post', fn(User $user, Post $post) => $user->id === $post->user_id);

// Policy - набор правил для модели
class PostPolicy {
    public function update(User $user, Post $post): bool {
        return $user->id === $post->user_id;
    }
    public function delete(User $user, Post $post): bool {
        return $user->isAdmin() || $user->id === $post->user_id;
    }
}

// Использование
$this->authorize('update', $post);
if ($user->can('delete', $post)) { /* ... */ }
@can('update', $post) ... @endcan
Открыть отдельно →
17 Что такое Laravel Queues?
// Job
class SendEmailJob implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(public User $user) {}

    public function handle(Mailer $mailer): void {
        $mailer->send($this->user->email, new WelcomeEmail());
    }

    public function failed(Throwable $e): void {
        Log::error('Email failed', ['user' => $this->user->id]);
    }
}

// Отправка в очередь
SendEmailJob::dispatch($user);
SendEmailJob::dispatch($user)->onQueue('emails')->delay(now()->addMinutes(5));

Драйверы: database, Redis, SQS, Beanstalkd. Воркер: php artisan queue:work --queue=emails

Открыть отдельно →
18 Как запустить Job? Как обработать ошибку в Job?
// Запуск
SendEmailJob::dispatch($user);                    // async
SendEmailJob::dispatchSync($user);                // sync (для тестов)
SendEmailJob::dispatch($user)->afterCommit();     // после commit транзакции

// Обработка ошибок
class ProcessPayment implements ShouldQueue {
    public int $tries = 3;              // макс. попыток
    public int $backoff = 60;           // секунд между попытками
    public int $timeout = 120;          // таймаут выполнения
    public int $maxExceptions = 2;      // макс. исключений

    public function handle(): void { /* ... */ }

    public function failed(Throwable $e): void {
        // Вызывается после исчерпания попыток
        Notification::send($admin, new JobFailed($e));
    }

    public function retryUntil(): DateTime {
        return now()->addHours(1);
    }
}
Открыть отдельно →
19 Что такое Events и Event Listeners?
// Event
class UserRegistered {
    public function __construct(public User $user) {}
}

// Listener
class SendWelcomeEmail {
    public function handle(UserRegistered $event): void {
        Mail::to($event->user)->send(new WelcomeMail());
    }
}

// Регистрация (EventServiceProvider или атрибуты)
Event::listen(UserRegistered::class, SendWelcomeEmail::class);

// Dispatch
event(new UserRegistered($user));
UserRegistered::dispatch($user);

Events обеспечивают слабую связанность - код, создающий событие, не знает о слушателях.

Открыть отдельно →
20 Что такое Observer?
class UserObserver {
    public function creating(User $user): void {
        $user->uuid = Str::uuid();
    }
    public function created(User $user): void {
        Mail::to($user)->send(new WelcomeMail());
    }
    public function updating(User $user): void { /* ... */ }
    public function deleting(User $user): void { /* ... */ }
}

// Регистрация
User::observe(UserObserver::class);
// Или через атрибут #[ObservedBy(UserObserver::class)]

Observer реагирует на Eloquent события модели: creating, created, updating, updated, deleting, deleted, restoring, restored.

Открыть отдельно →
21 Что такое Laravel Scheduler?
// routes/console.php (Laravel 11+)
Schedule::command('reports:generate')->dailyAt('06:00');
Schedule::job(new CleanupJob)->hourly();
Schedule::call(fn() => DB::table('temp')->delete())->daily();

// Настройки
Schedule::command('emails:send')
    ->everyFiveMinutes()
    ->withoutOverlapping()      // не запускать если предыдущий не завершился
    ->onOneServer()             // только на одном сервере
    ->runInBackground()
    ->emailOutputTo('admin@test.com');

// Cron на сервере (один раз):
// * * * * * cd /app && php artisan schedule:run >> /dev/null 2>&1
Открыть отдельно →
22 Что такое Sanctum vs Passport?

Sanctum - простая аутентификация: API tokens + SPA cookie auth. Для простых приложений.

Passport - полная реализация OAuth2 сервера. Для приложений, предоставляющих API третьим сторонам.

// Sanctum: API token
$token = $user->createToken('api-token')->plainTextToken;
// Authorization: Bearer {token}

// Sanctum: SPA auth (cookie-based)
// Автоматически через middleware

Выбор: Sanctum для 90% проектов (SPA, mobile, simple API). Passport - когда нужен OAuth2 (авторизация для third-party apps).

Открыть отдельно →
23 Что такое Laravel Pipeline?
use Illuminate\Pipeline\Pipeline;

$result = app(Pipeline::class)
    ->send($order)
    ->through([
        ValidateOrder::class,
        CalculateTotal::class,
        ApplyDiscount::class,
        ProcessPayment::class,
    ])
    ->thenReturn();

// Каждый шаг
class CalculateTotal {
    public function handle(Order $order, Closure $next): mixed {
        $order->total = $order->items->sum('price');
        return $next($order);
    }
}

Pipeline = Chain of Responsibility. Laravel middleware работают именно так. Полезно для обработки заказов, ETL, конвейеров трансформации.

Открыть отдельно →
24 Что такое Notifications?
class OrderShipped extends Notification {
    public function via(User $notifiable): array {
        return ['mail', 'database', 'slack'];
    }

    public function toMail(User $notifiable): MailMessage {
        return (new MailMessage)
            ->subject('Order Shipped')
            ->line('Your order has been shipped.');
    }

    public function toDatabase(User $notifiable): array {
        return ['order_id' => $this->order->id, 'status' => 'shipped'];
    }
}

$user->notify(new OrderShipped($order));
Notification::send($users, new OrderShipped($order));

Каналы: mail, database, broadcast, Slack, SMS (Vonage), push и custom.

Открыть отдельно →
25 Что такое кеширование в Laravel?
// Базовые операции
Cache::put('key', 'value', now()->addHours(1));
$value = Cache::get('key', 'default');
Cache::forget('key');

// Remember - кеш с lazy-загрузкой
$users = Cache::remember('users', 3600, fn() => User::all());

// Tags
Cache::tags(['users', 'admins'])->put('key', $data, 600);
Cache::tags('users')->flush();

// Atomic lock
Cache::lock('processing', 10)->block(5, function () {
    // эксклюзивный доступ на 10 секунд
});

Драйверы: file, database, Redis, Memcached, DynamoDB, array (для тестов).

Открыть отдельно →
26 Куда выносить бизнес-логику из контроллера?

Контроллер должен быть "тонким" - только маршрутизация, валидация, ответ:

// 1. Service - бизнес-логика
class OrderService {
    public function create(CreateOrderDTO $dto): Order {
        // валидация, расчеты, сохранение
    }
}

// 2. Action - одно действие (Single Action Class)
class CreateOrderAction {
    public function execute(CreateOrderDTO $dto): Order { /* ... */ }
}

// 3. Repository - работа с БД
class OrderRepository {
    public function findActive(): Collection {
        return Order::where('status', 'active')->get();
    }
}

// Контроллер
class OrderController {
    public function store(CreateOrderRequest $request, CreateOrderAction $action) {
        $order = $action->execute(CreateOrderDTO::fromRequest($request));
        return new OrderResource($order);
    }
}
Открыть отдельно →
🧠Квиз 🏆Лидеры 🎯Собесед. 📖Вопросы 📚База зн.