Laravel interviews test your command of Eloquent ORM, routing conventions, service container, authentication patterns, queue jobs, and testing. This guide covers 50 common questions — with concise answers and real code examples.
Quick reference
| Topic | Most asked questions |
|---|---|
| Architecture | Service container, service providers, facades |
| Routing | Route groups, middleware, model binding |
| Eloquent ORM | Relationships, scopes, mutators, eager loading |
| Blade | Directives, components, layouts |
| Authentication | Sanctum, Breeze, Jetstream, gates/policies |
| API Development | API resources, rate limiting, versioning |
| Queues & Jobs | Dispatching, retry, horizon |
| Events & Listeners | Event broadcasting, real-time |
| Testing | PHPUnit, Pest, HTTP testing, mocking |
| Performance | Caching, N+1 prevention, Octane |
Architecture
1. What is Laravel's service container?
The service container (IoC container) resolves class dependencies automatically via reflection and binding.
// Binding in a service provider
$this->app->bind(PaymentGateway::class, StripeGateway::class);
// Automatic resolution — Laravel injects UserRepository via constructor
public function __construct(UserRepository $repo)
{
$this->repo = $repo;
}
// Manual resolution
$gateway = app(PaymentGateway::class);
The container reads type hints and resolves the concrete class for you, enabling dependency injection throughout the framework.
2. What are service providers?
Service providers are the central place to bootstrap app components. Every provider has register() (bind things to the container) and boot() (code that runs after all providers are registered).
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(Cache::class, RedisCache::class);
}
public function boot(): void
{
// Runs after all providers registered
View::share('appName', config('app.name'));
}
}
Core providers (like RouteServiceProvider, EventServiceProvider) are listed in config/app.php.
3. What are facades?
Facades provide a static interface to classes in the service container.
// Behind the scenes, Cache:: resolves CacheManager from the container
Cache::put('key', 'value', 3600);
// Equivalent without facade
app('cache')->put('key', 'value', 3600);
| Facade | Underlying class |
|---|---|
Cache |
Illuminate\Cache\CacheManager |
DB |
Illuminate\Database\DatabaseManager |
Route |
Illuminate\Routing\Router |
Auth |
Illuminate\Auth\AuthManager |
Event |
Illuminate\Events\Dispatcher |
Queue |
Illuminate\Queue\QueueManager |
In tests, facades can be faked: Cache::fake().
4. What is the difference between @inject and constructor injection?
| Method | Where used | Notes |
|---|---|---|
| Constructor injection | Controllers, services, jobs | Preferred — explicit |
| Method injection | Controller actions | Auto-resolved by the router |
@inject directive |
Blade templates | Injects a service into a view |
app() / resolve() |
Anywhere | Manual — use sparingly |
// Method injection — router resolves PaymentService
public function store(Request $request, PaymentService $svc): JsonResponse
{
$svc->charge($request->all());
}
// Blade @inject
@inject('stats', 'App\Services\StatsService')
<p>Users: {{ $stats->totalUsers() }}</p>
5. What is the difference between config() and env()?
env()reads from the.envfile directly. Should only be called inside config files — never in controllers or views.config()reads the compiled config cache, which is fast.
// Wrong — breaks when config is cached
$key = env('STRIPE_SECRET');
// Correct — reads from config/services.php
$key = config('services.stripe.secret');
Run php artisan config:cache in production to cache all config files into a single file.
Routing
6. What are route model bindings?
Laravel automatically resolves an Eloquent model by its primary key (or a custom column) from the route segment.
// Implicit binding — {user} → App\Models\User::findOrFail($id)
Route::get('/users/{user}', [UserController::class, 'show']);
// Custom key — resolve by slug
Route::get('/posts/{post:slug}', [PostController::class, 'show']);
// Explicit binding in RouteServiceProvider
Route::bind('user', fn ($value) => User::where('uuid', $value)->firstOrFail());
Returns 404 automatically if the model is not found.
7. How do route groups work?
Route groups share attributes (middleware, prefix, name prefix, namespace) across many routes.
Route::prefix('api/v1')
->middleware(['auth:sanctum', 'throttle:60,1'])
->name('api.')
->group(function () {
Route::get('/users', [UserController::class, 'index'])->name('users.index');
Route::post('/users', [UserController::class, 'store'])->name('users.store');
});
8. What is the difference between Route::resource and Route::apiResource?
Route::resource generates 7 routes (index, create, store, show, edit, update, destroy).Route::apiResource generates 5 routes — omits create and edit (no HTML form views needed for APIs).
Route::resource('photos', PhotoController::class);
Route::apiResource('photos', PhotoController::class);
// Limit to specific actions
Route::resource('photos', PhotoController::class)->only(['index', 'show']);
Route::resource('photos', PhotoController::class)->except(['destroy']);
9. How do you create and apply middleware?
php artisan make:middleware EnsureEmailIsVerified
class EnsureEmailIsVerified
{
public function handle(Request $request, Closure $next): Response
{
if (! $request->user()->hasVerifiedEmail()) {
return response()->json(['message' => 'Email not verified.'], 403);
}
return $next($request);
}
}
Register and apply:
// Register in bootstrap/app.php (Laravel 11) or Http/Kernel.php (Laravel 10)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias(['verified.email' => EnsureEmailIsVerified::class]);
})
// Apply to route
Route::get('/dashboard', DashboardController::class)->middleware('verified.email');
10. What are named routes and how do you redirect to them?
Route::get('/users/{id}/profile', [UserController::class, 'profile'])->name('users.profile');
// Generate URL
$url = route('users.profile', ['id' => 42]);
// Redirect
return redirect()->route('users.profile', ['id' => 42]);
// Check current route in Blade
@if (request()->routeIs('users.profile'))
<li class="active">Profile</li>
@endif
Eloquent ORM
11. What are the Eloquent relationship types?
| Relationship | Method | Example |
|---|---|---|
| One-to-one | hasOne / belongsTo |
User → Phone |
| One-to-many | hasMany / belongsTo |
Post → Comments |
| Many-to-many | belongsToMany |
User ↔ Role |
| Has-one-through | hasOneThrough |
Country → Capital (through Continent) |
| Has-many-through | hasManyThrough |
Country → Posts (through User) |
| Polymorphic one | morphOne / morphTo |
Comment → Image or Video |
| Polymorphic many | morphMany / morphTo |
Post or Video → Comments |
| Many-to-many poly | morphToMany / morphedByMany |
Post/Video → Tags |
// Many-to-many with pivot table
class User extends Model
{
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class)
->withPivot('assigned_at')
->withTimestamps();
}
}
$user->roles()->attach($roleId, ['assigned_at' => now()]);
$user->roles()->sync([1, 2, 3]);
$user->roles()->detach($roleId);
12. What is eager loading and why does it matter?
Eager loading prevents the N+1 query problem by loading relationships in a single additional query.
// N+1 problem — executes 1 + N queries
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // fires a query per post
}
// Eager loading — executes 2 queries total
$posts = Post::with('author')->get();
// Multiple relationships
$posts = Post::with(['author', 'comments.author', 'tags'])->get();
// Constrained eager loading
$posts = Post::with(['comments' => function ($query) {
$query->where('approved', true)->orderBy('created_at', 'desc');
}])->get();
Use withCount() to load counts without hydrating models:
$posts = Post::withCount('comments')->get();
echo $post->comments_count;
13. What are local and global query scopes?
Local scopes are reusable query constraints you call explicitly.
Global scopes are automatically applied to all queries on a model.
class Post extends Model
{
// Local scope — called as Post::published()->get()
public function scopePublished(Builder $query): void
{
$query->where('status', 'published');
}
// Local scope with parameter
public function scopeOfType(Builder $query, string $type): void
{
$query->where('type', $type);
}
}
// Usage
$posts = Post::published()->ofType('news')->latest()->get();
// Global scope — always applied
class ActiveScope implements Scope
{
public function apply(Builder $builder, Model $model): void
{
$builder->where('active', true);
}
}
class User extends Model
{
protected static function booted(): void
{
static::addGlobalScope(new ActiveScope());
}
}
// Remove global scope for a query
User::withoutGlobalScope(ActiveScope::class)->get();
14. What are Eloquent accessors and mutators?
Accessors transform attribute values when you access them.
Mutators transform values when you set them.
// Laravel 9+ unified accessor/mutator
class User extends Model
{
protected function name(): Attribute
{
return Attribute::make(
get: fn ($value) => ucfirst($value),
set: fn ($value) => strtolower($value),
);
}
// Computed (virtual) attribute — not stored
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => "{$this->first_name} {$this->last_name}",
);
}
}
$user->name = 'JOHN'; // stored as 'john'
echo $user->name; // outputs 'John'
echo $user->full_name; // 'John Doe'
15. What is the difference between save(), create(), and fill()?
| Method | Mass assignment guard | Persists? |
|---|---|---|
create($data) |
Checks $fillable/$guarded |
Yes |
fill($data) |
Checks $fillable/$guarded |
No — call save() after |
forceFill($data) |
Bypasses guard | No |
save() |
N/A — saves current state | Yes |
update($data) |
Checks $fillable/$guarded |
Yes |
// create() — mass assign + persist
$user = User::create(['name' => 'Alice', 'email' => 'alice@example.com']);
// fill() + save()
$user = new User();
$user->fill(['name' => 'Alice', 'email' => 'alice@example.com']);
$user->save();
// update existing
$user->update(['name' => 'Bob']);
// First-or-create patterns
$user = User::firstOrCreate(['email' => 'alice@example.com'], ['name' => 'Alice']);
$user = User::updateOrCreate(['email' => 'alice@example.com'], ['name' => 'Alice']);
16. What is soft deleting?
Soft delete sets a deleted_at timestamp instead of removing the record.
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
}
$post->delete(); // sets deleted_at, record stays in DB
Post::all(); // excludes soft-deleted records
Post::withTrashed()->get(); // includes soft-deleted
Post::onlyTrashed()->get(); // only soft-deleted
$post->restore(); // clears deleted_at
$post->forceDelete(); // truly removes the record
Migration:
$table->softDeletes(); // adds nullable deleted_at timestamp
Blade Templating
17. What are Blade components?
Components are reusable, encapsulated UI pieces — similar to React components.
php artisan make:component Alert
// app/View/Components/Alert.php
class Alert extends Component
{
public function __construct(public string $type = 'info') {}
public function render(): View
{
return view('components.alert');
}
}
<!-- resources/views/components/alert.blade.php -->
<div class="alert alert-{{ $type }}">
{{ $slot }}
</div>
<!-- Named slots -->
<div class="card">
<div class="card-header">{{ $header }}</div>
<div class="card-body">{{ $slot }}</div>
</div>
<!-- Usage -->
<x-alert type="danger">Something went wrong!</x-alert>
<x-card>
<x-slot:header>User Profile</x-slot:header>
<p>Profile content here.</p>
</x-card>
18. What are the key Blade directives?
| Directive | Purpose |
|---|---|
@if / @elseif / @else / @endif |
Conditionals |
@unless |
Inverted @if |
@isset / @empty |
Null/empty checks |
@foreach / @endforeach |
Loops |
@forelse / @empty / @endforelse |
Loop with empty state |
@for / @while |
C-style loops |
@auth / @guest |
Auth state |
@can / @cannot |
Authorization |
@include / @includeIf |
Include sub-views |
@extends / @section / @yield |
Template inheritance |
@csrf |
CSRF token field |
@method('PUT') |
HTML form method spoofing |
@stack / @push |
Stackable content sections |
Authentication & Authorization
19. What is the difference between Laravel Breeze, Jetstream, and Sanctum?
| Package | Purpose | Auth type | Frontend |
|---|---|---|---|
| Breeze | Simple auth scaffold | Session | Blade, Livewire, React, Vue |
| Jetstream | Full auth + teams | Session | Livewire or Inertia |
| Sanctum | API token auth | Token / Session | SPA or mobile |
| Passport | Full OAuth2 server | OAuth2 | Any |
Use Sanctum for your own SPA or mobile app talking to your Laravel API.
Use Passport when you need to issue OAuth2 tokens to third-party clients.
20. How do gates and policies differ?
Gates are closures for simple, model-agnostic authorization.
Policies are classes that organize authorization around a model.
// Gate — defined in AuthServiceProvider
Gate::define('view-admin', fn (User $user) => $user->isAdmin());
// Check
if (Gate::allows('view-admin')) { ... }
abort_if(Gate::denies('view-admin'), 403);
// Policy
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id;
}
}
// In controller
$this->authorize('update', $post);
// In Blade
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan
21. How does Laravel Sanctum work for SPA authentication?
Sanctum issues CSRF-cookie-based session auth for same-domain SPAs and token-based auth for mobile/third-party clients.
// SPA — CSRF cookie (no tokens)
// 1. GET /sanctum/csrf-cookie to set XSRF-TOKEN cookie
// 2. Login via POST /login — sets session cookie
// 3. All subsequent requests use cookies automatically
// Token-based (mobile/API clients)
$token = $user->createToken('mobile')->plainTextToken;
// Client sends: Authorization: Bearer {token}
// Scopes
$token = $user->createToken('mobile', ['read:posts', 'write:posts'])->plainTextToken;
$user->tokens()->where('id', $id)->delete(); // revoke specific token
$user->tokens()->delete(); // revoke all
API Development
22. What are API Resources?
API Resources transform Eloquent models into JSON — a presentation layer between your models and API responses.
// php artisan make:resource UserResource
class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'role' => $this->whenLoaded('role', fn () => $this->role->name),
'created_at' => $this->created_at->toISOString(),
'links' => [
'self' => route('users.show', $this->id),
],
];
}
}
// Usage
return new UserResource($user);
return UserResource::collection(User::paginate(20));
// Resource collection with meta
class UserCollection extends ResourceCollection
{
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
'meta' => ['total_active' => User::where('active', true)->count()],
];
}
}
23. How do you implement rate limiting?
// config/routes — named rate limiter
RateLimiter::for('api', function (Request $request) {
return $request->user()
? Limit::perMinute(60)->by($request->user()->id)
: Limit::perMinute(10)->by($request->ip());
});
// Apply to route group
Route::middleware(['auth:sanctum', 'throttle:api'])->group(...);
// Custom per-route limit
Route::post('/login', LoginController::class)
->middleware('throttle:5,1'); // 5 per minute
// Multiple limits
RateLimiter::for('uploads', function (Request $request) {
return [
Limit::perMinute(3),
Limit::perDay(100)->by($request->user()->id),
];
});
24. How do you version a Laravel API?
URL versioning (most common):
Route::prefix('v1')->name('v1.')->group(base_path('routes/v1.php'));
Route::prefix('v2')->name('v2.')->group(base_path('routes/v2.php'));
Header versioning:
Route::middleware('api.version:v2')->group(function () { ... });
Structure approach:
app/Http/Controllers/
Api/
V1/UserController.php
V2/UserController.php
Queues & Jobs
25. How do you create and dispatch a job?
php artisan make:job SendWelcomeEmail
class SendWelcomeEmail implements ShouldQueue
{
use Queueable;
public function __construct(private readonly User $user) {}
public function handle(Mailer $mailer): void
{
$mailer->to($this->user->email)->send(new WelcomeMail($this->user));
}
public function failed(Throwable $exception): void
{
// Notify developer or mark user
}
}
// Dispatch
SendWelcomeEmail::dispatch($user);
SendWelcomeEmail::dispatch($user)->onQueue('emails');
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5));
// Dispatch synchronously (bypasses queue)
SendWelcomeEmail::dispatchSync($user);
26. What are queue workers and how do you configure retries?
# Start worker
php artisan queue:work redis --queue=emails,default --tries=3 --timeout=60
# Process one job and stop
php artisan queue:work --once
# Fresh worker (good for development)
php artisan queue:listen
class SendWelcomeEmail implements ShouldQueue
{
public int $tries = 3;
public int $timeout = 30;
public int $backoff = 60; // seconds before retry
// Exponential backoff
public function backoff(): array
{
return [1, 5, 10]; // seconds between retries
}
// Only retry if exception is retriable
public function retryUntil(): DateTime
{
return now()->addHours(2);
}
}
27. What is Laravel Horizon?
Horizon is a dashboard and configuration manager for Redis queues.
// config/horizon.php — per-environment worker pools
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['default', 'emails', 'notifications'],
'balance' => 'auto',
'processes' => 10,
'tries' => 3,
],
],
],
php artisan horizon # start Horizon
php artisan horizon:pause # pause all workers
php artisan horizon:continue
php artisan horizon:terminate # graceful shutdown
Dashboard at /horizon shows throughput, wait times, failed jobs, and metrics per queue.
Events & Listeners
28. How do events and listeners work in Laravel?
// Define event
class OrderShipped
{
public function __construct(public readonly Order $order) {}
}
// Define listener
class SendShipmentNotification implements ShouldQueue
{
public function handle(OrderShipped $event): void
{
Mail::to($event->order->user)->send(new ShipmentMail($event->order));
}
}
// Register in EventServiceProvider (or autodiscovery)
protected $listen = [
OrderShipped::class => [
SendShipmentNotification::class,
UpdateInventory::class,
],
];
// Fire event
event(new OrderShipped($order));
OrderShipped::dispatch($order); // alternative
29. What is event broadcasting?
Broadcasting pushes server-side events to the browser via WebSockets (Laravel Reverb, Pusher, Ably).
class OrderStatusUpdated implements ShouldBroadcast
{
public function __construct(public readonly Order $order) {}
public function broadcastOn(): Channel
{
return new PrivateChannel('orders.' . $this->order->id);
}
public function broadcastWith(): array
{
return ['status' => $this->order->status];
}
}
// Laravel 11 — start Reverb server
php artisan reverb:start
// Frontend (Laravel Echo + Pusher.js)
Echo.private(`orders.${orderId}`)
.listen('OrderStatusUpdated', (e) => {
console.log(e.status);
});
Testing
30. How do you test a Laravel API endpoint?
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserControllerTest extends TestCase
{
use RefreshDatabase;
public function test_authenticated_user_can_view_profile(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user, 'sanctum')
->getJson('/api/users/' . $user->id);
$response->assertOk()
->assertJsonStructure(['data' => ['id', 'name', 'email']])
->assertJsonPath('data.email', $user->email);
}
public function test_unauthenticated_request_returns_401(): void
{
$this->getJson('/api/users/1')->assertUnauthorized();
}
}
31. What are model factories?
Factories generate fake Eloquent model instances for testing.
class UserFactory extends Factory
{
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'password' => Hash::make('password'),
'email_verified_at' => now(),
];
}
// State modifier
public function admin(): static
{
return $this->state(fn () => ['role' => 'admin']);
}
public function unverified(): static
{
return $this->state(fn () => ['email_verified_at' => null]);
}
}
// Usage
$user = User::factory()->create();
$admin = User::factory()->admin()->create();
$users = User::factory()->count(10)->create();
// With relationship
$post = Post::factory()->for(User::factory())->create();
$posts = Post::factory()->count(3)->has(Comment::factory()->count(5))->create();
32. What is the difference between RefreshDatabase and DatabaseTransactions?
| Trait | Mechanism | Speed | Notes |
|---|---|---|---|
RefreshDatabase |
Runs migrations fresh before suite | Slow (first run), fast (cached) | Full isolation |
DatabaseTransactions |
Wraps each test in a transaction, rolls back | Fast | Doesn't work with multiple DB connections or queue workers |
| No trait | No cleanup | — | Use for read-only tests |
// Fastest setup for most test suites
class ExampleTest extends TestCase
{
use RefreshDatabase; // migrations + seeders if needed
public function test_example(): void { ... }
}
33. How do you mock external services in Laravel tests?
// Mock a service class
public function test_sends_email_on_registration(): void
{
Mail::fake();
$this->postJson('/api/register', [
'name' => 'Alice',
'email' => 'alice@example.com',
'password' => 'secret123',
])->assertCreated();
Mail::assertSent(WelcomeMail::class, fn ($mail) =>
$mail->hasTo('alice@example.com')
);
}
// Queue fake
Queue::fake();
dispatch(new ProcessOrder($order));
Queue::assertPushed(ProcessOrder::class);
// HTTP client fake
Http::fake([
'stripe.com/*' => Http::response(['id' => 'ch_123'], 200),
'sms.api.com/*' => Http::sequence()->push(['status' => 'sent']),
]);
Performance & Caching
34. How do you cache in Laravel?
// Store for 1 hour
Cache::put('users.count', User::count(), 3600);
// Retrieve with default
$count = Cache::get('users.count', 0);
// Remember pattern — most common
$users = Cache::remember('all.users', 3600, fn () => User::all());
// Forever
Cache::forever('settings', Settings::all());
// Remove
Cache::forget('users.count');
Cache::flush();
// Tags (Redis/Memcached only)
Cache::tags(['users', 'admins'])->remember('admin-list', 3600, fn () =>
User::where('role', 'admin')->get()
);
Cache::tags(['users'])->flush(); // invalidate all user caches
Drivers: file (default), redis, memcached, dynamodb, database, array (test).
35. How do you prevent N+1 queries?
// Detect in dev — add to AppServiceProvider::boot()
Model::preventLazyLoading(! app()->isProduction());
// Fix: eager load relationships
$posts = Post::with(['author', 'comments.author'])->get();
// Load after retrieval
$posts->load('comments');
$posts->loadCount('comments');
$posts->loadMissing('author'); // only if not already loaded
// Subquery selects for aggregates
$users = User::addSelect([
'latest_post_title' => Post::select('title')
->whereColumn('user_id', 'users.id')
->latest()
->limit(1),
])->get();
Security
36. How does Laravel prevent SQL injection?
Laravel uses PDO parameter binding for all query builder and Eloquent calls.
// Safe — parameter bound
User::where('email', $request->email)->first();
DB::table('users')->where('email', '=', $request->email)->first();
// Raw expression — SAFE — value is still bound
User::whereRaw('email = ?', [$request->email])->first();
// Dangerous — never concatenate user input
DB::statement("SELECT * FROM users WHERE email = '" . $email . "'"); // NEVER DO THIS
37. How does Laravel protect against CSRF?
Laravel generates a CSRF token per session and validates it on POST, PUT, PATCH, DELETE requests.
<!-- Include token in forms -->
<form method="POST">
@csrf
...
</form>
// Exclude certain routes in VerifyCsrfToken middleware
protected $except = [
'stripe/*',
'api/*', // APIs typically use token auth instead
];
For SPAs: Sanctum's /sanctum/csrf-cookie endpoint sets the XSRF-TOKEN cookie, which Axios/fetch sends back as the X-XSRF-TOKEN header.
38. What is mass assignment and how is it prevented?
Mass assignment allows setting model attributes via an array. Guarded against untrusted user input via $fillable and $guarded.
class User extends Model
{
// Allowlist — recommended
protected $fillable = ['name', 'email', 'password'];
// OR blocklist (less safe if you add columns later)
protected $guarded = ['role', 'is_admin'];
}
// If user sends ['role' => 'admin'] in request, it's ignored
User::create($request->all()); // safe — only fillable attributes used
User::create($request->only(['name', 'email', 'password'])); // even safer
Advanced Topics
39. What is Laravel's task scheduling?
// app/Console/Kernel.php (Laravel 10) or routes/console.php (Laravel 11)
Schedule::job(new GenerateDailyReport)->dailyAt('01:00');
Schedule::command('emails:prune')->weekly()->mondays()->at('08:00');
Schedule::call(fn () => Setting::set('last_ping', now()))->everyFiveMinutes();
Schedule::exec('backup.sh')->daily()->runInBackground()->sendOutputTo('/var/log/backup.log');
// Prevent overlapping tasks
Schedule::job(new LongRunningJob)->everyMinute()->withoutOverlapping();
// Run in production only
Schedule::job(new SyncJob)->hourly()->environments('production');
# Add to server's crontab — runs every minute
* * * * * cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1
40. What are observers?
Observers group multiple model event listeners into a single class.
// php artisan make:observer UserObserver --model=User
class UserObserver
{
public function created(User $user): void
{
SendWelcomeEmail::dispatch($user);
}
public function updated(User $user): void
{
if ($user->isDirty('email')) {
$user->sendEmailVerificationNotification();
}
}
public function deleted(User $user): void
{
$user->posts()->delete();
}
}
// Register in AppServiceProvider::boot()
User::observe(UserObserver::class);
Model events: creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored.
41. What is Laravel Octane?
Octane boots your application once and keeps it in memory using Swoole or RoadRunner, eliminating bootstrap overhead per request.
php artisan octane:install --server=swoole
php artisan octane:start --workers=8 --task-workers=4
Performance gains: 5-10× more requests/second compared to php-fpm.
Gotchas — avoid state leaking between requests:
// Bad — static state persists across requests in Octane
class OrderService
{
public static array $cache = [];
}
// Good — use the DI container
$this->app->scoped(OrderService::class); // fresh per request
42. How do you use database transactions?
// Automatic rollback on exception
DB::transaction(function () {
$order = Order::create([...]);
Payment::create(['order_id' => $order->id, ...]);
$order->sendConfirmationEmail();
});
// Manual control
DB::beginTransaction();
try {
// ...
DB::commit();
} catch (Throwable $e) {
DB::rollBack();
throw $e;
}
// Retry on deadlock
DB::transaction(function () { ... }, attempts: 5);
43. What is a repository pattern in Laravel?
The repository pattern decouples data access from business logic, making code testable and swappable.
// Interface
interface UserRepositoryInterface
{
public function findByEmail(string $email): ?User;
public function paginate(int $perPage): LengthAwarePaginator;
}
// Eloquent implementation
class EloquentUserRepository implements UserRepositoryInterface
{
public function findByEmail(string $email): ?User
{
return User::where('email', $email)->first();
}
public function paginate(int $perPage): LengthAwarePaginator
{
return User::latest()->paginate($perPage);
}
}
// Bind in service provider
$this->app->bind(UserRepositoryInterface::class, EloquentUserRepository::class);
// In test — swap with a mock
$this->app->bind(UserRepositoryInterface::class, InMemoryUserRepository::class);
44. What are pipelines?
Laravel's Pipeline sends an object through a series of stages (pipes) — great for multi-step processing.
$result = Pipeline::send($order)
->through([
ValidateInventory::class,
ApplyDiscount::class,
ChargePayment::class,
SendConfirmation::class,
])
->thenReturn();
// Pipe implementation
class ApplyDiscount
{
public function handle(Order $order, Closure $next): Order
{
if ($order->user->isPremium()) {
$order->applyDiscount(0.1);
}
return $next($order);
}
}
45. What is Laravel Livewire?
Livewire is a full-stack component framework that lets you write reactive UIs in PHP without JavaScript.
class Counter extends Component
{
public int $count = 0;
public function increment(): void
{
$this->count++;
}
public function render(): View
{
return view('livewire.counter');
}
}
<!-- resources/views/livewire/counter.blade.php -->
<div>
<button wire:click="increment">+</button>
<span>{{ $count }}</span>
</div>
Livewire sends AJAX requests on user interaction and surgically updates the DOM — no page reload.
46. What is the difference between has() and whereHas()?
// has() — filter models that have a relationship (any)
$usersWithPosts = User::has('posts')->get();
$usersWithManyPosts = User::has('posts', '>=', 5)->get();
// whereHas() — filter based on relationship constraints
$usersWithPublishedPosts = User::whereHas('posts', function ($query) {
$query->where('status', 'published');
})->get();
// doesntHave / whereDoesntHave
$usersWithNoPosts = User::doesntHave('posts')->get();
47. What is dependency injection in controllers?
Laravel automatically injects dependencies into controller constructors and action methods.
class PostController extends Controller
{
// Constructor injection — persists for all actions
public function __construct(private readonly PostRepository $repo) {}
// Method injection — resolved fresh per action
public function store(StorePostRequest $request, ImageService $images): JsonResponse
{
$data = $request->validated();
$data['image'] = $images->upload($request->file('image'));
$post = $this->repo->create($data);
return response()->json(new PostResource($post), 201);
}
}
48. What are Form Requests?
Form Requests extract validation and authorization logic out of controllers.
// php artisan make:request StorePostRequest
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'body' => ['required', 'string', 'min:10'],
'category' => ['required', 'exists:categories,id'],
'tags' => ['array'],
'tags.*' => ['exists:tags,id'],
];
}
public function messages(): array
{
return [
'category.exists' => 'Please select a valid category.',
];
}
}
// Controller — $request is already validated
public function store(StorePostRequest $request): JsonResponse
{
$post = Post::create($request->validated());
return response()->json(new PostResource($post), 201);
}
49. How do you handle file uploads?
// Store uploaded file
$path = $request->file('avatar')->store('avatars', 's3');
// With custom filename
$path = $request->file('avatar')
->storeAs('avatars', $user->id . '.jpg', 's3');
// Validation
$request->validate([
'avatar' => ['required', 'image', 'mimes:jpg,png,webp', 'max:2048'],
'docs' => ['required', 'array', 'max:5'],
'docs.*' => ['file', 'mimes:pdf', 'max:10240'],
]);
// Serving private files
return Storage::disk('s3')->response('avatars/' . $filename);
return Storage::temporaryUrl('avatars/' . $filename, now()->addMinutes(30)); // S3 signed URL
50. What are common Laravel anti-patterns?
| Anti-pattern | Problem | Fix |
|---|---|---|
| Fat controllers | Logic in controller, untestable | Move to service/action classes |
env() outside config |
Breaks config cache | Use config() everywhere, env() only in config files |
DB::select() for everything |
Bypasses Eloquent features | Use query builder or Eloquent; raw SQL as last resort |
User::all() |
Loads entire table | Paginate: User::paginate(20) |
| No eager loading | N+1 queries | Use with(), enable preventLazyLoading() in dev |
| Synchronous heavy tasks | Slow responses | Dispatch to queue |
| No form requests | Bloated controllers | Extract to FormRequest classes |
| Testing with real HTTP | Slow, fragile | Use Http::fake(), Mail::fake(), Queue::fake() |
Comparison
Laravel vs Django vs Rails vs Spring Boot
| Feature | Laravel | Django | Rails | Spring Boot |
|---|---|---|---|---|
| Language | PHP | Python | Ruby | Java/Kotlin |
| Philosophy | Expressive, developer joy | Pragmatic, batteries-included | Convention over config | Enterprise-grade |
| ORM | Eloquent (ActiveRecord) | Django ORM | Active Record | JPA/Hibernate |
| Template engine | Blade | Django templates / Jinja2 | ERB / Haml | Thymeleaf |
| Auth | Sanctum / Breeze / Fortify | django-allauth / rest_framework | Devise | Spring Security |
| Queue | Horizon + Redis | Celery + Redis | Sidekiq + Redis | Spring Batch |
| Async/reactive | Octane (Swoole) | Async views (ASGI) | async Action Controller | WebFlux |
| Learning curve | Low | Medium | Low | High |
| Performance | Medium | Medium | Medium | High |
| Best for | SaaS, APIs, full-stack PHP | Data-heavy web apps | Rapid prototyping | Enterprise / microservices |
Common mistakes
| Mistake | Why it's wrong | Fix |
|---|---|---|
env() in controllers |
Breaks when config is cached | Use config('app.key') |
User::all() in views |
Full table scan, no caching | Paginate + cache |
| Lazy loading relationships | N+1 queries | with() or preventLazyLoading() |
| No queue for emails | Request hangs for seconds | SendEmail::dispatch($user) |
| Putting logic in Blade | Hard to test | Move to Presenters or View Models |
Skipping $fillable |
Mass assignment vulnerability | Always define $fillable |
| Not using transactions | Partial writes on failure | Wrap related writes in DB::transaction() |
Committing .env |
Exposes secrets | .env in .gitignore, use .env.example |
FAQ
Q: What PHP version does Laravel 11 require?
A: PHP 8.2 or higher. Laravel 10 requires PHP 8.1+.
Q: When should I use Eloquent vs the query builder vs raw SQL?
A: Eloquent for CRUD and relationships; query builder for complex queries or performance-sensitive reports; raw SQL only for database-specific features unavailable in the builder.
Q: How is service container different from a singleton?
A: The container manages the full lifecycle — it can create a new instance per resolve (bind), create once and share (singleton), or create once per request (scoped). A PHP singleton is a design pattern; the container abstracts this at the framework level.
Q: What is the difference between dispatch() and dispatchSync()?
A: dispatch() pushes the job onto the queue driver. dispatchSync() runs the job immediately in the current process (useful for testing and local dev with QUEUE_CONNECTION=sync).
Q: How do I speed up Laravel in production?
A: php artisan config:cache, route:cache, view:cache, event:cache — cache everything. Use Redis for cache, session, and queue. Consider Octane with Swoole for high-throughput APIs.
Q: What is the difference between belongsTo and hasOne?
A: They're two sides of the same relationship. The model that holds the foreign key uses belongsTo; the model on the other side uses hasOne (or hasMany). Example: Post has user_id column → Post::belongsTo(User::class) and User::hasMany(Post::class).