Продвинутые темы
Контейнер вглубь, макросы, pipelines, broadcasting, архитектура приложения.
Middle Laravel 11/12/13: что важно знать о современной структуре приложения?
С Laravel 11 изменился «скелет» — знать обязательно, чтобы не путаться:
bootstrap/app.php— единая точка конфигурации: роуты, middleware, исключения, консоль. ФайлыHttp/Kernel.phpиConsole/Kernel.phpупразднены:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php')
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([...]);
$middleware->validateCsrfTokens(except: ['webhooks/*']);
})
->withExceptions(function (Exceptions $exceptions) { ... })
->create();
config/*.phpопциональны — дефолты вшиты, публикуютсяphp artisan config:publish;- нет
app/Http/Middleware/*из коробки — свои добавляются черезwithMiddleware; - планировщик —
routes/console.php(Schedule::), а неConsole/Kernel; Http/Controllers/Controllerбольше не содержит трейтов авторизации/валидации — используют$request->validate(),Gate::authorize()илиauthorize()с трейтом;- минимальная версия PHP 8.2+ (Laravel 11), 8.3 (Laravel 12), 8.4 (Laravel 13);
- Laravel 11+ : Reverb (websockets), Prompts,
php artisan about, per-second rate limiting; 12 —#[Scoped],ConsecutiveMigrationNames; стриминг-ответыStreamedJsonResponseи др.
Senior Contextual binding, tagged services, extend: контейнер за пределами bind/singleton.
Contextual binding — разные реализации одного интерфейса для разных потребителей:
$this->app->when(ReportGenerator::class)
->needs(Filesystem::class)
->give(fn () => Storage::disk('s3'));
$this->app->when(ImportCommand::class)
->needs('$chunkSize') // скаляры тоже
->give(1000);
Tagged — собрать группу реализаций:
$this->app->tag([CsvExporter::class, PdfExporter::class], 'exporters');
$this->app->bind(ExportManager::class, fn ($app) => new ExportManager($app->tagged('exporters')));
extend() — декорировать существующий биндинг:
$this->app->extend(Service::class, fn ($service, $app) => new LoggingDecorator($service));
Прочее: bindMethod() (вызов методов через app()->call), resolving-события ($app->resolving(...)), scoped() (синглтон на время запроса/jobs — важно для Octane и воркеров!), rebinding. Атрибут #[Scoped]/#[Singleton] на классе (Laravel 12+).
Senior Что такое макросы, миксины и где их уместно применять?
Макросы добавляют методы «магическим» классам фреймворка (Macroable-трейт: Collection, Str, Request, Response, Builder, Route...) без наследования:
// в AppServiceProvider::boot()
Str::macro('isSlug', fn (string $v) => preg_match('/^[a-z0-9-]+$/', $v) === 1);
Str::isSlug('hello-world'); // true
Миксин — пакетная регистрация группы макросов из класса:
Collection::mixin(new CollectionMacros);
// методы класса возвращают замыкания — они и становятся макросами
Свои «macroable» классы:
class Money { use Macroable; }
Money::macro('format', fn () => number_format($this->amount / 100, 2));
Когда уместно: инфраструктурные утилиты, пакеты (расширение чужих классов). Когда нет: бизнес-логика — её место в сервисах/моделях; макросы скрывают зависимости и ломают автодополнение IDE (лечится _ide_helper/докблоками @mixin).
Senior Pipeline: как пропустить объект через цепочку обработчиков?
Illuminate\Pipeline\Pipeline — паттерн «цепочка обработки» (на нём построены middleware):
$result = app(Pipeline::class)
->send($order)
->through([
ValidateStock::class,
ApplyDiscounts::class,
CalculateTotals::class,
ReserveItems::class,
])
->via('handle') // имя метода (по умолчанию handle)
->then(fn ($order) => $order); // финальное действие
Каждый шаг:
class ApplyDiscounts
{
public function handle(Order $order, Closure $next): Order
{
$order->total -= $order->discount;
return $next($order); // можно изменить объект до/после следующего шага
}
}
Применение: последовательные трансформации (импорт, биллинг, фильтры каталога), когда if/elseif разрастается. Шаги легко добавлять/переставлять/тестировать по одному.
Классика собеседования: «middleware — это частный случай Pipeline, где объект — Request, а финал — контроллер».
Senior Как работает Broadcasting (WebSockets) в Laravel?
Broadcasting доставляет события клиентам в реальном времени: событие → драйвер (Pusher / Ably / Reverb / soketi / Redis+Socket.io) → WebSocket → браузер (Laravel Echo).
class OrderShipped implements ShouldBroadcast
{
public function __construct(public Order $order) {}
public function broadcastOn(): array
{
return [new PrivateChannel("orders.{$this->order->id}")];
}
// broadcastAs() — имя события на клиенте; broadcastWith() — payload
}
Типы каналов:
Channel— публичный, все подписчики;PrivateChannel— авторизация через/broadcasting/auth:Broadcast::channel('orders.{orderId}', fn ($user, $orderId) => $user->id === Order::find($orderId)->user_id);PresenceChannel— как private + «кто онлайн».
Клиент (Echo):
Echo.private(`orders.${id}`).listen('OrderShipped', (e) => ...);
Laravel Reverb — собственный WebSocket-сервер на PHP (Laravel 11+), замена Pusher/soketi для self-hosted. Broadcast-события ставят в очередь (ShouldBroadcastNow — синхронно).
Senior Как организовать архитектуру большого Laravel-приложения? Контроллеры, сервисы, actions.
Проблема роста: «толстые» контроллеры и модели. Типовые слои:
- Контроллер — только HTTP: валидация (FormRequest) → вызов слоя бизнес-логики → ответ (Resource).
- Actions / Services — бизнес-операции:
class CreateOrderAction
{
public function execute(User $user, array $items): Order
{
return DB::transaction(function () use ($user, $items) {
$order = Order::create([...]);
event(new OrderCreated($order));
return $order;
});
}
}
- Доменные объекты — DTO (
spatie/laravel-data), enum'ы PHP 8.1+ для статусов. - Repositories — спорно в Laravel (Eloquent уже абстракция); оправданы при смене источника данных или сложных выборках.
- Реакции на события, jobs — горизонтальное расширение без связанности.
Принципы: тонкие контроллеры, зависимости через конструктор и интерфейсы (контейнер), транзакции на уровне use-case, тесты на actions, а не только HTTP.
Термины на собеседовании: «single action controllers» (__invoke), CQRS-подход (разделение команд и запросов), модульность (nwidart/laravel-modules или доменная структура app/Domain/...).