Laravel and PHP Interview Questions: Top 25 in 2026
The 25 most common Laravel and PHP interview questions. Eloquent ORM, middleware, artisan, queues, tests and architecture with detailed answers and code examples.

Laravel interviews assess proficiency with PHP's most popular framework, understanding of the Eloquent ORM, MVC architecture, and the ability to build robust, maintainable applications. This guide covers the 25 most frequently asked questions, from Laravel fundamentals to advanced production deployment patterns.
Interviewers appreciate candidates who explain Laravel's architectural choices. Understanding why the framework adopts certain conventions (Convention over Configuration, Service Container) makes a real difference in interviews.
Laravel Fundamentals
Question 1: Explain the Request Lifecycle in Laravel
The Laravel request lifecycle traverses several layers before reaching the controller. Understanding this cycle is essential for debugging and performance optimization.
// 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);The complete cycle: index.php → autoload → bootstrap → service providers → middlewares → routing → controller → response → terminate. Each step can be intercepted and customized.
Question 2: What Is the Service Container and How Does Dependency Injection Work?
The Service Container is Laravel's core. It manages class instantiation and automatically resolves dependencies through constructor injection.
// 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;
}
}
}// 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 decouples classes and facilitates unit testing through mocking.
Question 3: What Is the Difference Between Facades and Dependency Injection?
Facades provide static syntax to access container services, while dependency injection makes dependencies explicit.
// 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'));
}
}Prefer dependency injection in business classes for testability. Facades are suitable for helpers and ad-hoc code.
Question 4: How Do Service Providers Work in Laravel?
Service Providers are the central configuration point for the application. Each provider registers services, configures bindings, and initializes components.
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');
}
}Execution order: all register() methods first, then all boot() methods. This order ensures dependencies are available during boot.
Eloquent ORM
Question 5: Explain Eloquent Relationships and Their Differences
Eloquent provides several relationship types to model table associations. Each type has specific use cases.
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');
}
}Polymorphic relationships (morphOne, morphMany, morphToMany) allow a model to be associated with multiple other model types through a single relationship.
Question 6: What Is the N+1 Problem and How to Solve It with Eager Loading?
The N+1 problem occurs when a main query generates N additional queries to load relationships. It's the most common cause of slowness in Laravel applications.
// ❌ 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'];
}Use php artisan telescope:prune with Laravel Telescope to detect N+1 problems during development.
Question 7: How to Create Query Scopes and When to Use Them?
Query Scopes encapsulate reusable query conditions at the model level, making code more readable and DRY.
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();Local scopes improve readability and centralize query logic. Global scopes are suitable for multi-tenancy or soft delete.
Ready to ace your Laravel interviews?
Practice with our interactive simulators, flashcards, and technical tests.
Middleware and Routing
Question 8: How Do Middlewares Work in Laravel?
Middlewares filter incoming HTTP requests and can modify outgoing responses. Each request traverses a middleware stack.
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,
]);
});// Applying middlewares to routes
Route::middleware(['auth', 'subscription:premium'])->group(function () {
Route::get('/dashboard', DashboardController::class);
Route::resource('projects', ProjectController::class);
});Middleware order matters: they execute top-to-bottom on entry and bottom-to-top on exit.
Question 9: Explain Route Model Binding and Its Variants
Route Model Binding automatically injects Eloquent models into controllers based on URL parameters.
// 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();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();
}
}// 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 reduces boilerplate code and centralizes resolution logic.
Queues and Jobs
Question 10: How to Implement Jobs and Queues in Laravel?
Queues allow deferring heavy tasks to background execution, improving application responsiveness.
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);
}
}// 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();Start the worker with php artisan queue:work --queue=high,default to process multiple queues by priority.
Question 11: What Is the Difference Between Jobs, Events, and Listeners?
All three concepts decouple code, but with different intentions.
// 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,
]);
}
}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 favor decoupled architecture: the code that triggers the event is unaware of its consequences.
Security and Authentication
Question 12: How Does Laravel Protect Against CSRF Attacks?
Laravel automatically generates a unique CSRF token per session and verifies this token on every POST, PUT, PATCH, DELETE request.
// In Blade forms
<form method="POST" action="/profile">
@csrf {{-- Generates a hidden field with the token --}}
@method('PUT') {{-- HTTP method spoofing --}}
<input type="text" name="name" value="{{ $user->name }}">
<button type="submit">Update</button>
</form>
// For AJAX requests, the token is in the meta tag
<meta name="csrf-token" content="{{ csrf_token() }}">
// Axios configuration to automatically send the token
axios.defaults.headers.common['X-CSRF-TOKEN'] =
document.querySelector('meta[name="csrf-token"]').content;// 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
]);
});Never disable CSRF protection globally. Use exceptions only for endpoints authenticated otherwise.
Question 13: How to Implement Authentication with Laravel Sanctum?
Laravel Sanctum provides lightweight authentication for SPAs, mobile apps, and token-based APIs.
// 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;
}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 also supports cookie authentication for same-domain SPAs, providing automatic CSRF protection.
Question 14: How to Secure an API with Policies and Gates?
Policies and Gates centralize authorization logic, separating business rules from controllers.
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;
}
}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)
<a href="{{ route('articles.edit', $article) }}">Edit</a>
@endcan
// Gates for non-model authorizations
Gate::define('access-admin', function (User $user) {
return $user->role === 'admin';
});
// Usage
if (Gate::allows('access-admin')) {
// ...
}Policies are tied to a model, Gates are for general authorizations.
Validation and Forms
Question 15: How to Create Custom Validation Rules?
Laravel offers several ways to create custom validations depending on complexity and reusability requirements.
// 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) . '.');
}
}
}// 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 centralize validation, authorization, and error messages, lightening controllers.
Ready to ace your Laravel interviews?
Practice with our interactive simulators, flashcards, and technical tests.
Testing
Question 16: How to Structure Tests in Laravel?
Laravel provides PHPUnit with dedicated helpers for testing different application layers.
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);
}
}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);
}
}Separate Feature tests (HTTP, integration) from Unit tests (isolated classes with mocks).
Question 17: How to Use Factories and Seeders Effectively?
Factories generate realistic test data, Seeders populate the database.
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 allow creating variations without duplicating factory logic.
Architecture and Patterns
Question 18: How to Implement the Repository Pattern in Laravel?
The Repository Pattern abstracts data access and facilitates testing by allowing query mocking.
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;
}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
);The Repository Pattern is useful for complex applications but can be excessive for simple projects. Evaluate the cost/benefit ratio.
Question 19: How to Handle Transactions and Concurrency in Laravel?
Transactions ensure data integrity during multiple operations. Laravel simplifies their management.
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() prevents concurrent reads during the transaction. Use sparingly to avoid deadlocks.
Question 20: How to Implement Caching Effectively in Laravel?
Caching drastically improves performance by avoiding repetitive queries.
// 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));
});
}
}'stores' => [
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
],Use cache tags for efficient grouped invalidation. Watch out for cache stampedes during simultaneous expiration.
Performance and Optimization
Question 21: How to Optimize Laravel Application Performance?
Optimization covers several levels: queries, cache, configuration, and infrastructure.
$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# 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-devThese optimizations can reduce boot time by 50% or more in production.
Question 22: How to Debug and Profile a Laravel Application?
Laravel offers several tools to identify performance issues and bugs.
// 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]);'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'slack'],
'ignore_exceptions' => false,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 14,
],
],Use Telescope in development and an APM (New Relic, Datadog) in production for continuous monitoring.
Deployment and Production
Question 23: How to Handle Migrations in Production Without Downtime?
Production migrations require special attention to avoid service interruptions.
// 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();
});
}// 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
});
}The "expand-contract" strategy allows adding columns without downtime: add nullable → migrate data → make non-nullable.
Question 24: How to Configure Laravel for High Availability?
A high availability architecture requires separating stateless components and managing shared state.
'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'),
],
],// 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);
}
});Each instance must be stateless. Sessions, cache, and queues should use Redis or a shared store.
Question 25: What Are Laravel Deployment Best Practices?
A robust deployment combines automation, checks, and easy rollback.
# 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!"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 checklist:
- ✅
php artisan config:cache- Cache configuration - ✅
php artisan route:cache- Cache routes - ✅
php artisan view:cache- Compile views - ✅
composer install --no-dev- Production dependencies - ✅ Automated tests before deployment
- ✅ Health checks configured
- ✅ Monitoring and alerting in place
Conclusion
These 25 questions cover the essentials of Laravel and PHP interviews, from Service Container fundamentals to production deployment patterns.
Preparation checklist:
- ✅ Service Container and dependency injection
- ✅ Eloquent ORM: relationships, scopes, eager loading
- ✅ Middleware, routing, and security
- ✅ Queues, jobs, and asynchronous events
- ✅ Testing: Feature tests, Unit tests, Factories
- ✅ Advanced patterns: Repository, Transactions, Caching
- ✅ Deployment: migrations, optimization, high availability
Each question deserves deeper exploration with Laravel's official documentation. Interviewers value candidates who know the framework's subtleties and can justify their technical choices.
Start practicing!
Test your knowledge with our interview simulators and technical tests.
Tags
Share
Related articles

Eloquent ORM: Patterns and Optimizations for Laravel
Master Eloquent ORM with advanced patterns and optimization techniques. Eager loading, query scopes, accessors, mutators and performance for Laravel applications.

Laravel 11: Building a Complete Application from Scratch
Comprehensive guide to building a Laravel 11 application with authentication, REST API, Eloquent ORM, and deployment. Practical tutorial for beginners and intermediate developers.

SQL for Data Analysts: Window Functions, CTEs and Advanced Queries
Master SQL window functions (ROW_NUMBER, RANK, LAG/LEAD), Common Table Expressions, and advanced query techniques essential for data analyst interviews and daily work.