# Laravel Lösungen: Fortgeschrittene Patterns, Debugging und Interview-Fragen 2026 > Fortgeschrittene Laravel-Lösungen mit Service-Klassen, Repository-Pattern, Telescope-Debugging und Interview-Fragen für erfahrene PHP-Entwickler. - Published: 2026-08-25 - Updated: 2026-08-25 - Author: Anthony Fillion-Maillet - Tags: laravel, php, debugging, design-patterns, interview - Reading time: 12 min --- Laravel-Lösungen für Produktionsanwendungen erfordern mehr als grundlegende CRUD-Operationen. Der Unterschied zwischen Junior- und Senior-Laravel-Entwicklern zeigt sich in der Codestrukturierung, dem Debugging von Problemen und den Antworten auf architektonische Fragen in Vorstellungsgesprächen. > **Worauf Interviewer achten** > > Senior-Laravel-Positionen erwarten, dass Kandidaten den Service Container erklären, Debugging-Workflows mit Telescope oder Debugbar demonstrieren und begründen können, wann Patterns wie Repository vs. direkte Eloquent-Queries angewendet werden sollten. ## Service-Klassen: Geschäftslogik aus Controllern extrahieren Controller in Laravel behandeln HTTP-Aspekte: Anfragen empfangen, Eingaben validieren und Antworten zurückgeben. Geschäftslogik gehört in dedizierte Service-Klassen, was den Code testbar und wiederverwendbar über Controller, Commands und Jobs hinweg macht. ```php // app/Services/OrderService.php // Service class encapsulating order business logic namespace App\Services; use App\Models\Order; use App\Models\User; use App\Exceptions\InsufficientStockException; use App\Jobs\SendOrderConfirmationEmail; use Illuminate\Support\Facades\DB; class OrderService { public function __construct( private InventoryService $inventory, private PaymentService $payment ) {} /** * Create an order with full validation and side effects * * @throws InsufficientStockException */ public function createOrder(User $user, array $items, string $paymentMethod): Order { // Validate stock availability before starting transaction foreach ($items as $item) { if (!$this->inventory->hasStock($item['product_id'], $item['quantity'])) { throw new InsufficientStockException($item['product_id']); } } return DB::transaction(function () use ($user, $items, $paymentMethod) { // Create order record $order = $user->orders()->create([ 'status' => 'pending', 'total' => $this->calculateTotal($items), ]); // Attach order items foreach ($items as $item) { $order->items()->create($item); $this->inventory->decrementStock($item['product_id'], $item['quantity']); } // Process payment $this->payment->charge($user, $order->total, $paymentMethod); $order->update(['status' => 'paid']); // Queue confirmation email SendOrderConfirmationEmail::dispatch($order); return $order; }); } private function calculateTotal(array $items): int { return collect($items)->sum(fn($item) => $item['price'] * $item['quantity']); } } ``` ```php // app/Http/Controllers/OrderController.php // Controller delegating to service class namespace App\Http\Controllers; use App\Http\Requests\CreateOrderRequest; use App\Services\OrderService; use Illuminate\Http\JsonResponse; class OrderController extends Controller { public function __construct( private OrderService $orderService ) {} public function store(CreateOrderRequest $request): JsonResponse { $order = $this->orderService->createOrder( $request->user(), $request->validated('items'), $request->validated('payment_method') ); return response()->json(['order_id' => $order->id], 201); } } ``` Service-Klassen können überall in Laravel injiziert werden: Controller, Commands, andere Services oder Job-Klassen. Die [Service Container Dokumentation](https://laravel.com/docs/11.x/container) behandelt automatische Auflösung und Binding. ## Repository Pattern: Wenn direktes Eloquent nicht ausreicht Das Repository-Pattern fügt eine Abstraktionsschicht zwischen Geschäftslogik und Datenzugriff hinzu. Dieses Pattern erweist sich als wertvoll, wenn Anwendungen Datenquellen wechseln, Query-Ergebnisse transparent cachen oder komplexe Query-Logik isolieren müssen. > **Interview-Kontext** > > Kandidaten werden oft gefragt, ob sie das Repository-Pattern mit Laravel verwenden. Die richtige Antwort hängt vom Kontext ab: Repositories bieten Mehrwert für große Anwendungen mit komplexen Datenanforderungen, erzeugen aber unnötige Abstraktion für einfache CRUD-Anwendungen. ```php // app/Repositories/Contracts/UserRepositoryInterface.php // Interface defining the contract namespace App\Repositories\Contracts; use App\Models\User; use Illuminate\Support\Collection; interface UserRepositoryInterface { public function findById(int $id): ?User; public function findByEmail(string $email): ?User; public function getActiveUsers(): Collection; public function getUsersWithRecentOrders(int $days = 30): Collection; public function create(array $data): User; public function update(User $user, array $data): User; } ``` ```php // app/Repositories/EloquentUserRepository.php // Eloquent implementation of the repository namespace App\Repositories; use App\Models\User; use App\Repositories\Contracts\UserRepositoryInterface; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; class EloquentUserRepository implements UserRepositoryInterface { public function findById(int $id): ?User { return Cache::remember( "user.{$id}", now()->addMinutes(10), fn() => User::find($id) ); } public function findByEmail(string $email): ?User { return User::where('email', $email)->first(); } public function getActiveUsers(): Collection { return User::where('status', 'active') ->orderBy('name') ->get(); } public function getUsersWithRecentOrders(int $days = 30): Collection { return User::whereHas('orders', function ($query) use ($days) { $query->where('created_at', '>=', now()->subDays($days)); }) ->with(['orders' => fn($q) => $q->latest()->limit(5)]) ->get(); } public function create(array $data): User { $user = User::create($data); Cache::forget("user.{$user->id}"); return $user; } public function update(User $user, array $data): User { $user->update($data); Cache::forget("user.{$user->id}"); return $user->fresh(); } } ``` ```php // app/Providers/RepositoryServiceProvider.php // Binding interface to implementation namespace App\Providers; use App\Repositories\Contracts\UserRepositoryInterface; use App\Repositories\EloquentUserRepository; use Illuminate\Support\ServiceProvider; class RepositoryServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind( UserRepositoryInterface::class, EloquentUserRepository::class ); } } ``` Die Repository-Implementierung behandelt Caching intern und hält Service-Klassen auf Geschäftsregeln fokussiert, anstatt sich um Cache-Invalidierung zu kümmern. ## Laravel-Anwendungen mit Telescope debuggen [Laravel Telescope](https://laravel.com/docs/11.x/telescope) bietet einen Debug-Assistenten, der Anfragen, Exceptions, Datenbankabfragen, Jobs und mehr aufzeichnet. Produktions-Debugging erfordert das Verständnis, welche Metriken wichtig sind und wie man Rauschen filtert. ```php // config/telescope.php // Telescope configuration for different environments return [ 'enabled' => env('TELESCOPE_ENABLED', true), // Only record slow queries in production 'query' => [ 'slow' => env('TELESCOPE_SLOW_QUERY_THRESHOLD', 100), // milliseconds ], // Prune old entries to manage database size 'storage' => [ 'database' => [ 'connection' => env('DB_CONNECTION', 'mysql'), 'chunk' => 1000, ], ], ]; ``` ```php // app/Providers/TelescopeServiceProvider.php // Custom filtering for Telescope entries namespace App\Providers; use Laravel\Telescope\IncomingEntry; use Laravel\Telescope\Telescope; use Laravel\Telescope\TelescopeApplicationServiceProvider; class TelescopeServiceProvider extends TelescopeApplicationServiceProvider { public function register(): void { Telescope::night(); $this->hideSensitiveRequestDetails(); // Filter out noise in production Telescope::filter(function (IncomingEntry $entry) { // Always record exceptions and slow queries if ($entry->isException()) { return true; } if ($entry->isSlowQuery()) { return true; } // Skip health check endpoints if ($entry->isRequest() && str_starts_with($entry->content['uri'] ?? '', '/health')) { return false; } // Skip static asset requests if ($entry->isRequest() && preg_match('/\.(css|js|png|jpg|svg)$/', $entry->content['uri'] ?? '')) { return false; } // Record everything in local environment if ($this->app->environment('local')) { return true; } // Production: only record failures and slow operations return $entry->isFailedRequest() || $entry->isFailedJob() || $entry->hasMonitoredTag(); }); } protected function hideSensitiveRequestDetails(): void { // Hide sensitive data from Telescope UI Telescope::hideRequestParameters(['password', 'password_confirmation', 'token']); Telescope::hideRequestHeaders(['authorization', 'cookie']); } } ``` Die Methode `isSlowQuery()` markiert Datenbankabfragen, die den konfigurierten Schwellenwert überschreiten. Die Analyse langsamer Queries deckt fehlende Indizes und N+1-Probleme auf, die auch Profiling-Tools wie [Debugbar](https://github.com/barryvdh/laravel-debugbar) erkennen. ## Häufige Interview-Fragen und starke Antworten Technische Interviews für Laravel-Positionen folgen Mustern. Die folgenden Fragen erscheinen häufig, und die Antworten demonstrieren die Tiefe, die Interviewer erwarten. ### Was ist der Service Container und warum ist er wichtig? Der Service Container ist Laravels [Dependency Injection Container](https://laravel.com/docs/11.x/container). Er verwaltet Klassenabhängigkeiten und führt Dependency Injection automatisch durch. Wenn ein Controller-Konstruktor `OrderService` als Type-Hint hat, löst der Container eine Instanz auf und injiziert sie. ```php // Automatic resolution: container reads type hints and builds dependencies public function __construct(OrderService $service) { // $service is automatically instantiated and injected } // Manual binding for interfaces or complex setup $this->app->bind(PaymentGateway::class, function ($app) { return new StripeGateway( config('services.stripe.key'), config('services.stripe.secret') ); }); // Singleton: same instance throughout the request $this->app->singleton(MetricsCollector::class, function ($app) { return new MetricsCollector( $app->make(Cache::class) ); }); ``` Der Container ermöglicht lose Kopplung: Klassen hängen von Abstraktionen (Interfaces) ab statt von konkreten Implementierungen, was Testing und das Austauschen von Implementierungen vereinfacht. ### Wie behandelt Laravel Datenbank-Transaktionen? Laravel umschließt Datenbankoperationen mit der Methode `DB::transaction()` in Transaktionen. Transaktionen gewährleisten Atomarität: Entweder sind alle Operationen erfolgreich, oder alle werden zurückgerollt. ```php // Simple transaction with automatic rollback on exception DB::transaction(function () { $user = User::create(['email' => 'test@example.com']); $user->profile()->create(['bio' => 'New user']); // If profile creation fails, user creation rolls back }); // Manual transaction control for complex flows DB::beginTransaction(); try { $order = Order::create($data); PaymentGateway::charge($order->total); DB::commit(); } catch (PaymentFailedException $e) { DB::rollBack(); throw $e; } // Nested transactions use savepoints DB::transaction(function () { User::create(['email' => 'outer@test.com']); DB::transaction(function () { // This creates a savepoint Profile::create(['user_id' => 1]); }); // Inner failure only rolls back to savepoint }); ``` Interviewer fragen oft nach Deadlocks. Die Antwort: Laravel wiederholt Transaktionen, die aufgrund von Deadlocks fehlschlagen (konfigurierbar über das zweite Argument von `DB::transaction()`). ### Middleware erklären und einen praktischen Anwendungsfall nennen [Middleware](/technologies/laravel/interview-questions/middleware) filtert HTTP-Anfragen, die in die Anwendung eingehen. Jede Middleware kann Anfragen untersuchen, modifizieren oder ablehnen, bevor sie Controller erreichen. ```php // app/Http/Middleware/EnsureTeamMember.php // Custom middleware checking team membership namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; class EnsureTeamMember { public function handle(Request $request, Closure $next, string $role = 'member'): Response { $team = $request->route('team'); if (!$request->user()->belongsToTeam($team)) { abort(403, 'You are not a member of this team.'); } if ($role === 'admin' && !$request->user()->isTeamAdmin($team)) { abort(403, 'Admin access required.'); } return $next($request); } } ``` ```php // routes/web.php // Applying middleware to routes Route::middleware(['auth', 'team.member:admin'])->group(function () { Route::get('/teams/{team}/settings', [TeamController::class, 'settings']); Route::put('/teams/{team}/settings', [TeamController::class, 'updateSettings']); }); ``` Middleware wird in Reihenfolge ausgeführt. Authentifizierungs-Middleware sollte vor Autorisierungs-Middleware laufen, und Logging-Middleware umschließt typischerweise alles. ## Das N+1-Query-Problem lösen Das N+1-Problem erzeugt eine Query für die initiale Collection plus eine Query pro Element beim Zugriff auf Beziehungen. Eine Liste von 100 Artikeln mit Autoren produziert 101 Queries statt 2. ```php // Problem: 101 queries for 100 articles $articles = Article::all(); foreach ($articles as $article) { echo $article->author->name; // Each access triggers a query } // Solution: eager load with 2 queries total $articles = Article::with('author')->get(); foreach ($articles as $article) { echo $article->author->name; // No additional queries } // Nested eager loading for complex relationships $articles = Article::with([ 'author.profile', 'comments' => fn($query) => $query->latest()->limit(5), 'comments.user', 'tags', ])->get(); ``` Laravel kann [Lazy Loading verhindern](/technologies/laravel/interview-questions/database-migrations) in der Entwicklung, um N+1-Probleme früh zu erkennen: ```php // app/Providers/AppServiceProvider.php use Illuminate\Database\Eloquent\Model; public function boot(): void { Model::preventLazyLoading(!$this->app->isProduction()); } ``` Mit dieser Einstellung wirft der Zugriff auf eine nicht geladene Beziehung in der Entwicklung eine Exception und erzwingt explizites Eager Loading. ## Action-Klassen für Einzelzweck-Operationen Action-Klassen kapseln einzelne Operationen, die von mehreren Einstiegspunkten aufgerufen werden können. Im Gegensatz zu Service-Klassen, die verwandte Methoden gruppieren, behandeln Actions eine Aufgabe mit klaren Ein- und Ausgaben. ```php // app/Actions/CreateUserAction.php // Single-purpose action class namespace App\Actions; use App\Models\User; use App\Notifications\WelcomeNotification; use Illuminate\Support\Facades\Hash; class CreateUserAction { public function execute(array $data): User { $user = User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); $user->notify(new WelcomeNotification()); return $user; } } ``` ```php // Usage in controller public function store(RegisterRequest $request, CreateUserAction $action) { $user = $action->execute($request->validated()); return redirect()->route('dashboard'); } // Usage in command public function handle(CreateUserAction $action) { $user = $action->execute([ 'name' => $this->argument('name'), 'email' => $this->argument('email'), 'password' => $this->argument('password'), ]); $this->info("Created user: {$user->id}"); } // Usage in test public function test_user_creation(): void { $action = new CreateUserAction(); $user = $action->execute([ 'name' => 'Test User', 'email' => 'test@example.com', 'password' => 'password', ]); $this->assertDatabaseHas('users', ['email' => 'test@example.com']); } ``` Action-Klassen funktionieren gut mit Laravels [Job-System](/technologies/laravel/interview-questions/queues-jobs): Die Action enthält die Logik, und der Job behandelt Queueing und Retry-Verhalten. ## Event-getriebene Architektur mit Events und Listeners Events entkoppeln den Moment, in dem etwas passiert, von den Reaktionen auf dieses Event. Wenn eine Bestellung aufgegeben wird, wird das `OrderPlaced`-Event ausgelöst. Listeners behandeln E-Mail-Versand, Analytics-Updates und Lagerbenachrichtigungen unabhängig voneinander. ```php // app/Events/OrderPlaced.php // Event class carrying order data namespace App\Events; use App\Models\Order; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; class OrderPlaced { use Dispatchable, InteractsWithSockets, SerializesModels; public function __construct( public Order $order ) {} } ``` ```php // app/Listeners/SendOrderConfirmation.php // Queued listener for email namespace App\Listeners; use App\Events\OrderPlaced; use App\Notifications\OrderConfirmationNotification; use Illuminate\Contracts\Queue\ShouldQueue; class SendOrderConfirmation implements ShouldQueue { public function handle(OrderPlaced $event): void { $event->order->user->notify( new OrderConfirmationNotification($event->order) ); } } ``` ```php // app/Listeners/UpdateInventoryAnalytics.php // Another listener for the same event namespace App\Listeners; use App\Events\OrderPlaced; use App\Services\AnalyticsService; use Illuminate\Contracts\Queue\ShouldQueue; class UpdateInventoryAnalytics implements ShouldQueue { public function __construct( private AnalyticsService $analytics ) {} public function handle(OrderPlaced $event): void { foreach ($event->order->items as $item) { $this->analytics->recordSale( $item->product_id, $item->quantity, $event->order->created_at ); } } } ``` ```php // Dispatching the event OrderPlaced::dispatch($order); // Or using the event helper event(new OrderPlaced($order)); ``` Listeners, die `ShouldQueue` implementieren, laufen asynchron und verhindern, dass langsame Operationen die HTTP-Antwort blockieren. ## Erkenntnisse für Laravel-Entwickler - Service-Klassen extrahieren Geschäftslogik aus Controllern, verbessern die Testbarkeit und ermöglichen Wiederverwendung über HTTP-, CLI- und Queue-Kontexte hinweg - Das Repository-Pattern bietet Mehrwert, wenn Anwendungen Caching, Datenquellenabstraktion oder komplexe Query-Kapselung benötigen, erzeugt aber Overhead für einfaches CRUD - Telescope-Filter reduzieren Rauschen in der Produktion, indem nur Exceptions, langsame Queries und fehlgeschlagene Operationen aufgezeichnet werden - Der Service Container verwaltet Dependency Injection automatisch durch Type-Hints, mit expliziten Bindings für Interfaces und komplexe Setups - N+1-Probleme verschwinden mit Eager Loading via `with()`, und `Model::preventLazyLoading()` erkennt fehlende Eager Loads während der Entwicklung - Action-Klassen behandeln Einzelzweck-Operationen, die von Controllern, Commands, Tests und Jobs aufrufbar sind - Events entkoppeln "was passiert ist" von "was als nächstes passieren soll", wobei Queued Listeners Seiteneffekte asynchron behandeln - Interview-Antworten sollten Verständnis für Trade-offs demonstrieren: wann Patterns helfen versus wann sie unnötige Komplexität hinzufügen --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/laravel/laravel-solutions-advanced-patterns-debugging-interview