Laravel interview questions
Status: Complete for the Laravel interview core. Last reviewed 2026-08-27.
Example answers model one credible 60–120 second spoken response. They are not grading rubrics or uniquely correct scripts.
Request lifecycle, container, providers, and facades
Section titled “Request lifecycle, container, providers, and facades”LARAVEL-LIFECYCLE-01 — Trace a Laravel web request
Section titled “LARAVEL-LIFECYCLE-01 — Trace a Laravel web request”Trace a current Laravel web request from public/index.php to the emitted response. Include bootstrapping, providers, routing, and middleware ordering.
LARAVEL-LIFECYCLE-02 — Explain zero-configuration resolution
Section titled “LARAVEL-LIFECYCLE-02 — Explain zero-configuration resolution”Why can Laravel resolve some classes without an explicit binding, and where does that stop working?
LARAVEL-LIFECYCLE-03 — Separate provider registration from booting
Section titled “LARAVEL-LIFECYCLE-03 — Separate provider registration from booting”What belongs in a service provider’s register() method versus boot(), and why does the ordering matter?
LARAVEL-LIFECYCLE-04 — Explain what a facade invokes
Section titled “LARAVEL-LIFECYCLE-04 — Explain what a facade invokes”What actually happens when application code calls Cache::get($key) through a Laravel facade?
LARAVEL-LIFECYCLE-05 — Diagnose cross-request state leakage
Section titled “LARAVEL-LIFECYCLE-05 — Diagnose cross-request state leakage”An Octane application sometimes returns tenant A’s data during tenant B’s request, but ordinary feature tests pass. How would you investigate and fix it?
LARAVEL-LIFECYCLE-06 — Choose injection, a facade, or service location
Section titled “LARAVEL-LIFECYCLE-06 — Choose injection, a facade, or service location”When would you use constructor injection, a facade, or direct container access? Explain the trade-offs rather than giving a universal rule.
Routing, middleware, and model binding
Section titled “Routing, middleware, and model binding”LARAVEL-ROUTING-01 — Trace route selection and dispatch
Section titled “LARAVEL-ROUTING-01 — Trace route selection and dispatch”Trace what Laravel does from route matching through controller invocation. Where do constraints, middleware, and parameter binding participate?
LARAVEL-ROUTING-02 — Explain final middleware order
Section titled “LARAVEL-ROUTING-02 — Explain final middleware order”A route lists middleware in one order, but production behavior suggests another. How does Laravel produce the final pipeline, and how would you verify it?
LARAVEL-ROUTING-03 — Explain implicit model binding
Section titled “LARAVEL-ROUTING-03 — Explain implicit model binding”How does implicit Eloquent route model binding turn {post} into Post $post, and what happens for custom keys, soft-deleted rows, or a missing model?
LARAVEL-ROUTING-04 — Separate scoped binding from authorization
Section titled “LARAVEL-ROUTING-04 — Separate scoped binding from authorization”For /tenants/{tenant}/projects/{project}, what does scoped binding guarantee, and why is a policy or equivalent authorization still required?
LARAVEL-ROUTING-05 — Diagnose a production-only routing failure
Section titled “LARAVEL-ROUTING-05 — Diagnose a production-only routing failure”A new route works locally but production returns a 404 or invokes an older route. Give a diagnosis sequence that considers matching, deployment, and cache state.
LARAVEL-ROUTING-06 — Choose a boundary mechanism
Section titled “LARAVEL-ROUTING-06 — Choose a boundary mechanism”Choose among a route constraint, enum or model binding, middleware, and controller/application lookup for rejecting or resolving an incoming value. What drives the decision?
Validation, authorization, policies, and tenant boundaries
Section titled “Validation, authorization, policies, and tenant boundaries”LARAVEL-BOUNDARIES-01 — Trace a Form Request
Section titled “LARAVEL-BOUNDARIES-01 — Trace a Form Request”Trace a Form Request from container resolution to controller invocation. When do preparation, authorization, validation after-hooks, and passedValidation() run?
LARAVEL-BOUNDARIES-02 — Separate validation from invariants
Section titled “LARAVEL-BOUNDARIES-02 — Separate validation from invariants”Contrast input validation, authorization, a domain invariant, and a database constraint using a project update or reservation example.
LARAVEL-BOUNDARIES-03 — Choose a gate or policy
Section titled “LARAVEL-BOUNDARIES-03 — Choose a gate or policy”When would you use a gate versus a policy, and what are the consequences of policy before() hooks and authorization responses?
LARAVEL-BOUNDARIES-04 — Design tenant isolation
Section titled “LARAVEL-BOUNDARIES-04 — Design tenant isolation”Design tenant isolation for a Laravel feature across HTTP lookup, policies, queries, writes, cache, storage, and long-running workers.
LARAVEL-BOUNDARIES-05 — Diagnose an unsafe queued export
Section titled “LARAVEL-BOUNDARIES-05 — Diagnose an unsafe queued export”An export request is authorized correctly, but a queued job occasionally exports another tenant’s project. How would you diagnose, repair, and test the whole path?
LARAVEL-BOUNDARIES-06 — Choose denial semantics
Section titled “LARAVEL-BOUNDARIES-06 — Choose denial semantics”Choose among 401, 403, 404, and 422 for failures at a tenant-owned endpoint. What information-leak and client-contract trade-offs matter?
Eloquent model mechanics
Section titled “Eloquent model mechanics”LARAVEL-ELOQUENT-01 — Trace a model through hydration and save
Section titled “LARAVEL-ELOQUENT-01 — Trace a model through hydration and save”Trace an Eloquent model from query-result hydration through attribute assignment and save(). Which state is kept in memory, and when is the database actually changed?
LARAVEL-ELOQUENT-02 — Separate mass assignment from authorization
Section titled “LARAVEL-ELOQUENT-02 — Separate mass assignment from authorization”What do $fillable and $guarded protect, which assignment paths bypass them, and why are they not validation or authorization?
LARAVEL-ELOQUENT-03 — Choose casts, accessors, or value objects
Section titled “LARAVEL-ELOQUENT-03 — Choose casts, accessors, or value objects”Choose among a built-in cast, an Attribute accessor/mutator, a custom cast, and an application-owned value object. What guarantees does each add or leave missing?
LARAVEL-ELOQUENT-04 — Explain dirty tracking and stale state
Section titled “LARAVEL-ELOQUENT-04 — Explain dirty tracking and stale state”Contrast isDirty(), wasChanged(), fresh(), and refresh(). Why does none of them prevent a lost update by itself?
LARAVEL-ELOQUENT-05 — Diagnose a bulk-update side-effect gap
Section titled “LARAVEL-ELOQUENT-05 — Diagnose a bulk-update side-effect gap”A query-level Eloquent update changes every target row, but audit records and notifications are missing. Explain the cause and choose a repair that accounts for scale and retries.
LARAVEL-ELOQUENT-06 — Place model side effects safely
Section titled “LARAVEL-ELOQUENT-06 — Place model side effects safely”When is a model observer appropriate, what changes inside a database transaction, and when would you use after-commit handling or an outbox instead?
Eloquent relationships and loading
Section titled “Eloquent relationships and loading”LARAVEL-RELATIONSHIPS-01 — Contrast a relationship method and property
Section titled “LARAVEL-RELATIONSHIPS-01 — Contrast a relationship method and property”What is the difference between $post->comments() and $post->comments, including query composition, lazy loading, caching, and counting?
LARAVEL-RELATIONSHIPS-02 — Choose a relationship shape
Section titled “LARAVEL-RELATIONSHIPS-02 — Choose a relationship shape”Choose among belongsTo, has-one/has-many, many-to-many with a pivot, through, and polymorphic relationships. Which schema guarantees and trade-offs remain outside Eloquent?
LARAVEL-RELATIONSHIPS-03 — Choose with, load, or loadMissing
Section titled “LARAVEL-RELATIONSHIPS-03 — Choose with, load, or loadMissing”Contrast with(), load(), and loadMissing() by timing and intent. When can default or automatic eager loading help or obscure the query plan?
LARAVEL-RELATIONSHIPS-04 — Diagnose a hidden N+1
Section titled “LARAVEL-RELATIONSHIPS-04 — Diagnose a hidden N+1”An endpoint passes controller query-count review but production still emits repeated relationship queries during transformation. How would you locate and fix the trigger, including reverse child-to-parent traversal?
LARAVEL-RELATIONSHIPS-05 — Fix eager-loading over-fetching
Section titled “LARAVEL-RELATIONSHIPS-05 — Fix eager-loading over-fetching”How can eager loading remove an N+1 while still creating a poor query strategy? Diagnose a slow, memory-heavy endpoint and choose among constraints, aggregates, pagination, or a projection.
LARAVEL-RELATIONSHIPS-06 — Design a relationship-safe API resource
Section titled “LARAVEL-RELATIONSHIPS-06 — Design a relationship-safe API resource”Design an API resource and owning query so optional relationships, counts, pivots, and nested resources cannot silently change query count or response shape.
Testing Laravel applications
Section titled “Testing Laravel applications”LARAVEL-TESTING-01 — Choose a Laravel test boundary
Section titled “LARAVEL-TESTING-01 — Choose a Laravel test boundary”Choose among a framework-free unit test, Laravel feature test, real-driver integration test, and deployed smoke test. How does the failure risk determine the boundary?
LARAVEL-TESTING-02 — Design an HTTP confidence test
Section titled “LARAVEL-TESTING-02 — Design an HTTP confidence test”Design a Laravel test for a tenant-owned update endpoint. Which real layers and observable outcomes should it include, and what does Laravel’s HTTP testing layer still not prove?
LARAVEL-TESTING-03 — Choose database reset and engine fidelity
Section titled “LARAVEL-TESTING-03 — Choose database reset and engine fidelity”Contrast RefreshDatabase, DatabaseMigrations, and DatabaseTruncation. When should the suite use the production database engine rather than SQLite?
LARAVEL-TESTING-04 — Expose a queue-fake blind spot
Section titled “LARAVEL-TESTING-04 — Expose a queue-fake blind spot”Why can a queued job pass under Queue::fake() and fail in production? Design a credible set of tests for dispatch, execution, commit behavior, and worker wiring.
LARAVEL-TESTING-05 — Use Laravel fakes without fictional confidence
Section titled “LARAVEL-TESTING-05 — Use Laravel fakes without fictional confidence”How would you use event, mail, notification, storage, HTTP, and time fakes while remaining explicit about the behavior they do not prove?
LARAVEL-TESTING-06 — Diagnose a suite-only or parallel failure
Section titled “LARAVEL-TESTING-06 — Diagnose a suite-only or parallel failure”A test passes alone but fails in the full or parallel Laravel suite. Give a diagnosis sequence that includes framework state, database isolation, and shared external resources.
Queues, events, listeners, notifications, and retries
Section titled “Queues, events, listeners, notifications, and retries”LARAVEL-QUEUES-01 — Trace a queued job lifecycle
Section titled “LARAVEL-QUEUES-01 — Trace a queued job lifecycle”Trace a Laravel job from dispatch and serialization through reservation, execution, acknowledgement, redelivery, and terminal failure. Which state crosses the process boundary?
LARAVEL-QUEUES-02 — Align timeout and reservation settings
Section titled “LARAVEL-QUEUES-02 — Align timeout and reservation settings”Explain the relationship among a job timeout, worker --timeout, retry_after or visibility timeout, and supervisor termination. How can bad values cause concurrent execution?
LARAVEL-QUEUES-03 — Make a retried side effect idempotent
Section titled “LARAVEL-QUEUES-03 — Make a retried side effect idempotent”A payment provider succeeds, but the worker dies before Laravel records completion. Design the retry, idempotency, state-transition, and reconciliation behavior.
LARAVEL-QUEUES-04 — Choose a direct call, event, listener, notification, or job
Section titled “LARAVEL-QUEUES-04 — Choose a direct call, event, listener, notification, or job”Choose among a direct call, event with synchronous listeners, queued listener, notification, and explicit job. Which intent, ordering, latency, and failure boundaries drive the choice?
LARAVEL-QUEUES-05 — Separate uniqueness, overlap prevention, and idempotency
Section titled “LARAVEL-QUEUES-05 — Separate uniqueness, overlap prevention, and idempotency”Contrast ShouldBeUnique, ShouldBeUniqueUntilProcessing, WithoutOverlapping, and an idempotent handler. Which duplicate or concurrency problem does each solve?
LARAVEL-QUEUES-06 — Design transaction-safe fan-out and recovery
Section titled “LARAVEL-QUEUES-06 — Design transaction-safe fan-out and recovery”An order transaction should trigger several independent reactions and a customer notification. Design commit timing, listener queueing, failure isolation, and operational recovery.
Authentication, Sanctum, Passport, OAuth2, and API security
Section titled “Authentication, Sanctum, Passport, OAuth2, and API security”LARAVEL-AUTH-01 — Trace session authentication and logout
Section titled “LARAVEL-AUTH-01 — Trace session authentication and logout”Trace a Laravel browser login and later authenticated request through the guard, provider, password hash, session, regeneration, and secure logout.
LARAVEL-AUTH-02 — Choose session, Sanctum, Passport, or OpenID Connect
Section titled “LARAVEL-AUTH-02 — Choose session, Sanctum, Passport, or OpenID Connect”Choose authentication for a first-party browser SPA, mobile application, automation token, third-party delegated API client, and workforce SSO. What changes the decision?
LARAVEL-AUTH-03 — Explain Sanctum SPA authentication
Section titled “LARAVEL-AUTH-03 — Explain Sanctum SPA authentication”How does Sanctum authenticate a first-party SPA without issuing it a personal access token? Explain stateful domains, cookies, CSRF, CORS, and auth:sanctum.
LARAVEL-AUTH-04 — Design a personal-token lifecycle
Section titled “LARAVEL-AUTH-04 — Design a personal-token lifecycle”Design issuance, storage, abilities, expiry, rotation, revocation, and incident response for Sanctum personal access tokens. Which replay risks remain?
LARAVEL-AUTH-05 — Layer abilities, policies, and tenant isolation
Section titled “LARAVEL-AUTH-05 — Layer abilities, policies, and tenant isolation”A request passes auth:sanctum and its token has projects:write. Why can it still be unauthorized, and where should each remaining check occur?
LARAVEL-AUTH-06 — Secure reset, MFA, and account recovery
Section titled “LARAVEL-AUTH-06 — Secure reset, MFA, and account recovery”Design password reset and MFA recovery without creating a weaker alternative login path. Include enumeration, token handling, session revocation, and support operations.
Cache, sessions, and Redis integration
Section titled “Cache, sessions, and Redis integration”LARAVEL-CACHE-01 — Design a safe cache key and invalidation plan
Section titled “LARAVEL-CACHE-01 — Design a safe cache key and invalidation plan”Cache a tenant-specific project summary whose fields vary by viewer permissions. Design its keys, lifetime, invalidation, and fallback behavior.
LARAVEL-CACHE-02 — Explain why a cache lock is a lease
Section titled “LARAVEL-CACHE-02 — Explain why a cache lock is a lease”Why is a Laravel cache lock an expiring lease rather than permanent mutual exclusion, and what correctness work remains after acquiring it?
LARAVEL-CACHE-03 — Control a hot-key stampede
Section titled “LARAVEL-CACHE-03 — Control a hot-key stampede”A popular cache key expires and database load spikes. Compare locking, stale-while-revalidate, jitter, and pre-warming.
LARAVEL-CACHE-04 — Trace session concurrency and regeneration
Section titled “LARAVEL-CACHE-04 — Trace session concurrency and regeneration”Trace session read/write behavior across two concurrent browser requests. When should the identifier be regenerated, and when would you enable session blocking?
LARAVEL-CACHE-05 — Separate Redis workloads and failure domains
Section titled “LARAVEL-CACHE-05 — Separate Redis workloads and failure domains”What breaks when cache, sessions, queues, rate limiters, and locks share an undersized Redis deployment? Propose an isolation plan.
Filesystem, uploads, and object storage
Section titled “Filesystem, uploads, and object storage”LARAVEL-FILES-01 — Design a secure upload pipeline
Section titled “LARAVEL-FILES-01 — Design a secure upload pipeline”Design a tenant-safe document upload from validation through quarantine, scanning, persistence, and authorized download.
LARAVEL-FILES-02 — Choose public delivery, proxying, or temporary URLs
Section titled “LARAVEL-FILES-02 — Choose public delivery, proxying, or temporary URLs”Choose between a public URL, application-proxied download, and object-store temporary URL for several sensitivity and scale requirements.
LARAVEL-FILES-03 — Reconcile database and object-store state
Section titled “LARAVEL-FILES-03 — Reconcile database and object-store state”How do you handle successful object storage followed by database rollback, or a committed database record followed by failed storage?
LARAVEL-FILES-04 — Explain why disks are not interchangeable filesystems
Section titled “LARAVEL-FILES-04 — Explain why disks are not interchangeable filesystems”Which assumptions fail when moving a Laravel disk from local storage to an S3-compatible object store?
LARAVEL-FILES-05 — Test an object-storage integration honestly
Section titled “LARAVEL-FILES-05 — Test an object-storage integration honestly”What does Storage::fake() prove, what does it omit, and which additional tests would you retain?
Configuration, exception handling, and logging
Section titled “Configuration, exception handling, and logging”LARAVEL-CONFIG-01 — Explain config() versus env()
Section titled “LARAVEL-CONFIG-01 — Explain config() versus env()”Why should application code use config() rather than env(), and how does configuration caching change runtime behavior?
LARAVEL-CONFIG-02 — Order framework caches in a deployment
Section titled “LARAVEL-CONFIG-02 — Order framework caches in a deployment”Explain what Laravel’s configuration, route, event, and view caches contain and where their generation belongs in an immutable deployment.
LARAVEL-CONFIG-03 — Separate exception reporting from rendering
Section titled “LARAVEL-CONFIG-03 — Separate exception reporting from rendering”Which details belong in diagnostic reporting but not an API response, and where should expected domain failures be translated?
LARAVEL-CONFIG-04 — Design safe structured logging
Section titled “LARAVEL-CONFIG-04 — Design safe structured logging”Design log context, severity, redaction, correlation, sampling, and failure behavior for HTTP requests and queued jobs.
LARAVEL-CONFIG-05 — Diagnose stale configuration across runtimes
Section titled “LARAVEL-CONFIG-05 — Diagnose stale configuration across runtimes”A rotated credential works in some requests but fails in queue workers. Diagnose the disagreement and design the deployment fix.
Artisan commands and task scheduling
Section titled “Artisan commands and task scheduling”LARAVEL-ARTISAN-01 — Design an automation-safe command
Section titled “LARAVEL-ARTISAN-01 — Design an automation-safe command”How should a batch command expose validation, progress, partial failure, output, and exit status to both humans and automation?
LARAVEL-ARTISAN-02 — Make a long command resumable
Section titled “LARAVEL-ARTISAN-02 — Make a long command resumable”Design a multi-hour import so interruption, rerun, memory growth, and duplicate effects remain safe.
LARAVEL-ARTISAN-03 — Explain scheduler triggering and time
Section titled “LARAVEL-ARTISAN-03 — Explain scheduler triggering and time”What actually invokes Laravel’s scheduler, and how do missed minutes, sub-minute tasks, timezones, and daylight-saving changes affect guarantees?
LARAVEL-ARTISAN-04 — Compare overlap and one-server locks
Section titled “LARAVEL-ARTISAN-04 — Compare overlap and one-server locks”Compare withoutOverlapping(), onOneServer(), and an isolatable command. Why can all still require idempotency?
LARAVEL-ARTISAN-05 — Diagnose a missing or duplicated scheduled run
Section titled “LARAVEL-ARTISAN-05 — Diagnose a missing or duplicated scheduled run”Build a diagnostic sequence for a scheduled settlement that was skipped or executed twice.
Deployment and long-running workers
Section titled “Deployment and long-running workers”LARAVEL-DEPLOY-01 — Plan a zero-downtime Laravel release
Section titled “LARAVEL-DEPLOY-01 — Plan a zero-downtime Laravel release”Plan release publication, caches, traffic switching, migrations, worker restarts, scheduler behavior, and verification.
LARAVEL-DEPLOY-02 — Explain why queue workers run stale code
Section titled “LARAVEL-DEPLOY-02 — Explain why queue workers run stale code”Why does publishing new PHP not update queue:work or Horizon processes, and how should they be replaced safely?
LARAVEL-DEPLOY-03 — Design a compatible schema change
Section titled “LARAVEL-DEPLOY-03 — Design a compatible schema change”Rename a heavily used column while old web and queue processes coexist and rollback remains possible.
LARAVEL-DEPLOY-04 — Prevent state leakage in long-lived runtimes
Section titled “LARAVEL-DEPLOY-04 — Prevent state leakage in long-lived runtimes”What state is often harmless under short-lived request assumptions but unsafe in queue or Octane workers, and how do you test it?
LARAVEL-DEPLOY-05 — Make rollback a real capability
Section titled “LARAVEL-DEPLOY-05 — Make rollback a real capability”What must remain compatible for a Laravel rollback to be more than switching code to an earlier release?
Laravel foundations reconciliation
Section titled “Laravel foundations reconciliation”LARAVEL-FOUNDATIONS-01 — Place a Laravel use case across boundaries
Section titled “LARAVEL-FOUNDATIONS-01 — Place a Laravel use case across boundaries”Place validation, authorization, transaction ownership, persistence, serialization, and side effects for a multi-model API operation.
LARAVEL-FOUNDATIONS-02 — Choose a synchronous or asynchronous mechanism
Section titled “LARAVEL-FOUNDATIONS-02 — Choose a synchronous or asynchronous mechanism”Choose between a direct call, event/listener, job, and notification, including ordering and failure visibility.
LARAVEL-FOUNDATIONS-03 — Locate a production guarantee
Section titled “LARAVEL-FOUNDATIONS-03 — Locate a production guarantee”For validation, authorization, uniqueness, atomicity, exactly-once claims, and output shape, identify the strongest Laravel or database boundary.
LARAVEL-FOUNDATIONS-04 — Choose a Laravel full-stack shape
Section titled “LARAVEL-FOUNDATIONS-04 — Choose a Laravel full-stack shape”Choose Blade, Livewire, Inertia, or a separate API/client from product and operational constraints rather than fashion.
Example answers
Section titled “Example answers”LARAVEL-LIFECYCLE-01 — Example answer
The web server sends the request to public/index.php. That loads Composer, requires bootstrap/app.php to obtain the application/container, captures the HTTP request, and calls handleRequest(). The application resolves the HTTP kernel, whose bootstrappers load environment and configuration, configure exception handling and facades, then register and boot providers. All providers are registered before any are booted.
The kernel sends the request through global middleware, then the router matches a route and runs route middleware before invoking the controller or closure. Middleware behaves like nested functions: request-side code runs inward, and response-side code runs outward in reverse order; any layer can short-circuit. The route result becomes a response, unwinds through both middleware layers, and returns to the application, which sends it and runs termination handling. I would not treat termination work as a durable background queue because it still shares process and failure constraints.
LARAVEL-CACHE-01 — Example answer
I first avoid caching the final authorized response if possible. I cache an authorization-neutral project summary under a key containing application/environment prefix, tenant ID, project ID, locale, and representation version, then apply the current policy and field filtering after retrieval. If output truly varies by permission set, I add a permission-version or role dimension rather than user ID unless every user is unique.
Writes invalidate after commit, while a finite jittered TTL repairs missed invalidation. A miss reads the source of truth, and cache failure degrades to bounded database access rather than changing authorization. I record hit rate, source latency, age, and invalidation failures. A broad membership change increments a tenant permission version. I never use flush() as tenant invalidation because it can clear unrelated keys.
LARAVEL-CACHE-02 — Example answer
The lock has an expiry so a process crash cannot leave permanent exclusion. That makes it a lease: after expiry another owner may acquire it even if the first process was merely paused and later resumes. Owner tokens prevent one process from releasing another’s lock, and refreshing can extend supported locks, but a network failure can still prevent renewal.
I size the lease from measured work, bound acquisition waits, release in finally, and observe contention and expiry. Most importantly, the business effect remains idempotent or uses a database uniqueness constraint, conditional transition, or external idempotency key. The lock reduces simultaneous work; it cannot turn a multi-system effect into exactly-once execution.
LARAVEL-CACHE-03 — Example answer
remember() allows every concurrent miss to compute, so I first add TTL jitter to prevent synchronized expiry. For data that tolerates bounded staleness, Cache::flexible() serves stale content while one deferred refresh is arranged; I may combine that with a short refresh lock. Other callers serve stale or retry briefly rather than waiting indefinitely.
Pre-warming is useful for predictable hot keys around a release or catalogue update, but it cannot cover every dynamic key and may warm unused data. A strict-freshness path can let one lock holder compute while others use a bounded fallback. I measure miss concurrency, refresh duration/errors, lock contention, stale age, and source load, then revise fresh/stale intervals from evidence.
LARAVEL-CACHE-04 — Example answer
Session middleware loads server-side state using the cookie identifier and persists changed state on the response path. Two concurrent requests can read the same snapshot and then overwrite each other in last-response order. I enable route session blocking only for endpoints that mutate shared session workflow state, using a shared lock-capable driver and realistic lock/wait times; unrelated stateless API calls should not serialize.
I regenerate the identifier after login or privilege elevation to prevent fixation. On logout I call logout, invalidate server-side session data, and regenerate the CSRF token. Cookie lifetime and server expiry are separate, so I do not promise a precise simultaneous timeout. I test concurrent mutations and verify secure cookie attributes in the deployed proxy topology.
LARAVEL-CACHE-05 — Example answer
Prefixes or Redis logical databases prevent key collisions, not resource contention. One memory or latency incident can evict cache and sessions, delay queues, fail rate limits, expire locks, and exhaust connections together. Sessions and queues often require different eviction and persistence choices from disposable cache data.
I inventory each workload’s durability, latency, memory, and degraded-mode requirements. At minimum I use separate connections/prefixes and budgets; for materially different guarantees I use separate Redis deployments or clusters. I prevent unbounded keys and large payloads, reserve connection headroom, and monitor eviction, memory, command latency, blocked clients, replication, errors, and oldest queue age per workload. I also define which requests fail closed if Redis is unavailable.
LARAVEL-FILES-01 — Example answer
I validate size and expected file class but still treat client name, extension, and MIME type as untrusted. I create a tenant-owned attachment row with a generated ID/key and pending status, stream to a private quarantine prefix, and persist the returned path. A queued scanner identifies actual content, rejects dangerous types, creates safe derivatives where needed, and atomically marks the record available.
Downloads resolve the attachment through its tenant, authorize with a policy, and then proxy or issue a short-lived URL. The original display name is sanitized metadata, never the object key. Stable operation IDs make retries harmless. Reconciliation finds pending rows, missing objects, or orphaned objects, and lifecycle rules remove abandoned multipart uploads.
LARAVEL-FILES-02 — Example answer
I use a public URL only for intentionally public, non-revocable assets such as versioned marketing images. For sensitive or frequently re-authorized documents, an application-proxied response applies policy on every request and supports immediate denial and audit, at the cost of application bandwidth.
For large private downloads, I authorize in Laravel and issue a short-lived object-store URL. It scales delivery well, but the URL is a bearer credential until expiry, so I keep the lifetime short and keep query strings out of logs and analytics. If immediate revocation is required, proxying or a CDN authorization mechanism is better. Storage::url() alone neither authorizes access nor proves reachability.
LARAVEL-FILES-03 — Example answer
I do not pretend the database and object store share a transaction. I model a small saga: create a pending row with a stable object key, upload, verify metadata, and conditionally mark available. If upload fails, the row records a retryable failure. If the object exists but the database transition fails, the retry uses the same key and verifies it rather than creating another logical upload.
A reconciliation job scans old pending rows and compares records with objects, removing true orphans after a safety window and alerting on live records with missing data. Deletion is also idempotent and stateful: mark pending deletion after commit, delete repeatedly safely, then finalize. Checksums and operation IDs distinguish retries from conflicting content.
LARAVEL-FILES-04 — Example answer
A local disk has real directories, low-latency metadata, atomic filesystem operations within limits, and often rename semantics. An object store has flat keys with prefix conventions; rename is generally copy then delete, listing and metadata calls are remote, and append or random mutation is not portable. Permissions map through adapter visibility but are not application authorization.
I also revisit URL generation, temporary signing, IAM, CORS, multipart upload, checksums, timeouts, retries, lifecycle rules, and cost. Local instance storage may be ephemeral or invisible to other hosts. I keep keys immutable, stream large data, store returned paths, and test the actual production adapter because a common Laravel API does not erase backend guarantees.
LARAVEL-FILES-05 — Example answer
Storage::fake() proves that application code selected the intended disk and path and wrote expected bytes, and it can assert presence or absence quickly. It does not prove deployment credentials, bucket policy, encryption, region, endpoint, presigned URLs, CORS, CDN headers, multipart behavior, throttling, or production size limits.
I keep most feature tests on the fake, plus a small isolated integration suite against the real provider or faithful compatible service for upload, download, temporary URL, metadata, and deletion. A deployment smoke test uses the deployed identity on a dedicated prefix. Failure tests simulate timeouts and lost responses, and reconciliation tests prove orphan and missing-object handling.
LARAVEL-CONFIG-01 — Example answer
Environment variables are deployment input, while configuration is the application-facing graph built from them. I call env() only inside configuration files and use config() elsewhere so tests, HTTP requests, commands, and workers all read named resolved values.
config:cache evaluates configuration into one artifact. Once cached, Laravel does not load .env in the normal boot path, so a direct application env() call may see only a system variable or its default. Editing .env does not update that artifact or an already booted worker. Deployment must validate environment input, build the cache for the release, and gracefully restart every long-lived runtime. Secrets remain outside logs and broadly distributed build artifacts.
LARAVEL-CONFIG-02 — Example answer
Configuration cache stores evaluated configuration; route cache serializes the route table and requires cacheable definitions; event cache stores discovered event/listener mappings; view cache precompiles Blade templates. None is an HTTP response cache. They are release artifacts and can become inconsistent with changed code.
I build dependencies and assets in an immutable release, supply validated production configuration at the appropriate secure stage, generate Laravel caches, run smoke checks, then atomically switch traffic. I never clear and rebuild caches inside a shared live directory while requests run. After the switch I restart queue, Horizon, Octane, scheduler, and other booted processes and verify their release IDs. optimize is convenient orchestration, not a complete deployment strategy.
LARAVEL-CONFIG-03 — Example answer
Reporting is for operators: it can include exception class and chain, stack, release, correlation ID, safe request/job identity, tenant, dependency operation, and retry context. Rendering is for the caller: a stable status and API error code with safe detail, never stack traces, SQL, paths, credentials, tokens, or internal topology.
I translate expected domain exceptions at the outer protocol boundary or in Laravel’s exception rendering configuration, preserving the cause. Validation, authorization, conflict, dependency failure, and programmer bugs remain distinct. I avoid catch-log-rethrow in every controller because central reporting already sees the exception and duplicate logs obscure evidence. Unknown failures render a generic 500 with a correlation ID; APP_DEBUG stays false in production.
LARAVEL-CONFIG-04 — Example answer
I use structured records with stable fields such as request/trace ID, release, tenant, actor, operation, job ID, queue, and attempt. Context crosses dispatch through bounded Laravel Context data or explicit payload fields and is cleared between operations in long-lived workers. Passwords, bearer tokens, cookies, authorization headers, payment data, and uncontrolled bodies are redacted at source.
Severity reflects response: expected 404s are not errors; dependency degradation may be warning; failed operations are error; paging levels are rare. Sampling or duplicate suppression retains counters and never removes all evidence of security events. I avoid synchronous remote handlers on latency-sensitive paths, bound local disk use, and monitor log-pipeline loss independently because logging itself can block or fail.
LARAVEL-CONFIG-05 — Example answer
I compare release and redacted configuration fingerprints across FPM and workers. Likely causes are a configuration cache built before rotation, direct env() calls, old booted queue processes, mixed release instances, or a worker host receiving different system variables. Editing .env alone updates none of those reliably.
The fix is to build or activate one immutable release with validated input, regenerate configuration cache in the correct environment, atomically switch web traffic, and gracefully restart all long-lived processes. I verify via bounded dependency smoke tests and release-aware logs. Future deployments publish configuration and code as one versioned unit, prohibit runtime env() access, and include startup validation so disagreement fails visibly rather than intermittently.
LARAVEL-ARTISAN-01 — Example answer
The command validates every argument and option, supports explicit non-interactive operation, and separates human progress from machine-readable output. It logs a run ID, parameters, release, counts, checkpoint, duration, and bounded error summaries. Zero means the declared operation succeeded; a contract-violating partial result returns non-zero even if most records worked.
For expected partial outcomes I define a separate status and durable failure report so automation can decide whether to retry. Destructive work has scoped input and dry-run support. The command delegates business behavior to an application operation, catches only failures it can classify or recover from, and preserves exceptions for central reporting. Tests assert validation, output, exit code, effects, partial failures, and safe rerun.
LARAVEL-ARTISAN-02 — Example answer
I create a durable import run with source checksum, parameters, status, counters, and a monotonic checkpoint. The command streams input or queries in stable cursor chunks, commits bounded units, releases large objects, and records progress. Each logical row has stable identity, and persistence uses uniqueness, upsert, or conditional transition so retry converges.
On a termination signal it stops taking new chunks, finishes or rolls back the current one, records the checkpoint, and exits within supervisor grace. A hard kill remains safe because the last unit is transactional and idempotent. Permanent row errors go to a durable report; transient failures stop or retry by policy. If independent work is slow, the command dispatches bounded idempotent jobs and tracks their completion instead of living for hours.
LARAVEL-ARTISAN-03 — Example answer
Laravel only evaluates due tasks; an external cron usually calls schedule:run each minute, or a supervised schedule:work process does so. If that trigger misses a minute, Laravel does not automatically replay every missed instant. Sub-minute tasks keep the scheduler process alive for the minute and deployments should interrupt it deliberately.
I use UTC unless business-local time is required. A daylight-saving transition can duplicate or omit a local clock time, so business-critical daily work uses a durable business-date key and remains idempotent. I monitor the external trigger separately from dispatched jobs, record scheduled instant and task identity, and inspect schedule:list only as configuration evidence—not proof that cron, locks, dispatch, or execution worked.
LARAVEL-ARTISAN-04 — Example answer
withoutOverlapping() prevents another scheduled instance while its cache lease is held. onOneServer() elects one scheduler host for a due task. An Isolatable command locks command execution only when called with --isolated, with a customizable scope and expiry. All require one shared atomic lock store across participants.
They solve different duplicate sources and remain leases. A paused process can outlive expiry, a network partition can split observations, a manual path may use another key, and a process can finish an external effect then die. I align lock IDs and expiries with measured scope/runtime, monitor suppression, and enforce final safety with a business execution key, conditional database state, or provider idempotency.
LARAVEL-ARTISAN-05 — Example answer
For a missing run I verify the external cron or scheduler worker, host and release, effective timezone and due expression, maintenance mode, scheduler output, and whether overlap or one-server lock suppressed it. Then I distinguish successful dispatch from queued execution and inspect queue age/failures.
For duplication I check multiple cron entries, several hosts without a shared onOneServer() store, inconsistent prefixes, lease expiry before runtime, daylight-saving repetition, manual invocation, and queue redelivery. Telemetry should join scheduled instant, business date, task/lock key, dispatch ID, and final operation ID. Settlement also has a database uniqueness constraint on business date/account, making both missing-run replay and duplicate execution safe.
LARAVEL-DEPLOY-01 — Example answer
I build an immutable release with locked dependencies and assets, validate configuration, and generate release-local Laravel caches. Compatible schema expansion lands before code that needs it. I publish the release, start or warm new web capacity, atomically switch traffic, and drain old instances. Health and logs expose release identity.
Then I gracefully restart queue:work, terminate Horizon for supervisor restart, reload Octane and other long-lived processes, and interrupt/restart sub-minute scheduler processes where relevant. New consumers understand old queued payloads, and producers do not emit payloads unsupported by active workers. I verify HTTP, database, queue, scheduler, storage, and critical dependencies. Destructive schema contraction waits for a later release after old code and rollback windows are gone.
LARAVEL-DEPLOY-02 — Example answer
queue:work boots Laravel once and loops, so replacing files does not rebuild its container or loaded classes. queue:restart publishes a cache restart timestamp; each worker checks between jobs and exits gracefully. A process supervisor must then start it on the new release. The shared cache must be reachable, and the command does not interrupt an active job.
For Horizon I use horizon:terminate and let the external supervisor restart the master and workers. Stop grace exceeds intended job shutdown and timeout, while jobs remain idempotent because hard termination and redelivery still occur. I verify new worker release IDs and queue health. I also preserve queued payload compatibility across releases rather than assuming a restart rewrites waiting jobs.
LARAVEL-DEPLOY-03 — Example answer
I use expand-and-contract. First add the new column without removing the old one and deploy code capable of reading old data. Then deploy dual-write or a compatibility layer, backfill old rows in bounded resumable batches, and verify counts and semantics. Switch reads to the new column only after evidence, while rollback code can still use the old representation.
After every old web and queue process is gone and the rollback window closes, stop writing the old column. A later release enforces new constraints and eventually drops it. I assess engine-specific locks, replication lag, and index build behavior separately from application deploy. migrate --force only disables confirmation; it does not make the rename or backfill online-safe.
LARAVEL-DEPLOY-04 — Example answer
Queue and Octane processes retain statics, mutable singletons, facade-resolved objects, global locale/timezone, frozen time, logger context, tenant context, SDK clients, open transactions, and accumulated listeners or arrays. FPM recycling may hide these leaks; a long-lived process exposes them across users or jobs.
I put operation state in scoped services or local variables, avoid injecting request/container objects into Octane singletons, reset explicit globals in lifecycle hooks, and bound process lifetime by jobs, time, memory, or Octane request count. Recycling is containment, not correctness. Tests run two different tenants sequentially through the same process and assert the second sees no first-tenant context; load tests watch memory slope and worker recycling.
LARAVEL-DEPLOY-05 — Example answer
Rollback requires the previous code to understand the current schema, configuration, assets, cache formats, encrypted data, and queued payloads. New database writes must remain readable by old code, and destructive migrations or one-way transforms must wait. A previous artifact and its dependency lock must still exist; switching a symlink is insufficient if workers, OPcache, or CDN assets remain new.
I define a tested rollback sequence for web traffic and every long-lived runtime, preserve configuration versions, and use expand-and-contract migrations. I also define when rollback is no longer safe and roll-forward is required. Smoke checks verify the restored release against database, queue, scheduler, storage, and dependencies, with release IDs proving no mixed fleet remains.
LARAVEL-FOUNDATIONS-01 — Example answer
A Form Request validates transport shape and may perform coarse request authorization. The controller calls a policy on the resolved tenant-scoped resources, maps named input, and invokes one application action. That action owns the transaction because the multi-model state change is its atomic invariant; database constraints preserve uniqueness and references under concurrency.
The controller does not serialize models directly: an API Resource shapes the response and the query eagerly loads its declared relationships. External side effects happen after commit through explicit jobs or an outbox when publication cannot be lost, with idempotent handlers. Model observers are reserved for genuinely model-local behavior, not hidden transaction orchestration. Each boundary is tested for its own claim.
LARAVEL-FOUNDATIONS-02 — Example answer
I use a direct call when the caller needs a result or ordered step. I publish an event for a fact with independent reactions, remembering normal listeners are synchronous and their failures share the dispatch path. A queued listener or explicit job crosses a serialization, retry, latency, and commit boundary; an explicit job is clearer when “perform this later” is the main intent.
A notification is recipient/channel-oriented delivery and may fan out into multiple queued jobs. Required workflow order stays explicit rather than hidden in listener order. For every asynchronous choice I define after-commit timing, payload compatibility, idempotency, retry limits, terminal failure, and observability. Indirection must buy ownership or failure isolation, not merely avoid a method call.
LARAVEL-FOUNDATIONS-03 — Example answer
Validation proves request shape at a boundary; a policy and tenant-scoped lookup authorize this actor/resource; the domain operation enforces transition rules. Database unique and foreign-key constraints are the strongest concurrent uniqueness and integrity boundary. A transaction provides atomicity only within its database/resource scope.
Queue uniqueness and cache locks reduce duplicate or overlapping work but are leases, not exactly-once guarantees. Durable conditional state, unique operation IDs, external idempotency keys, and reconciliation make repeated delivery converge. API Resources own output shape while eager-loading belongs to the query. I locate each claim at the lowest boundary capable of enforcing it under concurrency and failure, rather than attributing every guarantee to middleware or Eloquent.
LARAVEL-FOUNDATIONS-04 — Example answer
I start with client and deployment needs. Blade is simplest for server-rendered pages with limited interactivity. Livewire fits server-driven interactions when round trips, component lifecycle, and server-held state are acceptable. Inertia keeps Laravel routing/controllers while using client components and avoids inventing a separate API boundary, but still has client bundle and navigation-state concerns.
A separate API/client earns its operational cost when mobile or third-party clients, independent deployment, offline behavior, or a real platform contract requires it. Then versioning, authentication, CORS, observability, and duplicated client/server validation become explicit. Team capability and accessibility, SEO, latency, hosting, and testing constraints revise the choice; “modern” is not a requirement.
LARAVEL-ELOQUENT-01 — Example answer
When Eloquent executes a select, it hydrates each row into a model by setting raw attributes, synchronizing an original snapshot, marking the object as existing, and firing retrieved. Casts and accessors transform values when application code reads or writes attributes; they do not change the database schema. Assignment changes the in-memory model only.
On save(), Eloquent decides between insert and update from the model’s existence state. It compares current storage-form attributes with the original snapshot, applies timestamps, and runs the appropriate saving/creating or saving/updating event sequence. A successful persistence operation synchronizes the original state; wasRecentlyCreated distinguishes an inserted object during its lifetime.
The model is still a snapshot, not a live row. Another transaction can update the database without changing this PHP object. If concurrent edits matter, I add an explicit version predicate or a transaction and lock; neither dirty tracking nor calling refresh() is concurrency control.
LARAVEL-ELOQUENT-02 — Example answer
Mass-assignment configuration filters arrays passed through paths such as constructor filling, fill(), create(), and instance update(). A fillable allow-list says which keys those APIs may assign; guarded configuration blocks selected keys. Direct assignment such as $user->is_admin = true and then save() bypasses that filter, as do query-level updates, because trusted application code sometimes needs to set protected fields deliberately.
That means the guard is neither validation nor authorization. It does not prove a value has the right shape, that this actor may change it, or that a domain transition is legal. I validate the boundary, authorize the operation, map only named input fields, and derive authority-bearing values such as tenant or role from trusted context. Then mass-assignment protection is useful defense in depth.
I also enable prevention of silently discarded attributes in development or tests. That catches typos and stale fillable lists, but it does not make passing a whole request array a safe design.
LARAVEL-ELOQUENT-03 — Example answer
I use a built-in cast for a standard storage conversion such as a boolean, immutable date, JSON array, or backed enum. I use an Attribute accessor/mutator for model-specific computed get/set behavior. A custom cast earns its name when the conversion is reusable, needs dependencies, or maps several columns. An application-owned value object adds domain vocabulary and can reject invalid construction; it may be exposed by a custom cast without making Eloquent itself the domain boundary.
None of those choices changes the column type or replaces a constraint. An enum cast can still encounter an unknown legacy value, a JSON cast does not validate its keys, and a value object cannot stop a separate bulk SQL update from writing invalid data. I align storage constraints and migration strategy with the PHP representation.
I would test null behavior, round trips, serialization, dirty tracking, and legacy rows. For object casts I would also decide whether Eloquent’s default object-instance caching is desirable, because mutating the returned object can be synchronized back on save.
LARAVEL-ELOQUENT-04 — Example answer
isDirty() compares the current attributes with the model’s original snapshot before saving; getDirty() shows the pending storage changes. After a save, wasChanged() reports attributes changed by that most recent save on this instance. Those methods explain local model state, not what another transaction has done.
fresh() queries the row and returns a new model, leaving the old instance untouched. refresh() reloads the current instance in place, which discards unsaved local changes. I use either only when I deliberately want database-generated or externally changed values; an automatic refresh can erase the command I meant to persist.
None prevents a lost update because the check and update do not assert that the database still contains my original version. For optimistic locking I update with both the ID and expected version and require one affected row. For pessimistic locking I read with lockForUpdate() inside a transaction. The choice depends on contention, retry behavior, and how expensive a conflicting operation is.
LARAVEL-ELOQUENT-05 — Example answer
Order::where(...)->update(...) is a set-based database operation. Eloquent does not hydrate every matching order, so it cannot run per-instance setters or dispatch saving, updating, updated, and saved. If auditing and notifications live in those model hooks, the rows can be correct while those secondary effects are absent. Query-level deletion has the same event boundary.
I first decide whether the bulk operation or the side effects define the required semantics. If the update can remain set-based, I encode necessary derived values in the operation, rely on constraints for invariants, record one auditable batch fact, and enqueue an explicit restartable reconciliation or notification process. If each row must execute PHP behavior, I process by stable key with chunkById(), save instances, make handlers idempotent, and checkpoint for retries. That costs more queries and may hold operational risk longer.
I test both paths explicitly. I do not “fix” it by assuming an observer should somehow see SQL that never creates a model instance.
LARAVEL-ELOQUENT-06 — Example answer
I use an observer for a small persistence-lifecycle concern that must accompany every instance-based model write, such as deriving a normalized field or recording a focused audit fact. I avoid hiding network calls, large workflows, or cross-aggregate business orchestration in save(), because that creates latency, recursion, and paths that bulk updates silently skip.
Inside a database transaction, an ordinary observer can run before commit. A queued listener or external call may then observe no row, see old state, or survive a rollback. If work only makes sense after a successful commit, an observer can implement Laravel’s after-commit contract. The handler must still be idempotent because retries and process failures remain possible.
After-commit timing is not atomic delivery: the process can fail after commit but before publishing. For a high-value integration event, I write an outbox record in the same transaction and let a retried publisher deliver it. I also review saveQuietly(), withoutEvents(), and bulk writes as intentional bypasses of observer semantics.
LARAVEL-LIFECYCLE-02 — Example answer
The container can use reflection to inspect an unbound concrete class’s constructor, recursively build its concrete class dependencies, and inject them. That is why a controller depending only on constructible concrete services may need no registration.
It stops when construction is ambiguous or impossible. An interface needs a binding to a concrete implementation. A scalar such as a DSN needs an explicit value or contextual binding. A union, inaccessible constructor, or dependency whose own graph is unresolvable can also fail. Contextual bindings are useful when different consumers need different implementations of the same contract. I would keep construction policy in providers and expose dependencies through constructors; scattering app() calls makes the same graph harder to see and test even though the container can resolve it.
LARAVEL-LIFECYCLE-03 — Example answer
register() declares services: interface mappings, bindings, singletons, scoped services, and related construction policy. boot() performs application integration that may consume those services, such as registering macros, routes, listeners, or view composers. Laravel registers all providers before booting them, so a boot method can safely depend on bindings declared by another provider.
Putting integration work in register() can observe an incomplete container and create provider-order bugs. Putting remote calls or database work in boot() is also risky even though dependencies are available: it adds startup latency and can prevent HTTP, CLI, or worker startup. In an Octane worker, both phases normally run once when the worker boots, not once per request, so neither phase should capture tenant or user state. I would verify a suspected issue by logging provider execution and object identities across sequential requests.
LARAVEL-LIFECYCLE-04 — Example answer
Cache is a facade class, not the cache store itself. Its facade accessor identifies a service in the container. The base Facade resolves that service as the facade root, caches the root, and its __callStatic() forwards get and the arguments to the resolved object. The call only looks static at the application boundary.
That indirection is why Laravel can fake a facade or replace the underlying binding in tests, unlike a conventional static utility. The cost is dependency visibility: a constructor does not tell me that the class depends on cache. I am comfortable with a facade in thin Laravel glue, but if caching is part of an application service’s behavior or I want an application-owned contract, I would usually inject that dependency. In long-running or unusual test runtimes, I would also remember that the facade caches its resolved root.
LARAVEL-LIFECYCLE-05 — Example answer
My first hypothesis is request state captured by something whose lifetime is the Octane worker. I would inspect singletons, static properties, facade swaps, provider boot code, and constructors that capture the request, authenticated user, or tenant. Then I would reproduce with two sequential requests handled by the same worker, using different tenant markers and logging object IDs, tenant IDs, and correlation IDs. Fresh-application feature tests can miss exactly this lifetime bug.
The fix depends on ownership. I would pass tenant identity explicitly into an operation where possible, or make a tenant-context collaborator scoped so Laravel flushes it for each request lifecycle. I would avoid injecting a request into a singleton; a late resolver is an integration fallback, not my first choice for domain code. I would add the sequential-request regression test, inspect other mutable worker-lifetime state, deploy the change, and restart Octane workers so old objects and code cannot remain resident.
LARAVEL-LIFECYCLE-06 — Example answer
Constructor injection is my default for application services because it makes required collaborators visible, supports application-owned interfaces, and works with ordinary test doubles. A growing constructor is useful design feedback that the class may own too much.
I use facades selectively in thin framework-facing code where the Laravel service is stable and terse syntax improves readability. They remain testable because calls proxy a container object, but they hide dependencies and can encourage scope creep. I reserve direct container access for composition boundaries such as providers, framework factories, or cases where the framework itself requires dynamic resolution. Calling app() throughout business code is service location and hides the object graph. I would revise the choice when reuse outside Laravel, test friction, runtime lifetime bugs, or many ambient dependencies show that the convenience is obscuring design.
LARAVEL-ROUTING-01 — Example answer
The router matches the request’s HTTP method, host, and URI against the registered route collection, including parameter constraints. The selected route carries its action, name, defaults, and middleware metadata. Laravel expands middleware groups and aliases, applies middleware priority, and runs the resulting route pipeline.
Binding happens inside that pipeline through SubstituteBindings. Explicit binders run, then implicit binding can reflect typed action parameters and replace route strings with models or backed enums. A missing model or invalid bound enum normally becomes a 404, so the controller never runs. If all middleware calls $next, the route invokes its closure or resolves the controller through the container, injects the bound parameters and other dependencies, and normalizes the result into a response. I would inspect route:list -vv rather than infer the deployed route and middleware graph only from source files.
LARAVEL-ROUTING-02 — Example answer
The final order is composed from more than the route’s local list. Middleware can be global, inherited from groups, referenced by aliases, declared by a controller, or attached directly. Laravel resolves those declarations and applies its middleware priority ordering, so two priority-listed middleware may be reordered even if a route lists them differently.
I would run php artisan route:list -vv in the same release and environment to see expanded middleware, inspect bootstrap/app.php for group and priority customization, and check controller middleware. Then I would add a focused feature test or temporary correlation-aware logging around each handle() method to confirm inward and outward order and identify a short-circuit. I would also check the route cache and worker/release state; editing a route file does not change an already-built cached route collection.
LARAVEL-ROUTING-03 — Example answer
After a route matches, SubstituteBindings asks Laravel to resolve bindings. For implicit Eloquent binding, the placeholder and typed parameter correspond—{post} and Post $post. Laravel queries the model by its route key, replaces the scalar parameter with the model, and does not invoke the action if lookup fails; that becomes a 404.
The default key is normally the primary key. {post:slug} selects a key locally; Laravel 13 can also declare #[RouteKey('slug')], while overriding getRouteKeyName() is common in older code. The key should be unique and indexed. Soft-deleted rows are excluded unless the route opts into withTrashed(). If resolution needs special infrastructure behavior I can use explicit binding or customize model resolution, but I prefer a local binding key over global custom behavior when only one endpoint differs.
LARAVEL-ROUTING-04 — Example answer
Without scoping, Laravel can resolve the tenant and project independently, so /tenants/1/projects/99 might inject project 99 even when it belongs to tenant 2. Scoped binding resolves the child through the parent’s relationship and returns 404 when that object graph does not exist. That makes the URL identify a coherent nested resource and reduces cross-tenant enumeration.
It still says nothing about the current actor. A project may belong to tenant 1 while this user has no membership or lacks the requested ability, so a policy or equivalent authorization must still decide access. Tenant isolation also has to survive non-HTTP entry points, direct queries, queues, cache keys, and storage paths. I would feature-test an allowed child, an unrelated child, and a related but unauthorized child, checking both status and absence of leaked data.
LARAVEL-ROUTING-05 — Example answer
I would first reproduce against the actual production method, host, and path, then run route:list -vv in the active release. I am looking for whether the route exists, which overlapping route wins, its constraints and domain, and its expanded middleware. A dynamic or resource route may capture a literal path; Laravel 13’s domain-route precedence can also differ from Laravel 12 assumptions.
If the deployed graph is old, I would inspect whether route:cache was rebuilt from the current release, whether traffic points to the intended release, and whether long-running workers need restart. I would not fix one server with an ad hoc cache clear. The deployment should compile the route cache from the release, fail if compilation fails, switch releases coherently, and verify the registered route afterward. If the graph is current, I would trace binding and middleware short-circuits, since a binding miss can also produce a 404 before the controller.
LARAVEL-ROUTING-06 — Example answer
I use a route constraint when validity is purely transport shape, such as digits or a slug pattern. A string-backed enum binding is good for a small finite set where an unknown value should be a 404. Model binding fits when the segment directly identifies a model and automatic not-found behavior is correct; scoped model binding adds parent-child identity semantics.
Middleware is for cross-cutting request behavior shared by endpoints, especially when it needs to wrap the response or short-circuit before the action. I resolve inside the controller or application operation when lookup has operation-specific rules, several legitimate outcomes, or needs errors richer than not found. The decision triggers are the meaning of failure, required state, reuse, ordering, and visibility. I avoid hiding endpoint-specific business orchestration in middleware or global binding callbacks simply because those mechanisms are convenient.
LARAVEL-BOUNDARIES-01 — Example answer
Laravel resolves the typed Form Request through the container before invoking the controller. prepareForValidation() runs first, so I keep it to bounded normalization without side effects. Then authorize() runs if present; a denial stops the request as an authorization failure. Laravel constructs the validator from rules() and configuration, runs validation including registered after-hooks, and throws a validation exception on failure. Only after success does passedValidation() run, and then the controller receives the request.
The response semantics differ: browser validation normally redirects with flashed errors and input, JSON validation returns 422, and failed authorization normally returns 403. I use validated() or safe() to select the validated set, but I do not treat it as authorized or domain-valid. An exists rule can prove a row exists without proving the actor may use it, and passedValidation() is not a transaction boundary.
LARAVEL-BOUNDARIES-02 — Example answer
Validation checks the submitted representation: for example, status is a permitted string and project_id has the expected shape. Authorization checks whether this actor may update this project. A domain invariant might say an archived project cannot return to active, and it must hold for HTTP, CLI, and queued callers. A database constraint protects facts the database can enforce under concurrency, such as uniqueness or valid foreign-key relationships.
These layers complement each other. exists:projects,id does not prove tenant ownership. A unique rule improves the error but two concurrent requests may both pass, so the unique index is authoritative. I would map validated fields into an application operation, authorize the bound tenant-scoped model, enforce the transition inside the use case, and handle database constraint conflicts. Passing validation is only permission to continue evaluating the command.
LARAVEL-BOUNDARIES-03 — Example answer
I use a gate for an ability that is not naturally owned by one model, such as viewing an operations dashboard. I use a policy to group abilities around a resource type, such as viewing, creating, or updating a project. The enforcement call still has to exist—policy discovery alone does nothing—and Blade visibility is not server-side enforcement.
A policy before() can allow or deny across abilities: true allows, false denies, and null falls through. I keep broad administrator overrides small because “admin” can accidentally become cross-tenant superuser access. Policy methods can return an authorization Response with a message or denial status; normal denial is 403, while not-found semantics may deliberately conceal existence. I test both ordinary decisions and overrides, and remember that inline allowIf or denyIf does not run normal before/after hooks.
LARAVEL-BOUNDARIES-04 — Example answer
I first establish tenant context from authenticated, verified state; a host, header, or path value is only a locator until I prove the actor belongs to that tenant. Missing context fails closed and the context is scoped per request or job. HTTP nested binding can resolve a project through its tenant, and a policy then checks actor, ability, tenant, and resource.
Every query and write carries the tenant dimension, with tenant_id assigned server-side and database constraints added where they can preserve relationships. Cache and lock keys, storage paths, search filters, exports, and broadcast channels are tenant-partitioned. Jobs carry a server-derived tenant ID and restore context before resolving models; long-running workers must not retain it. I add two-tenant tests for reads, writes, jobs, and artifacts. A global Eloquent scope may reduce accidents, but raw queries and scope removal mean it is defense in depth, not the whole boundary.
LARAVEL-BOUNDARIES-05 — Example answer
I would stop assuming the successful HTTP policy covers delayed execution. I would inspect the serialized job payload, how the worker establishes tenant context, whether it resolves a model globally by ID, storage and cache key construction, and whether a stale singleton can retain another tenant. Queue fakes would explain why the existing feature test missed this.
The repair is to build the job from the authorized model and a server-derived tenant identifier, restore a request/job-scoped tenant context before model resolution, and query by both tenant and resource. I would decide explicitly whether permission is a dispatch-time snapshot or must be reauthorized when the job runs. Export paths, cache keys, and download authorization must also include the tenant. The regression test should execute the real job with two tenants, attempt an attacker-controlled ID, and assert no foreign data, file, cache entry, or download capability is produced.
LARAVEL-BOUNDARIES-06 — Example answer
I use 401 when valid authentication is required but missing or invalid. I use 403 when the actor is known and the policy denies the ability. I use 404 for a genuinely absent resource, and sometimes for a denied resource when consistently concealing existence is part of the endpoint’s policy. I use 422 when the submitted representation fails semantic validation.
The choice must be consistent across binding and authorization. Returning 404 for policy denial does little if response timing, messages, counts, or later side effects reveal that the resource exists. Conversely, changing every denial to 404 can make trusted clients harder to debug. I keep detailed denial reasons in access-controlled, rate-aware logs with tenant and correlation context, expose only the client contract that is necessary, and test the observable response for both nonexistent and unauthorized identifiers.
LARAVEL-RELATIONSHIPS-01 — Example answer
$post->comments() returns a HasMany relation, which is a query builder already constrained by the post’s key. I use it to add SQL conditions, ask for existence or an aggregate, or perform a relationship write. $post->comments resolves the dynamic property. If the relation is absent from the model’s loaded-relations cache, that access executes a lazy query; afterward it returns the cached Eloquent collection.
That cache can become stale if another operation creates a comment, so I reload or unset it only when I deliberately need a new snapshot. Parentheses also change counting: comments()->count() asks the database for a scalar, while comments->count() hydrates the collection unless it was already needed and loaded.
In a loop, I do not accept dynamic-property access blindly. I prepare the relation with a constrained eager load, or enable lazy-loading violations to expose the missing query plan. I also group added orWhere conditions so they cannot escape the relationship’s parent-key predicate.
LARAVEL-RELATIONSHIPS-02 — Example answer
I start with key ownership and cardinality. The model holding the foreign key declares belongsTo; the parent usually declares hasOne or hasMany. A real one-to-one also needs a unique database constraint. Many-to-many fits when both sides have many peers and the intermediate row is mostly association data. If that row has identity, permissions, soft deletion, or a substantial lifecycle, I promote it to a normal model rather than forcing it into a pivot.
A through relation is useful for traversing a stable intermediate path. A polymorphic relation fits when several model types share one association, but it trades away straightforward foreign-key enforcement and generates per-type query planning. I enforce a morph map so stored values are stable aliases, migrating existing class-name values before enabling it.
Eloquent declarations never create indexes, foreign keys, cascade rules, tenant constraints, or authorization. I put those guarantees in migrations and application policy, then test the actual key conventions and delete behavior.
LARAVEL-RELATIONSHIPS-03 — Example answer
with() belongs at the initial query boundary when I already know the response or operation needs a relation. It makes the graph visible before parents are retrieved. load() eager-loads after retrieval, which is useful when a later decision chooses an include; it can replace an existing loaded relation. loadMissing() enriches only absent relations, so composable code preserves a caller’s existing, possibly constrained result.
A small $with default is reasonable when nearly every use needs the relation. Frequent without() calls show that the default is too expensive. Laravel 13’s automatic eager loading can batch a missing relationship across an Eloquent collection when one member accesses it. That can reduce incidental N+1 queries, but also makes initiation implicit and can pull a large nested graph into memory.
I choose using endpoint query traces, cardinality, memory, and response needs. Strict lazy-loading violations are helpful diagnostics, but they only tell me a plan is missing; they do not decide which graph should be loaded.
LARAVEL-RELATIONSHIPS-04 — Example answer
I capture SQL for the complete request, including resource transformation and serialization, then group repeated query shapes and identify the first relationship property access. I seed several parents with uneven children because a one-record test hides growth. Typical triggers outside the controller are a Blade loop, a resource that reads $this->author, an appended accessor, nested resources, or logging code.
I fix the owning query with the narrow eager load the representation requires and use whenLoaded in resources so transformation cannot initiate the query. I also inspect direction. Loading posts.comments does not necessarily populate $comment->post; reverse access inside the child loop can create another N+1. I can restructure the loop, eager-load the inverse, or use chaperone() when parent hydration is a stable requirement.
Finally I add a query-budget integration test and assert response cardinality, not just a single SQL count. Production telemetry still matters because optional includes and real dataset shapes may not appear in the test.
LARAVEL-RELATIONSHIPS-05 — Example answer
Eager loading replaces repeated point queries with batched queries; it does not bound rows or memory. Loading 100 projects with every historical task and each task’s comments may use only three queries while hydrating hundreds of thousands of models. I inspect total rows, selected columns, nested cardinality, database time, hydration time, and peak memory alongside query count.
Then I match the query to the contract. I paginate or otherwise bound parents first, constrain children to the relevant status and time window, and include the keys Eloquent needs to match results. If the response needs only facts, I use withCount, withExists, or another aggregate instead of loading collections. If it is a report, a dedicated projection or query builder result may be clearer than an Active Record graph.
When a predicate both selects parents and defines visible children, I use withWhereHas or share the constraint so whereHas and with cannot drift. I verify SQL plans and indexes after shaping the result.
LARAVEL-RELATIONSHIPS-06 — Example answer
The controller or application query owns allowed includes and prepares an explicit graph, for example a constrained author relation plus withCount('comments'). The API resource owns representation and uses whenLoaded('author'), whenCounted('comments'), and whenPivotLoaded for fields that depend on prepared relationship state. I pass the relation name to whenLoaded, not $this->author, because evaluating the property first can lazy-load it.
Nested resources follow the same rule. Direct model serialization recursively includes whatever relations happen to be loaded, so returning models can change response shape when upstream code adds an eager load. Explicit resources make that contract deliberate. Hidden fields only affect output; they do not prevent a relation from being queried or held in memory.
I enable lazy-loading violations in tests, exercise the real resource with optional includes both present and absent, and assert query count, keys, authorization, and maximum collection sizes. The resource should never decide to fetch data merely because it knows how to display it.
LARAVEL-TESTING-01 — Example answer
I begin with the claim and list the mechanisms capable of breaking it. A pure calculation or value-object invariant belongs in a framework-free unit test because booting Laravel adds no confidence. If the claim depends on container wiring, middleware, policies, Eloquent, or serialization, I use a Laravel feature test and keep those mechanisms real.
When Laravel replaces infrastructure with a fake, I state the missing boundary. An HTTP fake does not prove the provider’s current contract; a storage fake does not prove object-store permissions. I add a focused real-driver integration test when those semantics carry material risk. Finally, a deployed smoke test covers what Laravel’s process cannot: web server, proxy, TLS, environment, worker, and credentials.
I do not try to make every test end-to-end. I use the narrowest boundary that includes the suspected failure and layer a small number of wider tests over high-risk seams. Each assertion targets an outcome or invariant, not merely that one implementation method was called.
LARAVEL-TESTING-02 — Example answer
For a tenant-owned update I would create two tenants and an authenticated user with explicit factory states, then send the real JSON request through the named route. I keep routing, middleware, scoped binding, the Form Request, policy, application action, Eloquent query, and resource real. I test an allowed update and a foreign identifier, asserting status and response shape, the exact persisted fields, unchanged foreign data, and absence of jobs or files on denial.
That catches gaps hidden by a controller unit test, such as binding before authorization or a query missing the tenant predicate. I avoid testing several requests in one method because Laravel’s simulated request lifecycle is designed around one request per test. I also remember what this test omits: Laravel dispatches the request internally, disables CSRF middleware, and does not exercise Nginx, TLS, proxy headers, or deployment configuration.
I would add a browser or deployed smoke test only for those outer concerns and keep a regression test at the narrow Laravel boundary for the authorization invariant.
LARAVEL-TESTING-03 — Example answer
RefreshDatabase is my default for database-backed feature tests. It brings the schema current and normally wraps the test in a transaction, so it is fast. DatabaseMigrations rolls back and reruns migrations between tests. DatabaseTruncation migrates initially and clears tables between tests. The latter strategies cost more but allow scenarios where a real commit must be observed or transaction coverage is unsuitable.
That distinction matters for after-commit jobs and listeners: an outer transaction that never commits can hide the release behavior. I put those cases in a focused commit-aware group instead of weakening production configuration. I also check extra database connections and setup performed outside the test transaction because they can leak rows.
SQLite is fine for fast tests whose claims use common relational behavior. I use the production engine for locking, concurrency, isolation, JSON operators, collations, generated columns, type coercion, engine-specific constraints, migrations, and raw SQL. Engine fidelity is part of the test boundary, not an all-or-nothing rule for the entire suite.
LARAVEL-TESTING-04 — Example answer
Queue::fake() replaces the queue and records dispatches, so it can prove the job class, payload identity, delay, chain, or queue selection. It does not serialize the job or run handle(). A job can therefore pass that test and fail because a model payload is stale, tenant context existed only in the request, worker dependencies cannot resolve, or retrying repeats an external effect.
I split confidence into layers. The HTTP feature test fakes the queue and proves an authorized, committed request dispatches scalar tenant and resource identity. A handler test creates realistic rows, fakes only the external provider, invokes the real job twice, and asserts an idempotent outcome. A commit-aware integration test proves rollback suppresses dispatch and commit releases it. For a critical path, a small test using the real queue driver and worker proves serialization and environment wiring.
That last test still does not prove every production outage mode, so I pair it with failed-job monitoring and operational smoke checks. The point is to label each test’s boundary honestly.
LARAVEL-TESTING-05 — Example answer
I fake the subsystem whose external effect is irrelevant to the current claim and keep the rest of the path real. Event::fake() proves dispatch but suppresses listeners, so I create factories first if their model events matter or fake only selected events. Mail and notification fakes prove intent and recipients, not provider credentials or delivery. A storage fake proves paths and contents against the fake disk, not object-store permissions or signed URLs.
For outbound HTTP I register explicit responses and enable Http::preventStrayRequests() so an unmodeled call fails instead of reaching the network. I retain a controlled provider contract test for important integrations. Time travel makes expiry assertions deterministic, but it does not model another process or the database server’s clock; callback-scoped helpers also ensure time is restored.
For every fake I can finish the sentence, “this test does not prove…”. If the omitted behavior threatens the invariant, I add a smaller number of real-boundary tests rather than removing useful fast fakes from the suite.
LARAVEL-TESTING-06 — Example answer
I first reproduce the failure with the same test order, random seed, and parallel-process count. Then I reduce it to the smallest failing pair and run both orders. I inspect mutated configuration, facade fakes, container singletons, static properties, frozen time, global exception handling, open transactions, and fixtures created outside RefreshDatabase; each test must establish and restore its own state.
For parallel failures I remember that Laravel automatically separates test databases, not every external resource. I check Redis and cache prefixes, locks, queue names, storage paths, ports, and third-party sandbox accounts. I namespace them with the parallel process token through lifecycle hooks or serialize a resource that cannot be partitioned. Configuration cache and an unexpected .env.testing value are also common sources of local-versus-CI differences.
If the bug appears only in a worker or Octane-like lifecycle, I add a test that processes two tenants in one process because ordinary feature tests rebuild the application per method. Finally I run the repaired test repeatedly and in parallel; one green run is weak evidence against a race.
LARAVEL-QUEUES-01 — Example answer
Dispatch serializes the job class and its job state into the selected connection and queue. Eloquent models carried through Laravel’s queue serialization cross mainly as class and identifier, then are queried again by the worker; request-local authentication, tenant context, open transactions, and container state do not cross. Loaded relationship names can also cause broad reloads, so I exclude them and query deliberately in handle().
A worker reserves the message, resolves handler dependencies, runs middleware and the handler, then acknowledges or deletes the message on normal completion. An exception or deliberate release() can return it for another attempt with backoff. If the worker dies or its reservation expires before acknowledgement, another worker may receive the same payload, including after the external effect already happened.
Attempt and exception limits or retryUntil() eventually make the job fail and record it through the failed-job provider. I therefore treat delivery as potentially repeated, use explicit scalar identity and payload-compatible code, make effects idempotent, and attach tenant, attempt, queue, and correlation context to failure telemetry.
LARAVEL-QUEUES-02 — Example answer
The job timeout or worker --timeout limits how long Laravel lets an attempt run; blocking I/O still needs its own client timeout. For database and Redis queues, retry_after is the reservation period after which an unacknowledged job becomes available again. SQS expresses the analogous boundary with its visibility timeout.
I set the Laravel worker timeout several seconds shorter than the reservation window. Then a stuck worker is terminated before the broker can give the same job to another worker. If the reservation expires first, worker B can start while worker A is still performing the effect. The handler must remain idempotent even with correct settings because crashes and lost acknowledgements still cause redelivery.
The process supervisor’s stop grace must be longer than the intended job shutdown window and must restart exited workers. In Horizon I also check supervisor timeout and tries configuration rather than assuming job properties always win. I validate the longest legitimate runtime, external-client timeouts, retry delay, reservation setting, and deployment termination as one timeline, then alert on runtime approaching the reservation boundary.
LARAVEL-QUEUES-03 — Example answer
This is an uncertain-outcome problem: the absence of a local completion row does not mean the provider failed. Before dispatch I create a payment-attempt record with a stable provider reference and idempotency key. Every attempt sends the same key, so the provider returns the original result rather than charging again. The job first checks durable state, but that check is only an optimization; the provider key is the external concurrency guard.
After a successful response I record the provider transaction and move the attempt from pending to captured with an atomic conditional update. Reprocessing a captured attempt returns normally. Transient transport errors escape for bounded retry with backoff and jitter; permanent rejections become explicit terminal states instead of consuming every retry. A short overlap lock can reduce concurrent calls but does not replace idempotency.
If the provider response is lost, a reconciliation process queries by the stable reference and completes the local record. I alert on attempts stuck in uncertain states. Tests invoke the handler twice and simulate success followed by a failed local write, while a provider contract test confirms its idempotency semantics.
LARAVEL-QUEUES-04 — Example answer
I use a direct call when the caller requires a result or when the step is an explicit, ordered part of the use case. I use an event to publish a fact with independent reactions, accepting that ordinary Laravel listeners run synchronously and their exceptions share the dispatcher’s failure path. I do not hide required workflow order behind event fan-out.
A listener implements ShouldQueue when the reaction can cross a latency and retry boundary. Then its payload, commit timing, idempotency, and monitoring need the same treatment as any job. I use an explicit job when “perform this command later” is the main intent and the caller should choose queue, delay, chain, or batch behavior directly.
A notification is recipient-oriented delivery through channels such as mail, database, or broadcast. Queueing it may create one job per recipient-channel combination, so I plan fan-out and channel routing. These mechanisms can compose—an OrderPaid event may have a queued projection listener and a notification listener—but each extra indirection must buy independent ownership or failure isolation, not merely avoid a method call.
LARAVEL-QUEUES-05 — Example answer
ShouldBeUnique acquires a cache lock at dispatch and suppresses another queued job with the same key while the first remains locked. ShouldBeUniqueUntilProcessing releases that lock just before processing, so it prevents duplicate backlog rather than concurrent execution. Both require every dispatcher host to share a lock-capable cache.
WithoutOverlapping is execution middleware. It acquires a lock for a key when an attempt starts and releases or discards a conflicting attempt according to configuration. A release consumes an attempt, and an expiry must recover from crashes without expiring during valid work. Its shared-key option can coordinate different job classes.
None provides the business guarantee of idempotency. Locks expire, caches fail, operators retry failed jobs, and some paths may bypass the intended dispatcher. An idempotent handler uses durable uniqueness, conditional state transitions, or an external idempotency key so repeated execution converges on one outcome. I use uniqueness to reduce redundant queue entries, overlap middleware to limit concurrent work, and idempotency as the final correctness boundary.
LARAVEL-QUEUES-06 — Example answer
I commit the order and a durable event or outbox record in one database transaction. Plain afterCommit() dispatch prevents listeners from seeing rolled-back state, but an outbox is necessary if losing publication between database commit and broker acceptance would violate the invariant. A relay can retry publication, so every consumer remains idempotent.
I model OrderPlaced as a fact and give independent reactions separate queued listeners—for example inventory projection, analytics, and customer delivery—rather than making their ordering accidental. Required sequential business steps stay in an explicit application workflow or chain. The customer message is a notification because it is recipient- and channel-oriented; channel queues and rate limits can differ. A listener that needs committed rows uses the after-commit listener contract.
Each handler has bounded retries, classified permanent failures, correlation and tenant context, and an observable terminal state. Failed-job storage is paired with alerts and a runbook that decides whether replay is safe under current code and data. I monitor oldest-job age and failures per queue, reconcile outbox records and uncertain effects, and test rollback, redelivery, and partial fan-out independently.
LARAVEL-AUTH-01 — Example answer
On login, the session guard asks its configured provider to retrieve a user using the non-password credentials. It then validates the submitted password against the stored adaptive hash. On success I regenerate the session ID to prevent fixation; the session stores the authenticated user identifier, not the password. A later request presents the session cookie, and the guard uses the provider to retrieve the current user.
The guard determines the credential mechanism, while the provider determines user retrieval. A separate admin guard is therefore not an authorization system. I still use policies for administrative abilities. I also rate-limit login attempts, keep failure messages resistant to account enumeration, and use Hash::needsRehash() during a controlled hash migration.
On logout I call the guard’s logout method, invalidate server-side session state, and regenerate the CSRF token. Clearing only the browser cookie is insufficient if a stolen session identifier remains valid. For sensitive applications I define what happens to remember-me credentials, other active sessions, and personal tokens rather than assuming one logout revokes every credential type.
LARAVEL-AUTH-02 — Example answer
For a first-party server-rendered browser or SPA on the same top-level domain, I prefer Laravel’s session cookie; Sanctum adds the stateful SPA routing needed for API endpoints without exposing a bearer token to JavaScript. For a mobile app or a simple first-party automation client, a narrowly scoped, expiring Sanctum personal access token is usually enough.
I choose Passport only when the product is actually an OAuth2 authorization server for independently operated clients: it needs authorization-code flows, PKCE, refresh tokens, client registration, consent, or client credentials. That operational and security surface is wasteful for a single first-party script. OAuth2 delegates API access; it is not a login identity protocol by itself.
For workforce SSO or “sign in with” federation, I consume an established OpenID Connect provider and validate identity assertions for issuer, audience, signature, nonce, and expiry. The decision follows client trust, credential storage capability, delegation, revocation, and identity federation requirements—not whether the endpoint URL starts with /api.
LARAVEL-AUTH-03 — Example answer
Sanctum’s SPA mode uses Laravel’s session guard, not a personal access token. The SPA and API must share a top-level domain. I configure Sanctum’s stateful domains, the session cookie domain and security flags, enable stateful API middleware, and allow credentialed CORS only for intended origins when the frontend is cross-origin.
The SPA first requests /sanctum/csrf-cookie, which establishes the CSRF cookie and session context. It then submits login credentials with cookies and the decoded XSRF value in the expected header. On later routes protected by auth:sanctum, Sanctum checks the authenticated session first; it can also accept a bearer personal token when no stateful session applies.
Because cookies are automatically attached, state-changing requests still require CSRF protection. CORS is only a browser response policy and does not authorize the caller. I also avoid wildcard credentialed origins and verify proxy, scheme, domain, and SameSite configuration in the deployed topology. Finally, policies still authorize resources: successful session authentication establishes identity only.
LARAVEL-AUTH-04 — Example answer
I issue a token only after authenticated, recently confirmed intent, give it a human-readable device or integration name, minimum abilities, and an explicit expiry. Sanctum stores a hash and returns the plain token once, so the client must put it in an operating-system secret store or another appropriate server-side secret mechanism. I never place it in a URL or logs.
Every API request uses TLS and auth:sanctum; ability middleware limits credential purpose, while a policy and tenant scope still decide resource access. I expose last-used and creation metadata, let users revoke individual devices, prune expired records, and rate-limit issuance and sensitive endpoints. Rotation means issue a replacement, deploy it, verify use, then revoke the old token with a short controlled overlap where necessary.
For role changes, tenant removal, password reset, or suspected theft I define whether to revoke one token, all personal tokens, and sessions. Hashing protects tokens at rest in the database, but a captured bearer value remains replayable until expiry or revocation. Short lifetimes, narrow abilities, anomaly telemetry, and fast revocation reduce that window; they do not create proof of possession.
LARAVEL-AUTH-05 — Example answer
auth:sanctum proves that the session or bearer token maps to a user. The projects:write ability says this credential may request that class of operation; it does not say the user owns this project or still belongs to its tenant. For first-party SPA requests, Sanctum deliberately treats tokenCan() as true, making policy enforcement even more important.
I derive the active tenant from trusted host, route, and membership context, then resolve the project through that tenant or apply an explicit tenant predicate. I run ability middleware at the route when bearer scope matters and call the update policy with the resolved project. The use case still enforces state-transition invariants, and the database preserves constraints under concurrency.
I test each layer independently: missing or invalid credential gives 401; a token lacking the ability is denied; an authenticated user without project permission receives the chosen 403 or concealed 404; a foreign identifier cannot resolve; and no denied path writes, queues, caches, or broadcasts. A broad token ability can narrow a credential, but it must never widen the user’s application permissions.
LARAVEL-AUTH-06 — Example answer
I make reset responses similar for existing and nonexistent accounts and rate-limit by useful combinations of account and network identity. Laravel’s password broker issues a random, single-purpose, expiring token; I send it only through the intended channel, keep it out of logs, validate it once, hash the new password, and rotate remember state. Email verification similarly proves mailbox control at link-use time, not legal identity.
After reset I apply a documented risk policy: high-value products normally revoke other sessions, personal tokens, and OAuth refresh tokens, then notify the owner through an independent message. MFA enrollment, replacement, or disabling requires recent authentication. TOTP secrets need strong protection, recovery codes are shown once and rotated after use, and rate limits prevent online guessing.
Recovery cannot devolve into “support can remove MFA.” Support follows an auditable workflow with identity evidence appropriate to the asset, dual control for exceptional cases, security notifications, and a cooling-off period where warranted. I test expired and reused links, enumeration behavior, stolen-session revocation, recovery-code reuse, tenant membership changes, and every bypass available to administrators.