Prepare for Laravel interview questions grouped by experience level.
Laravel Interview Question & Answers
0-2 Years
Laravel is a PHP web application framework providing a genuinely structured, expressive foundation for building an application, routing, an ORM, templating, all built in. It was built to solve the problem of PHP developers previously either using plain, unstructured procedural PHP or a genuinely far more verbose enterprise framework, giving a genuinely modern, developer-friendly middle ground.
MVC stands for Model, View, Controller. In Laravel, a Model represents and interacts with genuine database data through Eloquent, a View renders the actual HTML through Blade templates, and a Controller handles genuine incoming requests, coordinating between the Model and the View to actually produce a response.
Artisan is Laravel's genuine command-line interface, providing commands to actually generate boilerplate code, like a new controller or model, run database migrations, and perform many other genuinely routine development tasks, saving considerable manual, repetitive setup work.
Composer is PHP's genuine dependency manager, handling installing and updating a project's external libraries. Laravel itself, and genuinely most of the packages it's built from, are distributed and installed through Composer, making it genuinely essential to actually set up and maintain a Laravel project.
The .env file stores genuinely environment-specific configuration, like database credentials and API keys, kept genuinely separate from the actual application code and typically excluded from version control, so sensitive values aren't genuinely committed directly into the codebase itself.
php artisan serve starts a genuinely lightweight local development server, letting you actually run and test a Laravel application in a browser without needing a genuinely fully configured web server like Apache or Nginx set up just for local development.
Route::get('/users', function () { return 'User list'; }); defines a route responding to a GET request at /users, running the given genuine closure (or, genuinely more commonly, calling a controller method) to actually produce the response.
Route::get('/users', [UserController::class, 'index']); tells Laravel to genuinely call the index method on UserController whenever a GET request hits /users, which is the genuinely standard, recommended approach for anything beyond a genuinely trivial route.
Route::get('/users/{id}', [UserController::class, 'show']); captures the value in that URL segment and passes it genuinely directly as an argument to the show method, letting the controller actually use it, like looking up a specific user by that captured id.
Naming a route, ->name('users.index'), lets you genuinely reference it elsewhere in the application by that name rather than hardcoding its actual URL path, like route('users.index'). This means the actual URL itself can genuinely change later without needing to update every single place that links to it.
A GET route genuinely retrieves and displays data, like showing a form or a list. A POST route genuinely submits data, like actually saving a new record after a form is genuinely submitted. Using the appropriate genuine HTTP method for each action follows standard, expected REST convention.
Route model binding automatically resolves a genuine Eloquent model instance directly from a route parameter, so instead of manually looking up a record by its ID inside the controller, Laravel genuinely injects the already-found model instance directly as the controller method's own parameter.
Eloquent is Laravel's genuine built-in ORM (Object-Relational Mapper), letting you actually interact with database tables using PHP objects and genuinely expressive method calls rather than writing raw SQL directly, mapping each database table to a genuinely corresponding PHP model class.
User::all() returns every record in the genuine users table as a collection of User model instances, letting you actually work with the data as genuine PHP objects rather than raw, unstructured database rows.
User::find(1) returns the genuine User record with a primary key of 1, or returns null if no genuinely matching record actually exists. find OrFail(1) does the exact same thing but genuinely throws an exception instead if no matching record is actually found.
A migration is a genuine PHP file describing a specific change to the database schema, creating a table or adding a column, that can genuinely be run to apply that change and rolled back to genuinely undo it. It solves the genuine problem of keeping a database schema consistently in sync across every developer's own local environment and every deployed server.
A model represents a genuinely single database table, and by Laravel's own default convention, a model named User genuinely, automatically maps to a table named users, following a genuinely simple, predictable naming pattern that can still be explicitly overridden when actually needed.
Mass assignment lets you set several model attributes at once from an array, like User::create($request->all()). Laravel requires an explicit fillable (allow list) or guarded (deny list) property because without it, a maliciously crafted request could set a field, like an is_admin flag, that was never actually intended to be user-editable at all.
A seeder inserts genuine sample or default data into the database, commonly used to actually populate a fresh development environment with realistic test data, or to genuinely insert essential default records, like an initial admin user, needed for the application to actually function correctly.
Blade is Laravel's genuine templating engine, letting you write HTML mixed with genuinely simple, readable directives, like @if or @foreach, that compile down into efficient plain PHP behind the scenes. It solves the genuine problem of mixing raw PHP directly into HTML, which quickly becomes genuinely messy and hard to actually read.
{{ $variable }} genuinely outputs a variable's value, automatically escaping it to prevent an actual cross-site scripting vulnerability. {!! $variable !!} outputs it genuinely unescaped, used only when you're deliberately, genuinely certain the content is actually safe to render as raw HTML.
A parent (layout) template defines genuine reusable structure with named sections marked using @yield. A child template uses @extends to genuinely inherit that layout, and @section to actually fill in the content for a genuinely specific named section, avoiding repeating the exact same layout HTML across every single page.
A Blade component packages a genuinely reusable piece of markup, like a button or a card, into its own dedicated file, letting you actually reuse it across multiple views with genuinely different data passed in, rather than copying and pasting the exact same HTML repeatedly.
@foreach ($users as $user) {{ $user->name }} @endforeach genuinely iterates over the $users collection, outputting each individual user's name, using Blade's genuinely simplified directive syntax rather than raw PHP's own foreach syntax mixed directly into the HTML.
@csrf outputs a genuine hidden input containing a CSRF token, which Laravel automatically verifies on form submission to actually protect against cross-site request forgery. Without it, a genuine POST, PUT, or DELETE form submission is automatically rejected by Laravel's own default security middleware.
A controller groups genuinely related request-handling logic into one class, keeping route definitions themselves clean and focused. php artisan make:controller UserController generates a genuinely new controller class with the standard boilerplate already in place.
A resource controller genuinely follows the standard convention for a resource's typical CRUD operations, index, show, create, store, edit, update, destroy, each mapped automatically to the genuinely appropriate HTTP method and URL pattern when registered with Route::resource().
Route::resource() registers all seven standard CRUD routes, including create and edit, which return an HTML form view. Route::apiResource() registers only the five routes genuinely relevant to a JSON API, index, store, show, update, destroy, skipping create and edit since an API client typically doesn't need a server-rendered form to submit data.
$request->input('name') (or the shorthand $request->name) retrieves a genuinely specific field's value from the incoming request, whether it was submitted through a form's POST body or as a genuine query string parameter.
The Request object represents an genuinely incoming HTTP request, providing access to submitted form data, query parameters, uploaded files, headers, and the genuinely current authenticated user, all through a single, consistent, genuinely convenient object passed into a controller method.
return redirect()->route('users.index'); sends the genuine browser a redirect response pointing to the named users.index route, commonly used right after successfully saving a form, so the user genuinely sees the resulting updated list rather than a raw form-submission response.
return response()->json(['name' => 'Anu']); returns a genuine JSON-formatted response with the appropriate Content-Type header automatically set, which is the standard way a Laravel controller responds when it's genuinely serving an API request rather than rendering an HTML view.
php artisan make:migration create_posts_table generates a genuinely new migration file with a timestamp prefix, providing the boilerplate structure for actually defining a new table's schema inside its up and down methods.
The up method defines what genuinely happens when the migration is actually run, typically creating a table or adding a column. The down method defines how to genuinely reverse that exact same change, letting the migration actually be rolled back cleanly if needed.
php artisan migrate applies every genuinely pending migration that hasn't already been run, updating the actual database schema to genuinely match what's currently defined across all the migration files.
It genuinely reverses the most recently run batch of migrations, calling each one's own down method, undoing the actual schema changes that batch had applied, which is genuinely useful when a recent migration turns out to have a mistake needing to be corrected.
A factory defines how to genuinely generate a realistic, fake instance of a model, commonly used for actually seeding a development database with realistic sample data, or for genuinely creating test data quickly within an automated test, without manually specifying every single field's value by hand.
3-6 Years
hasOne and hasMany define a genuine one-to-one or one-to-many relationship from the parent's own perspective. belongsTo defines the genuinely inverse relationship from the child's perspective. belongsToMany defines a genuine many-to-many relationship, typically using an intermediate pivot table.
It happens when fetching a genuine list of records triggers one query for the list, then one additional separate query per record to fetch each one's genuine related data. Eager loading, using with('relationName'), fetches the genuinely related data in one additional, efficient batch query instead of a genuinely separate query per record.
Lazy loading fetches a genuinely related model's data only at the exact moment it's actually accessed, potentially triggering a genuinely separate query for each individual record in a loop. Eager loading fetches related data genuinely upfront, in one efficient additional query, before the actual loop even begins.
A local scope defines a genuinely reusable query constraint as a method on the model itself, like scopeActive(), letting you write User::active()->get() instead of repeating the exact same where clause manually everywhere that specific, common filter is genuinely needed.
An accessor genuinely transforms an attribute's value when it's actually retrieved from the model, like formatting a stored date. A mutator genuinely transforms a value before it's actually saved to the database, like automatically hashing a password whenever it's genuinely set on the model.
Middleware filters an HTTP request as it genuinely passes through the application, before it actually reaches a controller (or after the response is generated), used for genuinely cross-cutting concerns like authentication checks, logging, or CORS handling, applied once rather than repeated inside every individual controller.
php artisan make:middleware CheckSubscription generates a genuinely new middleware class with a handle method containing your actual logic. Registering it in the application's genuine middleware configuration, either globally or as a genuinely named, route-specific middleware, actually activates it.
A Form Request is a genuinely dedicated class encapsulating both validation rules and authorization logic for a specific request, keeping the controller method itself genuinely clean and focused purely on its actual business logic, rather than being cluttered with a genuinely large block of inline validation rules.
$request->validate(['email' => 'required|email']); checks the genuine incoming data against the given rules, automatically redirecting back with genuine validation errors if it fails, or letting execution continue normally to the next line of code if it actually passes.
$request->validate() is genuinely simpler for a quick, small set of rules defined directly inline. A Form Request class is genuinely preferred for a more complex set of rules, or when validation logic needs to genuinely be reused across multiple different controller methods, keeping that logic in one single, dedicated, testable place.
Laravel provides genuine user registration, login, logout, and password reset functionality largely already built, through starter kits like Laravel Breeze or Jetstream, without needing to actually write that fairly standard, genuinely repetitive authentication logic entirely from scratch.
Authentication genuinely verifies who a user actually is, handled by the login process itself. Authorization decides what that now-identified user is genuinely allowed to do, handled through Laravel's own Gates and Policies.
A Gate defines a genuinely simple authorization check as a closure, like Gate::define('edit-post', function ($user, $post) { return $user->id === $post->user_id; });, letting you actually check authorize('edit-post', $post) elsewhere in the application to verify a genuine permission.
A Policy groups genuinely related authorization logic for a specific model into its own dedicated class, with a method genuinely corresponding to each specific action, like update or delete. It's genuinely preferred over Gates once authorization logic for a specific model grows beyond just a genuinely simple, single check.
Applying the auth middleware to a route, either individually or grouped, ensures Laravel genuinely redirects an unauthenticated user to the login page automatically, rather than letting the route's own logic actually execute for someone who genuinely isn't logged in at all.
The Service Container is Laravel's genuine dependency injection container, responsible for actually resolving and injecting a class's dependencies automatically, rather than a developer needing to manually instantiate every genuine dependency by hand wherever it's actually needed.
Dependency injection provides a class with the genuine dependencies it actually needs from an external source, rather than the class itself genuinely creating them directly. Laravel's Service Container automatically detects a genuine type-hinted dependency in a controller's constructor or method and actually resolves and injects an appropriate instance.
A Service Provider is where genuinely most of an application's own bootstrapping happens, registering a genuine binding in the Service Container, or performing other genuinely necessary setup that needs to run once when the application actually starts up.
A singleton binding ensures the exact same genuine instance is actually returned every single time that class is resolved throughout the entire request lifecycle. A regular binding genuinely creates a brand new instance every single time it's actually resolved, which matters when a class genuinely shouldn't share state across different, separate resolutions.
A Unit test verifies a genuinely small, isolated piece of code, like a single class method, in isolation. A Feature test verifies genuinely broader application behavior, like making an actual HTTP request to a route and asserting on the genuine resulting response, exercising several parts of the application together.
$response = $this->get('/users'); $response->assertStatus(200); genuinely makes a simulated GET request to the specified route and asserts the actual returned status code matches the expected value, all within Laravel's own built-in testing helpers.
A factory generates a genuinely realistic fake model instance with sensible default values, letting a test create the exact test data it actually needs in one concise line, User::factory()->create(), rather than manually specifying every single required field by hand in every individual test.
Send a request with genuinely deliberately invalid data, and assert that the response includes a genuine validation error for that specific field, using $response->assertSessionHasErrors('email'), confirming the validation rule actually catches the invalid input the way it's genuinely supposed to.
6-8 Years
DB::table('users')->where('active', true)->get(); builds and runs a genuine query directly without going through an Eloquent model at all. It's genuinely appropriate for a performance-sensitive, read-heavy query where you genuinely don't need Eloquent's own model features, like relationships or accessors, and want to avoid its genuine overhead.
Laravel Debugbar or Telescope shows genuinely every query executed during a request, including the actual generated SQL and its genuine execution time, making it easy to actually spot an unexpectedly slow query or a genuine N+1 problem hiding within a page that otherwise looks fine at a glance.
chunk() (or the more memory-efficient cursor()) processes records in genuinely small batches rather than loading an entire, potentially enormous result set into memory all at once. It solves the genuine problem of a script running out of memory when it needs to actually process a genuinely very large table.
chunk() runs a genuinely separate query for each batch, offset progressively through the table. lazy() (built on cursor pagination internally) avoids the offset-based approach entirely, which can degrade in performance on a very large table, and is generally the more efficient choice for genuinely very large datasets specifically because of that difference.
get() returns a genuine collection of every matching record. first() returns genuinely just the single first matching record (or null), and internally applies a LIMIT 1 to the actual underlying SQL query, making it meaningfully more efficient than fetching every result and then just taking the first one in PHP.
User::select('id', 'name')->get(); genuinely limits the query to only actually fetch the specific columns needed, reducing the amount of genuine data transferred from the database and held in memory, which matters meaningfully more once a table has genuinely many columns or a large number of rows.
DB::transaction(function () { ... }); genuinely ensures every database operation inside the closure either all succeed together, or genuinely all roll back together if any one of them fails. It's used when several genuinely related writes need to succeed as one single, atomic unit, like deducting inventory and creating an order record together.
A queue lets you genuinely defer a slow or resource-intensive task, like sending an email, to actually run in the background rather than during the original request, letting the original request return a genuinely fast response to the user without waiting for that slower task to actually finish.
php artisan make:job SendWelcomeEmail generates a genuine Job class containing a handle method with the actual logic to run. ProcessOrder::dispatch($order); genuinely queues that job for background processing rather than running it immediately, inline, during the current request.
A queued job is genuinely placed onto a queue and processed later by a genuinely separate queue worker process. A job dispatched synchronously, using dispatchSync(), runs genuinely immediately, inline, within the current request, which is useful mainly for testing or a genuinely simple case where actual background processing isn't truly needed.
Laravel genuinely retries a failed job automatically based on its configured retry settings, and after exhausting genuine retries, it's recorded in a failed_jobs table. Defining a failed() method on the Job class lets you actually handle that final failure explicitly, like notifying someone or logging genuinely additional context.
A queue worker, started with php artisan queue:work, continuously genuinely polls the queue and processes any jobs waiting on it. It needs to genuinely keep running continuously in production, typically managed by a process monitor like Supervisor, since queued jobs otherwise genuinely just sit there unprocessed with nothing actually picking them up.
8-10 Years
A Repository wraps all database access behind an genuine interface, so the rest of the application talks to that interface instead of directly to Eloquent. It solves the genuine problem of making it easier to genuinely swap the underlying data source later, and makes unit testing business logic possible without touching a genuinely real database at all.
A Service class holds genuinely reusable business logic, kept genuinely separate from the request-response handling a controller is actually responsible for. Putting logic in a controller directly works for something genuinely simple, but a Service class lets that logic genuinely be reused, like from a queued job or an Artisan command, without duplicating it across each different entry point.
An Event represents something that genuinely happened, like OrderPlaced, and a Listener reacts to that event, like sending a genuine confirmation email. It solves the genuine problem of tightly coupling unrelated side effects directly into the original code path, letting new behavior genuinely be added later by simply attaching a genuinely new listener, without touching the original code at all.
Push genuinely business logic out of the controller into either the Eloquent model itself, for logic genuinely specific to that model, or a dedicated Service class, for logic genuinely spanning multiple models or external integrations, leaving the controller responsible only for genuinely handling the request and returning a response.
An Eloquent Observer listens for genuine model lifecycle events, like creating or deleting, and runs logic automatically in response, without cluttering the model class itself with that logic directly. A genuinely practical use case is automatically generating a unique slug whenever a new Post model is genuinely being created.
Organize the application by genuinely feature or domain rather than purely by Laravel's own default folder structure, grouping a feature's controllers, models, and services together, rather than one single, enormous folder each of controllers and models covering the entire, genuinely unrelated application.
I'd weigh whether the functionality is genuinely specific to this one application, or whether it's genuinely reusable across multiple projects, in which case extracting it into its own package avoids duplicating that same code, at the real cost of the extra overhead of maintaining a genuinely separate package and its own versioning.
An API Resource transforms an Eloquent model (or a collection of them) into a genuinely consistent, controlled JSON structure, letting you actually decide exactly which fields to expose and how to genuinely format them, rather than returning a model's raw, genuine database attributes directly as-is.
Sanctum provides a genuinely lightweight, simple token-based authentication system, well suited for a first-party SPA or a genuinely simple API. Passport implements a genuinely full OAuth2 server, fitting a scenario needing genuinely more sophisticated authorization flows, like issuing tokens to a genuinely third-party application.
URI versioning, prefixing routes with /api/v1/ and /api/v2/, is the genuinely most explicit and easiest approach for consumers to understand, letting a genuinely new version's routes and controllers coexist alongside the older, still-supported version during a genuine transition period.
Laravel's throttle middleware limits how many requests a client can genuinely make within a defined time window, applied to a route or route group, like Route::middleware('throttle:60,1')->group(...), protecting an API from genuine abuse or an accidental overload from a misbehaving client.
Laravel's centralized exception handler can genuinely be customized to catch a specific exception type and return a genuinely consistent JSON error structure, rather than letting each individual controller format its own genuine error response slightly differently and inconsistently.
Packages are genuinely reusable pieces of functionality, distributed through Composer, that can be genuinely installed into a Laravel application to add capability, like Sanctum for authentication or Spatie's permission package for genuinely fine-grained role management, without needing to actually build that same, genuinely common functionality entirely from scratch.
10+ Years
I'd look at where the actual pain is genuinely coming from, a specific component genuinely needing to scale independently, or genuine team-ownership conflicts within the same codebase, rather than assuming a monolith is inherently a problem just because it's genuinely grown large. A well-structured monolith is often the right call for a genuinely smaller team or an earlier-stage product.
Run it incrementally, using Laravel's own official upgrade guide and any available automated upgrade tooling wherever genuinely possible, leaning on existing test coverage to catch a genuine regression early, and migrating feature by feature rather than attempting one single, large, disruptive upgrade all at once.
I check whether it's genuinely solving the real problem or just its symptom, whether business logic is genuinely kept out of controllers appropriately, and whether it's genuinely consistent with patterns already established elsewhere in the codebase. An inconsistent one-off pattern becomes a genuine maintenance burden the whole team inherits later.
Automate what can genuinely be automated, PHP-CS-Fixer or Laravel Pint for formatting, PHPStan or Larastan for static analysis, enforced directly in CI so standards aren't purely a matter of individual opinion during manual code review. For architectural conventions that genuinely resist full automation, I'd document the handful of decisions that actually matter most.
I'd weigh the genuine time saved and consistency benefit across projects against the real risk of depending on a genuinely third-party package's own maintenance and update cadence, and whether the package's own conventions genuinely fit well with how the organization's own applications are already structured.
I'd check whether the actual local development data volume genuinely resembles production, since a query that's fast on a small local dataset can genuinely become dramatically slower once the real, much larger production data is genuinely involved. I'd also check queue worker health and whether background jobs are genuinely backing up under real production load.
Track application error rate and response latency using a tool like Laravel Telescope in a non-production environment, or a dedicated production monitoring service, alerting on meaningful deviation from a normal, established baseline rather than relying only on a hard, static threshold.
Treat the package's actual public interface as a genuine contract with every consuming team. Adding something new is generally safe. Changing or removing an existing public method's signature needs a documented deprecation period before actual removal, rather than a silent breaking change in a minor version bump.
I'd check the deployment's own genuine change log first, since a recent deployment is the most likely genuine suspect, and consider rolling back immediately if the issue is genuinely severe, prioritizing mitigating user impact over fully understanding root cause immediately.
I'd monitor query performance and queue processing time trends proactively, and address a slow query or a genuinely growing queue backlog before it actually starts visibly, noticeably affecting real users, rather than waiting for a genuine complaint to surface the problem first.
This is a judgment question interviewers use to see how you reason under genuine uncertainty, not to test a specific textbook fact. A strong answer names the actual constraint that forced the decision, the realistic options that were genuinely on the table, why you picked one knowing it wasn't guaranteed to be right, and what you'd do differently with what you know now.
I'd walk through one of their actual controllers together, asking what would genuinely happen if that same logic needed to also run from a queued job or an Artisan command, and let them see firsthand that the current structure genuinely doesn't support reusing it cleanly at all. Seeing the actual, concrete limitation tends to shift their instincts more effectively than a general rule alone.
I wouldn't push a disruptive full restructuring. I'd reorganize one genuinely painful, frequently-touched feature area as a visible example, letting the team directly see the genuine difference in navigability, and let that build organic buy-in rather than mandating the change from the top down before anyone's actually seen the benefit for themselves.
I'd frame it around whether the logic is genuinely intrinsic to that one model's own data, in which case the model itself is a reasonable home, or whether it genuinely spans multiple models or external systems, in which case a service class is generally the cleaner fit. Grounding the discussion in the specific logic at hand resolves it faster than a general, abstract preference.
I'd translate the debt into terms leadership already tracks: a specific incident or delayed feature that traced directly back to it, and how much longer a typical change in that area now takes compared to a genuinely well-structured part of the same codebase. Framed as a velocity problem with a real, already-incurred cost, it competes far better for prioritization than framed as a general code-quality concern.




