# Laravel und PHP Interviewfragen: Die Top 25 in 2026 > Die 25 haeufigsten Laravel- und PHP-Interviewfragen. Eloquent ORM, Middleware, Artisan, Queues, Tests und Architektur mit ausfuehrlichen Antworten und Codebeispielen. - Published: 2026-01-30 - Updated: 2026-04-12 - Author: SharpSkill - Tags: laravel, php, interview, eloquent, technical interview - Reading time: 22 min --- Laravel-Vorstellungsgespraeche pruefen den sicheren Umgang mit dem beliebtesten PHP-Framework, das Verstaendnis des Eloquent ORM, der MVC-Architektur und die Faehigkeit, robuste und wartbare Anwendungen zu entwickeln. Dieser Leitfaden behandelt die 25 am haeufigsten gestellten Fragen, von Laravel-Grundlagen bis hin zu fortgeschrittenen Deployment-Patterns in der Produktion. > **Interview-Tipp** > > Interviewer schaetzen Kandidaten, die Laravels architektonische Entscheidungen erlaeutern koennen. Das Verstaendnis, warum das Framework bestimmte Konventionen verfolgt (Convention over Configuration, Service Container), macht in Vorstellungsgespraechen einen echten Unterschied. ## Laravel-Grundlagen ### Frage 1: Der Request Lifecycle in Laravel Der Laravel Request Lifecycle durchlaeuft mehrere Schichten, bevor er den Controller erreicht. Das Verstaendnis dieses Zyklus ist fuer Debugging und Performance-Optimierung unverzichtbar. ```php // public/index.php // Entry point for all HTTP requests require __DIR__.'/../vendor/autoload.php'; // Load the Laravel application $app = require_once __DIR__.'/../bootstrap/app.php'; // The HTTP kernel handles the request $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); $response->send(); $kernel->terminate($request, $response); ``` Der vollstaendige Zyklus: `index.php` → Autoload → Bootstrap → Service Providers → Middlewares → Routing → Controller → Response → Terminate. Jeder Schritt kann abgefangen und angepasst werden. ### Frage 2: Was ist der Service Container und wie funktioniert Dependency Injection? Der Service Container bildet den Kern von Laravel. Er verwaltet die Instanziierung von Klassen und loest Abhaengigkeiten automatisch ueber Constructor Injection auf. ```php // app/Services/PaymentService.php // Service with automatically injected dependencies namespace App\Services; use App\Contracts\PaymentGatewayInterface; use App\Repositories\OrderRepository; use Illuminate\Support\Facades\Log; class PaymentService { public function __construct( private PaymentGatewayInterface $gateway, // Interface resolved by container private OrderRepository $orders // Concrete class auto-resolved ) {} public function processPayment(int $orderId, float $amount): bool { $order = $this->orders->find($orderId); try { $result = $this->gateway->charge($amount, $order->customer); $order->markAsPaid($result->transactionId); return true; } catch (PaymentException $e) { Log::error('Payment failed', ['order' => $orderId, 'error' => $e->getMessage()]); return false; } } } ``` ```php // app/Providers/AppServiceProvider.php // Binding an interface to a concrete implementation public function register(): void { // Simple binding: new instance on each injection $this->app->bind( PaymentGatewayInterface::class, StripeGateway::class ); // Singleton: same instance shared everywhere $this->app->singleton( CacheService::class, fn($app) => new CacheService($app['config']['cache.driver']) ); } ``` Dependency Injection entkoppelt Klassen und erleichtert Unit-Tests durch Mocking. ### Frage 3: Was ist der Unterschied zwischen Facades und Dependency Injection? Facades bieten eine statische Syntax fuer den Zugriff auf Container-Services, waehrend Dependency Injection Abhaengigkeiten explizit macht. ```php // Using Facades: concise syntax but implicit dependencies use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; class ReportController extends Controller { public function generate() { // Facades: static access to services $data = Cache::remember('report_data', 3600, fn() => $this->fetchData()); Log::info('Report generated'); return view('report', compact('data')); } } // Dependency Injection: explicit and testable dependencies use Illuminate\Contracts\Cache\Repository as CacheContract; use Psr\Log\LoggerInterface; class ReportController extends Controller { public function __construct( private CacheContract $cache, private LoggerInterface $logger ) {} public function generate() { // Same functionality, explicit dependencies $data = $this->cache->remember('report_data', 3600, fn() => $this->fetchData()); $this->logger->info('Report generated'); return view('report', compact('data')); } } ``` In Business-Klassen ist Dependency Injection wegen der besseren Testbarkeit vorzuziehen. Facades eignen sich fuer Hilfsfunktionen und Ad-hoc-Code. ### Frage 4: Wie funktionieren Service Providers in Laravel? Service Providers sind der zentrale Konfigurationspunkt der Anwendung. Jeder Provider registriert Services, konfiguriert Bindings und initialisiert Komponenten. ```php // app/Providers/PaymentServiceProvider.php namespace App\Providers; use Illuminate\Support\ServiceProvider; use App\Services\PaymentService; use App\Contracts\PaymentGatewayInterface; use App\Gateways\StripeGateway; class PaymentServiceProvider extends ServiceProvider { // Register method: bindings and registrations // Don't access other services here (not yet loaded) public function register(): void { $this->app->singleton(PaymentGatewayInterface::class, function ($app) { return new StripeGateway( config('services.stripe.key'), config('services.stripe.secret') ); }); } // Boot method: initialization after all providers // Full access to all application services public function boot(): void { // Register macros, event listeners, routes, etc. $this->loadRoutesFrom(__DIR__.'/../routes/payment.php'); $this->loadViewsFrom(__DIR__.'/../resources/views', 'payment'); // Publish files for packages $this->publishes([ __DIR__.'/../config/payment.php' => config_path('payment.php'), ], 'payment-config'); } } ``` Ausfuehrungsreihenfolge: Zuerst alle `register()`-Methoden, dann alle `boot()`-Methoden. Diese Reihenfolge stellt sicher, dass Abhaengigkeiten waehrend des Boot-Vorgangs verfuegbar sind. ## Eloquent ORM ### Frage 5: Eloquent-Beziehungen und ihre Unterschiede Eloquent bietet verschiedene Beziehungstypen zur Modellierung von Tabellenverknuepfungen. Jeder Typ hat spezifische Anwendungsfaelle. ```php // app/Models/User.php class User extends Model { // A user has one profile (1:1) public function profile(): HasOne { return $this->hasOne(Profile::class); } // A user has many articles (1:N) public function articles(): HasMany { return $this->hasMany(Article::class); } // A user has many roles via pivot table (N:N) public function roles(): BelongsToMany { return $this->belongsToMany(Role::class) ->withPivot('assigned_at') // Additional pivot columns ->withTimestamps(); // created_at/updated_at on pivot } // A user has many comments through articles (HasManyThrough) public function comments(): HasManyThrough { return $this->hasManyThrough( Comment::class, // Final model Article::class // Intermediate model ); } } // app/Models/Article.php class Article extends Model { // An article belongs to a user (inverse of hasMany) public function author(): BelongsTo { return $this->belongsTo(User::class, 'user_id'); } // Polymorphic relationship: an article can have tags, like other models public function tags(): MorphToMany { return $this->morphToMany(Tag::class, 'taggable'); } } ``` Polymorphe Beziehungen (`morphOne`, `morphMany`, `morphToMany`) ermoeglichen es einem Model, ueber eine einzige Beziehung mit mehreren anderen Model-Typen verknuepft zu werden. ### Frage 6: Das N+1-Problem und Eager Loading als Loesung Das N+1-Problem entsteht, wenn eine Hauptabfrage N zusaetzliche Abfragen zum Laden von Beziehungen ausloest. Es ist die haeufigste Ursache fuer Performanceprobleme in Laravel-Anwendungen. ```php // ❌ PROBLEM: N+1 queries // 1 query for articles + 1 query PER article for the author $articles = Article::all(); foreach ($articles as $article) { echo $article->author->name; // SQL query on each iteration! } // ✅ SOLUTION 1: with() - Eager Loading // Only 2 queries (articles + users with IN clause) $articles = Article::with('author')->get(); foreach ($articles as $article) { echo $article->author->name; // Already loaded, no query } // ✅ SOLUTION 2: Nested Eager Loading // Loads articles, their authors, and author roles $articles = Article::with(['author.roles', 'comments.user'])->get(); // ✅ SOLUTION 3: Eager Loading with constraints $articles = Article::with([ 'comments' => function ($query) { $query->where('approved', true) ->orderBy('created_at', 'desc') ->limit(5); } ])->get(); // ✅ SOLUTION 4: Default Eager Loading in the model class Article extends Model { // These relationships are always loaded automatically protected $with = ['author', 'category']; } ``` Mit `php artisan telescope:prune` und Laravel Telescope lassen sich N+1-Probleme waehrend der Entwicklung erkennen. ### Frage 7: Query Scopes erstellen und einsetzen Query Scopes kapseln wiederverwendbare Abfragebedingungen auf Model-Ebene und machen den Code lesbarer und DRY. ```php // app/Models/Article.php class Article extends Model { // Global Scope: automatically applied to ALL queries protected static function booted(): void { // Excludes soft-deleted articles by default static::addGlobalScope('published', function (Builder $builder) { $builder->where('status', 'published'); }); } // Local Scope: called explicitly via scopeScopeName public function scopePopular(Builder $query, int $minViews = 1000): Builder { return $query->where('view_count', '>=', $minViews); } public function scopeByAuthor(Builder $query, User $author): Builder { return $query->where('user_id', $author->id); } public function scopeRecent(Builder $query, int $days = 7): Builder { return $query->where('created_at', '>=', now()->subDays($days)); } public function scopeWithStats(Builder $query): Builder { return $query->withCount('comments') ->withSum('reactions', 'score'); } } // Usage: fluent scope chaining $articles = Article::popular(500) ->recent(30) ->byAuthor($user) ->withStats() ->orderByDesc('comment_count') ->paginate(20); // Ignore a Global Scope $allArticles = Article::withoutGlobalScope('published')->get(); ``` Lokale Scopes verbessern die Lesbarkeit und zentralisieren die Abfragelogik. Globale Scopes eignen sich fuer Multi-Tenancy oder Soft Delete. ## Middleware und Routing ### Frage 8: Wie funktionieren Middlewares in Laravel? Middlewares filtern eingehende HTTP-Requests und koennen ausgehende Responses modifizieren. Jeder Request durchlaeuft einen Middleware-Stack. ```php // app/Http/Middleware/CheckSubscription.php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; class CheckSubscription { public function handle(Request $request, Closure $next, string $plan = 'basic'): Response { $user = $request->user(); // Check before the controller if (!$user || !$user->hasActiveSubscription($plan)) { if ($request->expectsJson()) { return response()->json(['error' => 'Subscription required'], 403); } return redirect()->route('subscription.plans'); } // Pass to next middleware or controller $response = $next($request); // Modify response after the controller $response->headers->set('X-Subscription-Plan', $user->subscription->plan); return $response; } } // bootstrap/app.php (Laravel 11+) return Application::configure(basePath: dirname(__DIR__)) ->withMiddleware(function (Middleware $middleware) { // Global middleware (all requests) $middleware->append(LogRequestMiddleware::class); // Aliases for use in routes $middleware->alias([ 'subscription' => CheckSubscription::class, 'role' => EnsureUserHasRole::class, ]); // Middleware groups $middleware->group('api', [ ThrottleRequests::class.':api', SubstituteBindings::class, ]); }); ``` ```php // routes/web.php // Applying middlewares to routes Route::middleware(['auth', 'subscription:premium'])->group(function () { Route::get('/dashboard', DashboardController::class); Route::resource('projects', ProjectController::class); }); ``` Die Reihenfolge der Middlewares ist entscheidend: Sie werden beim Eingang von oben nach unten und beim Ausgang von unten nach oben ausgefuehrt. ### Frage 9: Route Model Binding und seine Varianten Route Model Binding injiziert automatisch Eloquent-Models in Controller, basierend auf URL-Parametern. ```php // routes/web.php // Implicit binding: Laravel automatically resolves by ID Route::get('/articles/{article}', [ArticleController::class, 'show']); // Binding by slug instead of ID Route::get('/articles/{article:slug}', [ArticleController::class, 'show']); // Binding with relationship (automatic scope) Route::get('/users/{user}/articles/{article}', function (User $user, Article $article) { // Laravel automatically verifies that the article belongs to the user return $article; })->scopeBindings(); ``` ```php // app/Models/Article.php class Article extends Model { // Customize the default resolution key public function getRouteKeyName(): string { return 'slug'; // Resolves by slug instead of id } // Customize the resolution query public function resolveRouteBinding($value, $field = null): ?Model { return $this->where($field ?? 'slug', $value) ->where('status', 'published') ->firstOrFail(); } } ``` ```php // app/Providers/RouteServiceProvider.php // Explicit custom binding public function boot(): void { Route::bind('article', function (string $value) { return Article::where('slug', $value) ->published() ->with('author') ->firstOrFail(); }); } ``` Route Model Binding reduziert Boilerplate-Code und zentralisiert die Aufloeselogik. ## Queues und Jobs ### Frage 10: Jobs und Queues in Laravel implementieren Queues ermoeglichen es, ressourcenintensive Aufgaben in den Hintergrund zu verlagern und so die Reaktionsfaehigkeit der Anwendung zu verbessern. ```php // app/Jobs/ProcessPodcast.php namespace App\Jobs; use App\Models\Podcast; use App\Services\AudioProcessor; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class ProcessPodcast implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // Number of attempts before final failure public int $tries = 3; // Timeout in seconds public int $timeout = 600; // Delay between attempts (exponential backoff) public array $backoff = [30, 60, 120]; public function __construct( public Podcast $podcast ) {} public function handle(AudioProcessor $processor): void { // The job executes in the background $processor->transcode($this->podcast->audio_path); $processor->generateWaveform($this->podcast); $this->podcast->update(['status' => 'processed']); } // Failure handling public function failed(\Throwable $exception): void { $this->podcast->update(['status' => 'failed']); // Admin notification, logging, etc. } // Conditions for retrying the job public function retryUntil(): \DateTime { return now()->addHours(24); } } ``` ```php // Dispatching the job ProcessPodcast::dispatch($podcast); // Default queue ProcessPodcast::dispatch($podcast)->onQueue('audio'); // Specific queue ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10)); // Delay // Job chaining (sequential execution) Bus::chain([ new ProcessPodcast($podcast), new GenerateThumbnail($podcast), new NotifySubscribers($podcast), ])->dispatch(); // Job batch (parallel execution with tracking) Bus::batch([ new ProcessPodcast($podcast1), new ProcessPodcast($podcast2), new ProcessPodcast($podcast3), ])->then(function (Batch $batch) { // All jobs succeeded })->catch(function (Batch $batch, \Throwable $e) { // First failure })->finally(function (Batch $batch) { // All jobs finished (success or failure) })->dispatch(); ``` Der Worker wird mit `php artisan queue:work --queue=high,default` gestartet, um mehrere Queues nach Prioritaet abzuarbeiten. ### Frage 11: Unterschied zwischen Jobs, Events und Listeners Alle drei Konzepte entkoppeln Code, verfolgen aber unterschiedliche Absichten. ```php // Jobs: single task to execute // Used for heavy or deferred operations class SendWelcomeEmail implements ShouldQueue { public function handle(Mailer $mailer): void { $mailer->send(new WelcomeEmail($this->user)); } } // Events: notification that something happened // The event contains only data, not logic class UserRegistered { public function __construct( public User $user, public string $source ) {} } // Listeners: react to events // An event can have multiple listeners class SendWelcomeNotification implements ShouldQueue { public function handle(UserRegistered $event): void { $event->user->notify(new WelcomeNotification()); } } class TrackRegistration { public function handle(UserRegistered $event): void { Analytics::track('user_registered', [ 'user_id' => $event->user->id, 'source' => $event->source, ]); } } ``` ```php // app/Providers/EventServiceProvider.php protected $listen = [ UserRegistered::class => [ SendWelcomeNotification::class, // Queued TrackRegistration::class, // Sync CreateDefaultSettings::class, // Sync ], ]; // Triggering the event event(new UserRegistered($user, 'web')); // Or UserRegistered::dispatch($user, 'web'); ``` Events foerdern eine entkoppelte Architektur: Der Code, der das Event ausloest, kennt dessen Auswirkungen nicht. ## Sicherheit und Authentifizierung ### Frage 12: Wie schuetzt Laravel vor CSRF-Angriffen? Laravel generiert automatisch ein eindeutiges CSRF-Token pro Session und ueberprueft dieses Token bei jedem POST-, PUT-, PATCH- und DELETE-Request. ```php // In Blade forms
@csrf {{-- Generates a hidden field with the token --}} @method('PUT') {{-- HTTP method spoofing --}}
// For AJAX requests, the token is in the meta tag // Axios configuration to automatically send the token axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').content; ``` ```php // bootstrap/app.php // Exclude routes from CSRF verification (external webhooks) return Application::configure(basePath: dirname(__DIR__)) ->withMiddleware(function (Middleware $middleware) { $middleware->validateCsrfTokens(except: [ 'stripe/webhook', // Stripe webhook authenticated by signature 'api/*', // API authenticated by token ]); }); ``` Der CSRF-Schutz sollte niemals global deaktiviert werden. Ausnahmen sind nur fuer Endpunkte sinnvoll, die anderweitig authentifiziert werden. ### Frage 13: Authentifizierung mit Laravel Sanctum implementieren Laravel Sanctum bietet leichtgewichtige Authentifizierung fuer SPAs, mobile Apps und Token-basierte APIs. ```php // Installation and configuration // php artisan install:api (Laravel 11+) // app/Models/User.php use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; } ``` ```php // app/Http/Controllers/AuthController.php class AuthController extends Controller { public function login(Request $request): JsonResponse { $credentials = $request->validate([ 'email' => 'required|email', 'password' => 'required', ]); if (!Auth::attempt($credentials)) { return response()->json(['message' => 'Invalid credentials'], 401); } $user = Auth::user(); // Create a token with abilities (permissions) $token = $user->createToken('api-token', [ 'articles:read', 'articles:write', 'profile:update', ]); return response()->json([ 'user' => $user, 'token' => $token->plainTextToken, ]); } public function logout(Request $request): JsonResponse { // Revoke the current token $request->user()->currentAccessToken()->delete(); // Or revoke all tokens // $request->user()->tokens()->delete(); return response()->json(['message' => 'Logged out']); } } // routes/api.php Route::post('/login', [AuthController::class, 'login']); Route::middleware('auth:sanctum')->group(function () { Route::get('/user', fn(Request $r) => $r->user()); Route::post('/logout', [AuthController::class, 'logout']); // Ability verification Route::middleware('ability:articles:write')->group(function () { Route::post('/articles', [ArticleController::class, 'store']); }); }); ``` Sanctum unterstuetzt auch Cookie-Authentifizierung fuer Same-Domain-SPAs und bietet dabei automatischen CSRF-Schutz. ### Frage 14: API-Absicherung mit Policies und Gates Policies und Gates zentralisieren die Autorisierungslogik und trennen Geschaeftsregeln von Controllern. ```php // app/Policies/ArticlePolicy.php namespace App\Policies; use App\Models\Article; use App\Models\User; class ArticlePolicy { // Pre-check: admins have all rights public function before(User $user, string $ability): ?bool { if ($user->isAdmin()) { return true; // Allow everything } return null; // Continue to specific method } public function view(?User $user, Article $article): bool { // Published articles visible to all if ($article->status === 'published') { return true; } // Drafts visible only to author return $user?->id === $article->user_id; } public function update(User $user, Article $article): bool { return $user->id === $article->user_id; } public function delete(User $user, Article $article): bool { return $user->id === $article->user_id && $article->comments()->count() === 0; } } ``` ```php // app/Http/Controllers/ArticleController.php class ArticleController extends Controller { public function update(Request $request, Article $article) { // Check policy, throws 403 if unauthorized $this->authorize('update', $article); $article->update($request->validated()); return redirect()->route('articles.show', $article); } } // In Blade @can('update', $article) Edit @endcan // Gates for non-model authorizations Gate::define('access-admin', function (User $user) { return $user->role === 'admin'; }); // Usage if (Gate::allows('access-admin')) { // ... } ``` Policies sind an ein Model gebunden, Gates dienen allgemeinen Autorisierungen. ## Validierung und Formulare ### Frage 15: Custom Validation Rules erstellen Laravel bietet verschiedene Moeglichkeiten zur Erstellung benutzerdefinierter Validierungen, je nach Komplexitaet und Wiederverwendbarkeit. ```php // app/Rules/StrongPassword.php // Custom rule as a class (reusable) namespace App\Rules; use Closure; use Illuminate\Contracts\Validation\ValidationRule; class StrongPassword implements ValidationRule { public function validate(string $attribute, mixed $value, Closure $fail): void { $errors = []; if (strlen($value) < 12) { $errors[] = 'at least 12 characters'; } if (!preg_match('/[A-Z]/', $value)) { $errors[] = 'at least one uppercase letter'; } if (!preg_match('/[a-z]/', $value)) { $errors[] = 'at least one lowercase letter'; } if (!preg_match('/[0-9]/', $value)) { $errors[] = 'at least one digit'; } if (!preg_match('/[@$!%*?&#]/', $value)) { $errors[] = 'at least one special character'; } if (!empty($errors)) { $fail("The password must contain: " . implode(', ', $errors) . '.'); } } } ``` ```php // app/Http/Requests/RegisterRequest.php // Form Request with complex validation namespace App\Http\Requests; use App\Rules\StrongPassword; use Illuminate\Foundation\Http\FormRequest; class RegisterRequest extends FormRequest { public function authorize(): bool { return true; // Or authorization logic } public function rules(): array { return [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email', 'unique:users,email'], 'password' => ['required', 'confirmed', new StrongPassword()], 'company' => ['required_if:account_type,business', 'string'], 'vat_number' => [ 'nullable', 'string', // Closure for inline validation function ($attribute, $value, $fail) { if ($value && !$this->isValidVatNumber($value)) { $fail('The VAT number is invalid.'); } }, ], ]; } public function messages(): array { return [ 'email.unique' => 'This email address is already in use.', 'password.confirmed' => 'The passwords do not match.', ]; } protected function prepareForValidation(): void { // Normalization before validation $this->merge([ 'email' => strtolower(trim($this->email)), ]); } } ``` Form Requests zentralisieren Validierung, Autorisierung und Fehlermeldungen und entlasten damit die Controller. ## Testing ### Frage 16: Teststruktur in Laravel Laravel bietet PHPUnit mit dedizierten Hilfsfunktionen zum Testen verschiedener Anwendungsschichten. ```php // tests/Feature/ArticleControllerTest.php namespace Tests\Feature; use App\Models\Article; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class ArticleControllerTest extends TestCase { use RefreshDatabase; // Resets DB between each test public function test_guest_can_view_published_articles(): void { $article = Article::factory()->published()->create(); $response = $this->get('/articles/' . $article->slug); $response->assertStatus(200); $response->assertSee($article->title); } public function test_authenticated_user_can_create_article(): void { $user = User::factory()->create(); $response = $this->actingAs($user) ->post('/articles', [ 'title' => 'My new article', 'content' => 'Test content', ]); $response->assertRedirect(); $this->assertDatabaseHas('articles', [ 'title' => 'My new article', 'user_id' => $user->id, ]); } public function test_user_cannot_update_others_article(): void { $owner = User::factory()->create(); $other = User::factory()->create(); $article = Article::factory()->for($owner)->create(); $response = $this->actingAs($other) ->put('/articles/' . $article->id, [ 'title' => 'Modified title', ]); $response->assertStatus(403); } } ``` ```php // tests/Unit/Services/PaymentServiceTest.php namespace Tests\Unit\Services; use App\Services\PaymentService; use App\Contracts\PaymentGatewayInterface; use App\Repositories\OrderRepository; use Mockery; use Tests\TestCase; class PaymentServiceTest extends TestCase { public function test_process_payment_charges_correct_amount(): void { // Mock dependencies $gateway = Mockery::mock(PaymentGatewayInterface::class); $gateway->shouldReceive('charge') ->once() ->with(99.99, Mockery::any()) ->andReturn((object) ['transactionId' => 'tx_123']); $orders = Mockery::mock(OrderRepository::class); $orders->shouldReceive('find') ->with(1) ->andReturn($this->createOrder()); $service = new PaymentService($gateway, $orders); $result = $service->processPayment(1, 99.99); $this->assertTrue($result); } } ``` Feature-Tests (HTTP, Integration) und Unit-Tests (isolierte Klassen mit Mocks) sollten klar getrennt werden. ### Frage 17: Factories und Seeders effektiv einsetzen Factories generieren realistische Testdaten, Seeders befuellen die Datenbank. ```php // database/factories/ArticleFactory.php namespace Database\Factories; use App\Models\Article; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; class ArticleFactory extends Factory { protected $model = Article::class; public function definition(): array { return [ 'user_id' => User::factory(), 'title' => fake()->sentence(6), 'slug' => fake()->unique()->slug(), 'content' => fake()->paragraphs(5, true), 'status' => 'draft', 'view_count' => fake()->numberBetween(0, 10000), 'created_at' => fake()->dateTimeBetween('-1 year'), ]; } // States for different configurations public function published(): static { return $this->state(fn(array $attr) => [ 'status' => 'published', 'published_at' => fake()->dateTimeBetween('-6 months'), ]); } public function draft(): static { return $this->state(['status' => 'draft', 'published_at' => null]); } public function popular(): static { return $this->state(['view_count' => fake()->numberBetween(10000, 100000)]); } // Relationship configuration public function configure(): static { return $this->afterCreating(function (Article $article) { // Create tags after article creation $article->tags()->attach( \App\Models\Tag::factory()->count(3)->create() ); }); } } // Usage in tests $article = Article::factory()->published()->create(); $articles = Article::factory()->count(10)->for($user)->create(); $articleWithComments = Article::factory() ->has(Comment::factory()->count(5)) ->create(); ``` States ermoeglichen verschiedene Varianten, ohne die Factory-Logik zu duplizieren. ## Architektur und Patterns ### Frage 18: Das Repository Pattern in Laravel implementieren Das Repository Pattern abstrahiert den Datenzugriff und erleichtert das Testen durch Mocking von Abfragen. ```php // app/Contracts/ArticleRepositoryInterface.php namespace App\Contracts; use App\Models\Article; use Illuminate\Pagination\LengthAwarePaginator; interface ArticleRepositoryInterface { public function find(int $id): ?Article; public function findBySlug(string $slug): ?Article; public function getPublished(int $perPage = 20): LengthAwarePaginator; public function getByAuthor(int $userId, int $perPage = 20): LengthAwarePaginator; public function create(array $data): Article; public function update(Article $article, array $data): Article; public function delete(Article $article): bool; } ``` ```php // app/Repositories/EloquentArticleRepository.php namespace App\Repositories; use App\Contracts\ArticleRepositoryInterface; use App\Models\Article; use Illuminate\Pagination\LengthAwarePaginator; class EloquentArticleRepository implements ArticleRepositoryInterface { public function __construct( private Article $model ) {} public function find(int $id): ?Article { return $this->model->with('author')->find($id); } public function findBySlug(string $slug): ?Article { return $this->model ->where('slug', $slug) ->with(['author', 'tags']) ->firstOrFail(); } public function getPublished(int $perPage = 20): LengthAwarePaginator { return $this->model ->published() ->with('author') ->orderByDesc('published_at') ->paginate($perPage); } public function create(array $data): Article { return $this->model->create($data); } public function update(Article $article, array $data): Article { $article->update($data); return $article->fresh(); } public function delete(Article $article): bool { return $article->delete(); } } // Binding in the ServiceProvider $this->app->bind( ArticleRepositoryInterface::class, EloquentArticleRepository::class ); ``` Das Repository Pattern ist bei komplexen Anwendungen nuetzlich, kann aber bei einfachen Projekten ueberdimensioniert sein. Das Kosten-Nutzen-Verhaeltnis sollte abgewogen werden. ### Frage 19: Transaktionen und Concurrency in Laravel Transaktionen gewaehrleisten die Datenintegritaet bei mehreren Operationen. Laravel vereinfacht deren Verwaltung. ```php // app/Services/OrderService.php use Illuminate\Support\Facades\DB; class OrderService { public function processOrder(Cart $cart, User $user): Order { // Transaction with closure: automatic rollback on exception return DB::transaction(function () use ($cart, $user) { // Create the order $order = Order::create([ 'user_id' => $user->id, 'total' => $cart->total(), 'status' => 'pending', ]); // Create order items foreach ($cart->items as $item) { $order->items()->create([ 'product_id' => $item->product_id, 'quantity' => $item->quantity, 'price' => $item->product->price, ]); // Decrement stock with pessimistic locking $product = Product::lockForUpdate()->find($item->product_id); if ($product->stock < $item->quantity) { throw new InsufficientStockException($product); } $product->decrement('stock', $item->quantity); } // Clear the cart $cart->clear(); return $order; }, attempts: 3); // 3 attempts in case of deadlock } public function updateOrderStatus(Order $order, string $status): void { // Optimistic locking with version/timestamp $updated = DB::table('orders') ->where('id', $order->id) ->where('updated_at', $order->updated_at) // Version check ->update([ 'status' => $status, 'updated_at' => now(), ]); if ($updated === 0) { throw new ConcurrencyException('The order was modified in the meantime'); } } } ``` `lockForUpdate()` verhindert parallele Lesezugriffe waehrend der Transaktion. Es sollte sparsam eingesetzt werden, um Deadlocks zu vermeiden. ### Frage 20: Effektives Caching in Laravel Caching verbessert die Performance drastisch, indem repetitive Abfragen vermieden werden. ```php // Caching strategies use Illuminate\Support\Facades\Cache; class ArticleService { public function getPopularArticles(): Collection { // Cache-Aside: check cache, otherwise load and store return Cache::remember('articles:popular', 3600, function () { return Article::published() ->popular() ->with('author') ->limit(10) ->get(); }); } public function getArticle(string $slug): Article { // Cache by dynamic key return Cache::remember("article:{$slug}", 1800, function () use ($slug) { return Article::where('slug', $slug) ->with(['author', 'comments.user']) ->firstOrFail(); }); } public function updateArticle(Article $article, array $data): Article { $article->update($data); // Cache invalidation after modification Cache::forget("article:{$article->slug}"); Cache::forget('articles:popular'); // Tags for grouped invalidation (Redis only) Cache::tags(['articles', "user:{$article->user_id}"])->flush(); return $article; } public function getArticleWithLock(int $id): Article { // Atomic lock to avoid cache stampede return Cache::lock("article-lock:{$id}", 10)->block(5, function () use ($id) { return Cache::remember("article:{$id}", 3600, fn() => Article::findOrFail($id)); }); } } ``` ```php // config/cache.php - Recommended Redis configuration for production 'stores' => [ 'redis' => [ 'driver' => 'redis', 'connection' => 'cache', 'lock_connection' => 'default', ], ], ``` Cache-Tags ermoeglichen eine effiziente gruppierte Invalidierung. Bei gleichzeitigem Ablauf vieler Cache-Eintraege besteht die Gefahr eines Cache Stampede. ## Performance und Optimierung ### Frage 21: Performance einer Laravel-Anwendung optimieren Optimierung umfasst mehrere Ebenen: Abfragen, Cache, Konfiguration und Infrastruktur. ```php // 1. Eloquent query optimization $articles = Article::query() ->select(['id', 'title', 'slug', 'published_at', 'user_id']) // Specific columns ->with(['author:id,name,avatar']) // Selective eager loading ->withCount('comments') // Count in one query ->published() ->latest('published_at') ->cursorPaginate(20); // Cursor pagination (more performant) // 2. Chunking for mass operations Article::query() ->where('status', 'published') ->chunkById(1000, function ($articles) { foreach ($articles as $article) { // Processing in batches of 1000 ProcessArticle::dispatch($article); } }); // 3. Mass update without models Article::where('published_at', '<', now()->subYear()) ->update(['status' => 'archived']); // Single SQL query ``` ```bash # Optimization commands for production php artisan config:cache # Cache configuration php artisan route:cache # Cache routes php artisan view:cache # Compile Blade views php artisan event:cache # Cache event mappings php artisan optimize # Run all optimizations # Optimized autoloader composer install --optimize-autoloader --no-dev ``` Diese Optimierungen koennen die Boot-Zeit in der Produktion um 50 % oder mehr reduzieren. ### Frage 22: Debugging und Profiling einer Laravel-Anwendung Laravel bietet verschiedene Werkzeuge zur Identifizierung von Performance-Problemen und Bugs. ```php // Laravel Telescope for development debugging // Captures requests, jobs, exceptions, etc. // Query debugging DB::enableQueryLog(); $articles = Article::with('author')->get(); $queries = DB::getQueryLog(); dump($queries); // Shows all SQL queries // Debug bar integrated with Blade @dump($variable) // Display and continue @dd($variable) // Dump and die // Structured logging use Illuminate\Support\Facades\Log; Log::channel('slack')->critical('Payment failed', [ 'user_id' => $user->id, 'amount' => $amount, 'error' => $exception->getMessage(), 'trace' => $exception->getTraceAsString(), ]); // Context logging Log::withContext(['request_id' => request()->id()]); Log::info('Processing order', ['order_id' => $order->id]); ``` ```php // config/logging.php - Channel configuration 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['daily', 'slack'], 'ignore_exceptions' => false, ], 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', 'days' => 14, ], ], ``` Telescope eignet sich fuer die Entwicklung, ein APM-Tool (New Relic, Datadog) fuer das kontinuierliche Monitoring in der Produktion. ## Deployment und Produktion ### Frage 23: Migrationen in der Produktion ohne Downtime Produktions-Migrationen erfordern besondere Sorgfalt, um Serviceunterbrechungen zu vermeiden. ```php // database/migrations/2026_01_30_add_role_to_users_table.php // Safe migration: add a nullable column first public function up(): void { Schema::table('users', function (Blueprint $table) { // Step 1: Add nullable column $table->string('role')->nullable()->after('email'); }); } // Step 2: Data migration (separate job) // php artisan tinker // User::whereNull('role')->update(['role' => 'user']); // Step 3: Second migration for constraint public function up(): void { Schema::table('users', function (Blueprint $table) { $table->string('role')->nullable(false)->default('user')->change(); }); } ``` ```php // For column deletions (3 deployments) // Deployment 1: Stop using the column in code // Deployment 2: Delete the column Schema::table('users', function (Blueprint $table) { $table->dropColumn('deprecated_field'); }); // Migration with timeout for large tables public function up(): void { DB::statement('SET lock_timeout TO \'5s\''); Schema::table('large_table', function (Blueprint $table) { $table->index('status'); // Concurrent index if PostgreSQL }); } ``` Die Expand-Contract-Strategie ermoeglicht das Hinzufuegen von Spalten ohne Downtime: nullable hinzufuegen → Daten migrieren → non-nullable setzen. ### Frage 24: Laravel fuer Hochverfuegbarkeit konfigurieren Eine Hochverfuegbarkeitsarchitektur erfordert die Trennung zustandsloser Komponenten und die Verwaltung geteilter Zustaende. ```php // config/session.php - Sessions shared between instances 'driver' => env('SESSION_DRIVER', 'redis'), 'connection' => 'session', // config/cache.php - Shared cache 'default' => env('CACHE_DRIVER', 'redis'), // config/queue.php - Redis queues for distribution 'default' => env('QUEUE_CONNECTION', 'redis'), 'connections' => [ 'redis' => [ 'driver' => 'redis', 'connection' => 'default', 'queue' => 'default', 'retry_after' => 90, 'block_for' => null, ], ], // config/filesystems.php - S3 storage for files 'disks' => [ 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), ], ], ``` ```php // Health check endpoint for load balancer Route::get('/health', function () { try { DB::connection()->getPdo(); Cache::store('redis')->ping(); return response()->json(['status' => 'healthy']); } catch (\Exception $e) { return response()->json(['status' => 'unhealthy'], 503); } }); ``` Jede Instanz muss zustandslos sein. Sessions, Cache und Queues sollten Redis oder einen gemeinsamen Speicher nutzen. ### Frage 25: Best Practices fuer Laravel-Deployments Ein robustes Deployment kombiniert Automatisierung, Pruefungen und einfaches Rollback. ```bash # deploy.sh - Typical deployment script #!/bin/bash set -e echo "Pulling latest code..." git pull origin main echo "Installing dependencies..." composer install --no-dev --optimize-autoloader echo "Running migrations..." php artisan migrate --force echo "Caching configuration..." php artisan config:cache php artisan route:cache php artisan view:cache php artisan event:cache echo "Restarting queue workers..." php artisan queue:restart echo "Clearing old cache..." php artisan cache:clear echo "Deployment complete!" ``` ```php // .env.production - Critical variables APP_ENV=production APP_DEBUG=false APP_KEY=base64:... # Generated with php artisan key:generate LOG_CHANNEL=stack LOG_LEVEL=warning # Never expose credentials in plain text # Use secret managers (Vault, AWS Secrets Manager) ``` Deployment-Checkliste: - `php artisan config:cache` - Konfiguration cachen - `php artisan route:cache` - Routen cachen - `php artisan view:cache` - Views kompilieren - `composer install --no-dev` - Produktionsabhaengigkeiten - Automatisierte Tests vor dem Deployment - Health Checks konfiguriert - Monitoring und Alerting eingerichtet ## Fazit Diese 25 Fragen decken die wesentlichen Aspekte von Laravel- und PHP-Interviews ab, vom Service Container bis hin zu Deployment-Patterns in der Produktion. Vorbereitungs-Checkliste: - Service Container und Dependency Injection - Eloquent ORM: Beziehungen, Scopes, Eager Loading - Middleware, Routing und Sicherheit - Queues, Jobs und asynchrone Events - Testing: Feature-Tests, Unit-Tests, Factories - Erweiterte Patterns: Repository, Transaktionen, Caching - Deployment: Migrationen, Optimierung, Hochverfuegbarkeit > **Weitergehende Informationen** > > Jede Frage verdient eine tiefere Auseinandersetzung mit der offiziellen Laravel-Dokumentation. Interviewer schaetzen Kandidaten, die die Feinheiten des Frameworks kennen und ihre technischen Entscheidungen begruenden koennen. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/laravel/laravel-php-interview-questions