Fundamentals
1. What happens between a request arriving and a response leaving?
public/index.php loads Composer’s autoloader and bootstraps the application from bootstrap/app.php. The HTTP kernel runs global middleware, the router matches a route and runs its middleware stack, the controller executes and returns a response, the response travels back out through the middleware in reverse, and finally terminate() runs on any terminable middleware after the response is sent.
2. What is the difference between MVC as Laravel implements it and classic MVC?
Laravel is closer to Model–View–Presenter. The controller does not observe the model; it explicitly fetches data and passes it to a view. In practice a good Laravel controller is thin — validate, delegate to a service or action class, return a response — and business logic lives outside both the controller and the model.
3. Facade vs helper vs dependency injection — which should you use?
All three end up at the same container binding. Facades (Cache::get()) are static-looking proxies resolved from the container. Helpers (cache()) are functions that do the same. Constructor injection makes the dependency explicit and is easiest to test and swap. Use injection in classes you own; facades are fine in routes, Blade and quick scripts.
4. What is the difference between .env and config/?
.env holds per-environment values and is never committed. config/ files read those values via env() and are committed. Call env() only inside config files — once config:cache has run in production, env() returns null everywhere else.
5. What does php artisan optimize do?
Caches configuration, routes, events and compiled Blade views into single files so the framework skips filesystem discovery on every request. Run it on deploy; run optimize:clear when any of those change.
Eloquent and the Database
6. Eloquent vs the Query Builder vs raw SQL?
Eloquent returns model objects with relationships, events, casts and accessors — ideal for domain logic. The query builder returns plain stdClass rows with far less overhead — ideal for reports and bulk work. Raw SQL is the escape hatch for database-specific features. Mixing them freely is normal and correct.
7. Explain the N+1 problem and three ways to fix it.
Looping over parents and touching a relationship inside the loop fires one query per parent. Fix it with with() before the loop, load() on an already-fetched collection, or withCount() when you only need a number. Prevent it permanently with Model::preventLazyLoading() in non-production environments.
8. What is the difference between hasMany and belongsToMany?
hasMany uses a foreign key on the related table — one parent, many children. belongsToMany uses a pivot table so both sides can have many of the other. The pivot can carry its own columns, exposed with withPivot().
9. What do $fillable and $guarded protect against?
Mass assignment. Without them, User::create($request->all()) would let a visitor post is_admin=1 and escalate their own privileges. $fillable is an allow list, $guarded a deny list. Prefer $fillable, and prefer passing $request->validated() so only rules-backed keys ever reach the model.
10. What is the difference between delete() and soft deletes?
delete() removes the row. With the SoftDeletes trait, it sets deleted_at and a global scope hides the row from normal queries. You then get withTrashed(), onlyTrashed(), restore() and forceDelete(). Remember that unique indexes still see soft-deleted rows.
11. Accessor, mutator, cast — what is the difference?
An accessor transforms an attribute on read, a mutator on write; since Laravel 9 both live in one Attribute method. A cast converts a column to a native type (array, boolean, datetime, an enum, or a custom cast class) in both directions and is declared in $casts. Reach for a cast first; use an accessor for computed values that are not stored.
12. What are query scopes?
Reusable query fragments. A local scope is a scopeXxx() method called as Post::published(). A global scope applies automatically to every query on the model — which is powerful and easy to forget about, so keep them few and obvious.
13. How do database transactions work in Laravel?
DB::transaction(function () {
$order = Order::create([...]);
$order->items()->createMany([...]);
Inventory::decrement(...);
}); // rolls back automatically on any exception
// Manual control
DB::beginTransaction();
try {
// ...
DB::commit();
} catch (\Throwable $e) {
DB::rollBack();
throw $e;
}Mention DB::transaction($callback, 3) for deadlock retries, and the fact that dispatching a queued job inside a transaction needs afterCommit() — interviewers like that detail.
Architecture and the Container
14. What is the service container?
A registry that resolves classes and their dependencies. It reads constructor type hints via reflection and builds the whole object graph, which is why controllers can just type-hint what they need.
15. bind() vs singleton()?
bind() runs the resolver every time; singleton() caches the first instance for the life of the request. Use singleton() for expensive stateless objects like clients and connections, and scoped() instead if you are running Octane and the object holds request state.
16. Difference between register() and boot() in a service provider?
Laravel calls register() on every provider before calling boot() on any of them. So register() may only add bindings; anything that uses a binding — routes, events, Blade directives, validators, policies — belongs in boot().
17. What is a contract in Laravel?
An interface in Illuminate\Contracts that a core service implements. Type-hinting the contract rather than the facade or concrete class keeps your code swappable and easy to fake in tests.
18. When would you use a repository pattern with Eloquent?
Honestly — rarely. Eloquent already is a data access layer, and wrapping it usually adds indirection without benefit. It earns its place when you genuinely need to swap the data source, or when a team wants a hard boundary around queries. Saying “it depends, and usually no” with reasons is a better answer than reciting the pattern.
Requests, Routing and Middleware
19. What is middleware and when does it run?
A layer wrapping the route handler. Code before $next($request) runs inbound and can short-circuit; code after runs outbound and can modify the response. terminate() runs after the response has been sent.
20. What is route model binding?
Type-hinting a model in a route or controller makes Laravel resolve it from the route parameter and 404 if it does not exist. Implicit binding uses the primary key; customise it with getRouteKeyName(), a scoped binding (/posts/{post}/comments/{comment:slug}), or an explicit binding in a service provider.
21. What is CSRF and how does Laravel handle it?
Cross-Site Request Forgery: a third-party site tricks a logged-in browser into submitting a state-changing request. Laravel issues a per-session token, @csrf embeds it in forms, and VerifyCsrfToken rejects any non-idempotent request without a match. API routes using bearer tokens do not need it because there is no ambient cookie to abuse.
22. Difference between routes/web.php and routes/api.php?
web gets sessions, cookies and CSRF; api is stateless, prefixed with /api, and rate limited. Putting an API endpoint in web.php is the usual cause of a mystery 419.
23. What are API resources?
Transformation classes that decouple your JSON contract from your table columns. whenLoaded() and whenCounted() let a resource include a relationship only if the controller eager loaded it, which keeps resources from silently causing N+1.
Queues, Events and Scheduling
24. Why use a queue?
To keep slow, failure-prone or non-urgent work off the request cycle — and to get automatic retries. Emails, PDFs, third-party API calls, imports and exports.
25. What is the difference between queue:work and queue:listen?
queue:work boots the framework once and stays resident — fast, but it must be restarted after a deploy (queue:restart). queue:listen reboots per job — slow, but picks up code changes, which makes it convenient locally.
26. How do you stop a job running twice?
Make the job idempotent, and add ShouldBeUnique with a uniqueId() so only one instance for that key can be queued. Also keep the job’s $timeout below the connection’s retry_after, or the queue will hand a still-running job to a second worker.
27. Events and listeners vs directly calling a service?
Events decouple “something happened” from “what should follow”, and let several listeners react without the emitter knowing. The cost is indirection — tracing what happens after an event is harder. Use events for genuine fan-out, and a direct call for a single consequence.
28. How does the scheduler work?
One cron entry runs schedule:run every minute; Laravel decides which of your defined tasks are due. Use withoutOverlapping() to prevent a long task stacking, and onOneServer() when several servers share the same schedule.
Security
29. How does Laravel prevent SQL injection?
Eloquent and the query builder use PDO prepared statements, so values are bound rather than interpolated. The gaps are DB::raw(), whereRaw(), orderByRaw() and dynamic column names — bind parameters there too, and validate any column name against an allow list.
30. Gate vs Policy?
A gate is a closure for a standalone permission (“can this user view the admin panel?”). A policy is a class of methods for a specific model (“can this user update this post?”). Both are checked with can(), authorize(), the can middleware or @can in Blade.
31. How are passwords stored?
Hash::make() uses bcrypt by default (Argon2 available), with a per-password salt and a configurable work factor. Verify with Hash::check(). Never encrypt passwords — encryption is reversible, hashing is not.
32. How does Blade protect against XSS?
{{ $value }} escapes output through htmlspecialchars. {!! $value !!} does not — only use it on content you have sanitised with something like HTMLPurifier.
33. Sanctum vs Passport?
Sanctum: simple database-backed tokens plus cookie auth for first-party SPAs. Passport: a full OAuth2 server for third-party clients. Choose Sanctum unless you actually need OAuth2 grants.
Testing
34. Feature test vs unit test in Laravel?
A feature test boots the framework and exercises a route end to end ($this->post('/orders', [...])->assertRedirect()). A unit test instantiates a class directly with no framework. Most valuable Laravel tests are feature tests — they cover routing, middleware, validation and the database in one pass.
35. What does RefreshDatabase do?
Migrates the test database once, then wraps each test in a transaction that is rolled back afterwards — so tests stay isolated and fast. DatabaseMigrations re-migrates per test and is much slower.
36. How do you test something that sends mail or hits an API?
Mail::fake();
Queue::fake();
Event::fake();
Notification::fake();
Storage::fake('s3');
Http::fake([
'api.stripe.com/*' => Http::response(['id' => 'ch_123'], 200),
]);
// ... exercise the code ...
Mail::assertSent(OrderConfirmation::class);
Queue::assertPushed(GenerateInvoicePdf::class, fn ($job) => $job->order->is($order));
Http::assertSent(fn ($request) => $request->url() === 'https://api.stripe.com/v1/charges');Performance
37. A page takes four seconds. Walk me through diagnosing it.
Measure before guessing. Check the query count and durations (Debugbar, Telescope or DB::listen) — a three-figure query count means N+1. Then look for missing indexes with EXPLAIN, synchronous external API calls that should be queued, unpaginated result sets loading everything into memory, and expensive work in a Blade loop. Cache last, once the underlying query is actually fast.
38. How do you handle a table with 50 million rows?
Index for the access patterns you have; paginate with cursor pagination rather than OFFSET; process in chunks with chunkById() or lazyById() rather than get(); move aggregates to a summary table maintained by a job; and consider partitioning or archiving cold rows.
39. What is chunkById() and why prefer it over chunk()?
Both process large result sets in batches, but chunk() uses OFFSET, so rows that are modified during iteration can shift and be skipped. chunkById() pages by primary key, which is stable even while you are updating the rows you are reading.
Senior-Level Questions
40. How would you design a multi-tenant Laravel application?
There is no single right answer, and the interviewer wants your reasoning. Cover the three models — a tenant_id column with a global scope (simplest, weakest isolation), a database per tenant (strong isolation, migration overhead), a schema per tenant on PostgreSQL (middle ground). Then talk about the parts people forget: tenant-scoped cache keys, tenant-scoped queue jobs, file storage separation, and how you stop a missing global scope leaking one tenant’s data into another’s page.
For senior questions, the structure matters more than the answer. State the trade-offs, pick one, say what would make you pick differently, and name the failure mode you would watch for. That is what separates a senior answer from a confident guess.
How to Prepare
- Read the framework source. Open
Illuminate\Database\Eloquent\Builderonce. Candidates who have read any framework code are immediately obvious. - Have one project you can talk about in depth — what was slow, what you changed, what the numbers were before and after.
- Know the version you claim. Laravel 11 removed the HTTP kernel and middleware moved to
bootstrap/app.php. Answering with Laravel 8 structure signals you have not shipped recently. - Say “I do not know” well. “I have not used broadcasting in production, but I understand it as X — how do you use it here?” reads far better than bluffing.
- Practise explaining out loud. Knowing the answer and articulating it under mild pressure are separate skills.