Interview Prep

Common Laravel interview questions with detailed answers.

Clear

Clean syntax

Built-in authentication Eloquent ORM Security features RESTful routing Large community support Laravel development fast aur secure hota hai.
Example
Laravel ke faide:

Clean aur readable syntax
Authentication built-in
Database handle karna easy
Security strong
API banana easy
Interview Tip

Laravel ke faide: Clean aur readable syntax Authentication built-in Database handle karna easy Security strong API banana easy

View full answer →

MVC stands for Model, View, Controller.

Model → Handles database logic View → UI (Blade files) Controller → Business logic It separates concerns, making code organized and maintainable.
Example
User registration system:

Model → User data save karega
Controller → Validation karega
View → Registration form show karega
Interview Tip

MVC ka matlab hai Model, View aur Controller. Model database se deal karta hai View frontend hota hai Controller logic handle karta hai Is se code clean aur organized rehta hai.

View full answer →

Laravel is a PHP framework used to build modern, secure, and scalable web applications. It follows the MVC (Model-View-Controller) architecture and provides built-in features like routing, authentication, middleware, and ORM (Eloquent).

Laravel is a PHP framework used to build modern, secure, and scalable web applications. It follows the MVC (Model-View-Controller) architecture and provides built-in features like routing, authentication, middleware, and ORM (Eloquent). Laravel simplifies complex tasks such as database management, security, and API creation. It helps developers write clean and maintainable code.
Interview Tip

Agar aap hostel management system bana rahe ho, Laravel: Login system ready deta hai Database easily connect karta hai Role-based access control deta hai

View full answer →

Query scopes are reusable query constraints defined as methods on Eloquent models, keeping query logic organized and DRY.

Query scopes encapsulate commonly used WHERE clauses into named methods: Local scopes: - Defined as methods with 'scope' prefix - Called without the prefix: Post::published() - Can accept parameters: Post::inCategory(5) - Chainable with other query methods Global scopes: - Apply constraints to ALL queries for a model - Examples: SoftDeletes (only non-deleted records) - Can be removed with withoutGlobalScope() Benefits: DRY code, readable queries, testable constraints, easy maintenance.
Example
<?php
// Local scopes on Post model
public function scopePublished($query)
{
    return $query->whereNotNull('published_at')
                ->where('published_at', '<=', now());
}

public function scopeInCategory($query, $categoryId)
{
    return $query->where('category_id', $categoryId);
}

public function scopePopular($query)
{
    return $query->where('views_count', '>=', 1000);
}

// Usage - clean, readable queries
$posts = Post::published()->inCategory(5)->popular()->get();

// Dynamic scoping
$query = Post::query();
if ($request->has('status')) {
    $query->where('status', $request->status);
}
$posts = $query->get();
Interview Tip

Show how scopes make queries readable: "Post::published()->popular()->latest()" is self-documenting compared to nested where() calls.

View full answer →

Laravel uses exception handling via the bootstrap/app.php handler. Exceptions are caught globally and rendered as responses or logged.

Laravel\'s exception handling: 1. The app handler catches all uncaught exceptions 2. Exceptions are rendered as HTTP responses (404, 403, 422, 500) 3. Custom exception pages in resources/views/errors/ 4. report() method for logging 5. render() method for response generation Best practices: - Use try-catch for expected failures (payment, API calls) - Create custom exception classes for domain errors - Use abort() for early exits with specific status codes - Handle validation exceptions gracefully - Log unexpected exceptions for debugging
Example
<?php
// Custom exception
class PaymentFailedException extends \RuntimeException
{
    public function __construct(
        string $message = 'Payment processing failed',
        int $code = 422,
        ?\Throwable $previous = null
    ) {
        parent::__construct($message, $code, $previous);
    }
}

// Usage in service
public function processPayment(Payment $payment): void
{
    try {
        $this->gateway->charge($payment);
    } catch (\Exception $e) {
        Log::error('Payment failed', ['error' => $e->getMessage()]);
        throw new PaymentFailedException($e->getMessage(), 422, $e);
    }
}

// Handler
public function register(): void
{
    $this->reportable(function (PaymentFailedException $e) {
        // Custom reporting logic
    });
}

// Custom error page
// resources/views/errors/422.blade.php
Interview Tip

Discuss the difference between report() (logging) and render() (response). Mention custom exception pages and the abort() helper.

View full answer →

Sessions store data on the server (identified by a session ID cookie). Cookies store data directly in the browser, limited to ~4KB.

Sessions vs Cookies: Sessions: - Data stored on the server (file, database, Redis) - Session ID stored in a cookie on the client - Unlimited size (server storage) - More secure (data never leaves server) - Expiration controlled server-side Cookies: - Data stored directly in the browser - Limited to ~4KB per cookie - Sent with every HTTP request - Visible to the user (can be inspected) - Can have expiration set by server In Laravel: - Session: session()->put('key', 'value') or $request->session() - Cookie: Cookie::make('key', 'value') or $request->cookie('key') - Session driver: file, cookie, database, Redis, memcached
Example
<?php
// Session usage
session(['key' => 'value']);
$value = session('key');

// Request session
$request->session()->put('cart', $cartItems);
$cart = $request->session()->get('cart');
$request->session()->forget('cart');

// Cookie usage
// Set cookie in response
return response($content)->cookie('name', 'value', 60);

// Read cookie
$value = $request->cookie('name');

// Encrypt cookies (automatic in Laravel)
// Cookie::queue(Cookie::make('name', 'value', 120));

// Session configuration (.env)
SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_ENCRYPT=true
Interview Tip

Emphasize security: sessions are more secure because sensitive data stays on the server. Mention Laravel's session encryption and CSRF protection.

View full answer →

Model observers group model event listeners into dedicated classes, providing a clean way to hook into Eloquent lifecycle events.

Observers respond to Eloquent model events (created, updated, deleted, saved, etc.) and are organized in dedicated classes: Events observed: retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored, forceDeleted Benefits over $dispatchesEvents: - Multiple handlers for the same event - Organized, testable code - Clear separation of concerns - Easier to enable/disable Register observers in AppServiceProvider or model's boot() method.
Example
<?php
// php artisan make:observer PostObserver --model=Post

class PostObserver
{
    public function created(Post $post): void
    {
        // Auto-publish to social media
        SocialMediaJob::dispatch($post);
        // Clear cache
        Cache::forget('posts:all');
    }
    
    public function updated(Post $post): void
    {
        if ($post->isDirty('published_at')) {
            $post->user->notify(new PostPublishedNotification($post));
        }
        Cache::forget('posts:all');
    }
    
    public function deleted(Post $post): void
    {
        Cache::forget('posts:all');
        Storage::disk('public')->delete($post->thumbnail);
    }
}

// Register in AppServiceProvider
public function boot(): void
{
    Post::observe(PostObserver::class);
}
Interview Tip

Compare observers to events/listeners: "Observers are model-specific, events are application-wide. Use observers when the logic is tied to a specific model."

View full answer →

Events represent something that happened. Listeners react to events. This decouples the triggering code from the side effects.

Events and Listeners: Events: - Represent something that happened (UserRegistered, OrderPlaced) - Carry data about what happened - Dispatched with event() helper or Dispatchable trait - Decouple the main action from side effects Listeners: - React to specific events - Perform side effects (send email, update cache, sync data) - One event can have multiple listeners - Can be queued for background processing Benefits: - Open/Closed principle: add new behaviors without modifying existing code - Testability: test listeners independently - Decoupling: core logic doesn't know about side effects - Multiple responses to one event without code changes
Example
<?php
// Event class
class OrderPlaced
{
    use Dispatchable, SerializesModels;
    
    public function __construct(
        public readonly Order $order
    ) {}
}

// Listener
class SendOrderConfirmation
{
    public function __construct(private Mailer $mailer) {}
    
    public function handle(OrderPlaced $event): void
    {
        $this->mailer->to($event->order->user->email)
                     ->send(new OrderConfirmationMail($event->order));
    }
}

// EventServiceProvider
protected $listen = [
    OrderPlaced::class => [
        SendOrderConfirmation::class,
        UpdateInventory::class,
        NotifyAdmin::class,
    ],
];

event(new OrderPlaced($order)); // Triggers all 3 listeners
Interview Tip

Explain that listeners can be queued: "A listener can implement ShouldQueue for background processing, like sending emails without slowing the request."

View full answer →

The service container (IoC) is Laravel's dependency injection container that manages class dependencies and performs automatic injection.

The service container is the heart of Laravel\'s architecture: 1. Bind: Register how a class should be resolved $app->bind(Service::class, ConcreteService::class) 2. Singleton: Register so only one instance is created $app->singleton(Cache::class, RedisCache::class) 3. Resolve: Get an instance from the container $service = app(Service::class) 4. Auto-resolution: If not bound, the container resolves automatically using reflection How it works: - Constructor injection: Automatically resolves type-hinted dependencies - Method injection: Resolves parameters in controller methods - Interface binding: Bind interfaces to concrete implementations The container makes dependency injection natural in Laravel.
Example
<?php
// Binding
$this->app->bind(PostRepositoryInterface::class, function ($app) {
    return new EloquentPostRepository(new Post);
});

$this->app->singleton(Mailer::class, function ($app) {
    return $app->make('mailer');
});

// Constructor injection - container auto-resolves
public function __construct(
    private PostRepositoryInterface $repository,
    private Mailer $mailer
) {}

// Method injection in controllers
public function store(Request $request, PostService $service)
{
    $post = $service->create($request->validated());
}

// Manual resolution
$service = app(PostService::class);
$service = app()->make(PostService::class);
Interview Tip

Explain that auto-resolution works without explicit binding when all constructor dependencies can be resolved. Binding is needed for interfaces or when you want custom behavior.

View full answer →

Dependency injection is a design pattern where a class receives its dependencies from outside rather than creating them internally.

Dependency Injection (DI) means a class doesn\'t create its own dependencies — they\'re injected (usually via constructor): Without DI (tight coupling): class OrderService { private $mailer; // Creates its own dependency public function __construct() { $this->mailer = new Mailer(); } } With DI (loose coupling): class OrderService { public function __construct(private Mailer $mailer) {} // Injected } Benefits: - Loose coupling: swap implementations easily - Testability: inject mocks/stubs in tests - Single Responsibility: class doesn\'t manage dependencies - Flexibility: change implementations without changing the class Laravel\'s container automates DI: it reads type hints and injects the correct implementation.
Example
<?php
// Without DI - hard to test, tightly coupled
class ReportService
{
    public function generate()
    {
        $pdf = new PdfGenerator(); // Can't swap this
        $mailer = new Mailer();     // Can't mock this
        // ...
    }
}

// With DI - testable, flexible
class ReportService
{
    public function __construct(
        private PdfGeneratorInterface $pdf,
        private MailerInterface $mailer
    ) {}
    
    public function generate(): Report
    {
        $report = $this->pdf->create($data);
        $this->mailer->send($report);
        return $report;
    }
}

// Testing with DI
class ReportServiceTest extends TestCase
{
    public function test_generate(): void
    {
        $pdf = Mockery::mock(PdfGeneratorInterface::class);
        $mailer = Mockery::mock(MailerInterface::class);
        $service = new ReportService($pdf, $mailer);
        // Test with mocks
    }
}
Interview Tip

Give a concrete test example: "With DI, I can inject a mock Mailer that doesn't actually send emails during tests."

View full answer →

Secure APIs with HTTPS, authentication (Sanctum/Passport), rate limiting, input validation, CORS configuration, and proper error handling.

API security layers: 1. HTTPS: Always use SSL/TLS in production 2. Authentication: Sanctum tokens or Passport OAuth 3. Rate limiting: Throttle requests per user/IP 4. Input validation: Validate all input with Form Requests 5. CORS: Configure allowed origins properly 6. CSRF protection: Not for API routes (token-based auth) 7. Error handling: Don\'t expose stack traces in production 8. Authorization: Check user permissions (Policies/Gates) 9. Input sanitization: Prevent SQL injection and XSS 10. API versioning: Support multiple API versions Additional measures: - API keys for third-party access - Request signing for webhooks - IP whitelisting for admin endpoints - Audit logging for sensitive operations
Example
<?php
// Rate limiting
Route::middleware(['auth:sanctum', 'throttle:60,1'])->group(function () {
    Route::apiResource('posts', PostController::class);
});

// Form Request validation
public function rules(): array
{
    return [
        'title' => ['required', 'string', 'max:255'],
        'body' => ['required', 'string', 'max:5000'],
    ];
}

// Authorization
$this->authorize('update', $post);

// CORS configuration
// config/cors.php
'allowed_origins' => ['https://frontend.com'],

// Safe error handling
report() => Log::error($e);
render() => response()->json(['error' => 'Server error'], 500);
Interview Tip

Discuss defense in depth: "No single security measure is enough. Use multiple layers: HTTPS + auth + validation + rate limiting + authorization."

View full answer →

Rate limiting restricts the number of requests a client can make in a given time period, protecting against abuse and DDoS attacks.

Rate limiting controls request frequency: Laravel provides: 1. RateLimiter facade for defining limits 2. throttle middleware for applying limits 3. Per-user and per-IP limiting 4. Custom rate limiters 5. Response headers (X-RateLimit-Remaining, Retry-After) Types: - Per-minute: 60 requests per minute per user - Per-second: Burst limiting for APIs - Per-endpoint: Different limits for different routes - Sliding window: More accurate than fixed windows Benefits: Prevents abuse, ensures fair usage, protects server resources, enables tiered API access.
Example
<?php
// In AppServiceProvider boot()
use Illuminate\Routing\RateLimiter;
use Illuminate\Http\Requests\Request;
use Illuminate\Support\Facades\Cache;

RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(60)->by(
        $request->user()?->id ?: $request->ip()
    );
});

RateLimiter::for('login', function (Request $request) {
    return Limit::perMinute(5)->by($request->input('email'));
});

// Apply to routes
Route::middleware('throttle:api')->group(function () {
    Route::apiResource('posts', PostController::class);
});

Route::post('/login', [AuthController::class, 'login'])
    ->middleware('throttle:login');

// Response headers automatically included:
// X-RateLimit-Remaining: 59
// X-RateLimit-Limit: 60
// Retry-After: 60
Interview Tip

Explain that rate limits should be per-user for authenticated routes and per-IP for public routes. Mention the Retry-After header.

View full answer →

Caching stores computed results to avoid repeated expensive operations. Strategies include read-through, write-through, cache-aside, and write-behind.

Caching strategies: 1. Cache-Aside (Lazy Loading): - Check cache first, if miss, query DB, store in cache - Most common in Laravel (Cache::remember) 2. Write-Through: - Write to cache and DB simultaneously - Ensures consistency but slower writes 3. Read-Through: - Cache automatically loads data on miss - Similar to cache-aside but handled by the cache layer 4. Write-Behind (Write-Back): - Write to cache immediately, DB asynchronously - Fast writes but risk of data loss Laravel Cache facade implements cache-aside: - Cache::get / Cache::put / Cache::remember - Tags for grouping related cache entries - Multiple drivers: file, Redis, Memcached, database
Example
<?php
// Cache-Aside (most common)
$posts = Cache::remember('posts:published', 3600, function () {
    return Post::published()->with('user')->get();
});

// Tag-based caching (Redis/Memcached)
Cache::tags(['posts', 'home-page'])->put('featured', $posts, 3600);
Cache::tags(['posts'])->flush(); // Clear all post caches

// Cache invalidation patterns
// 1. Time-based: TTL expires automatically
Cache::put('key', $value, now()->addHour());

// 2. Event-based: Clear on data change
Post::created(fn () => Cache::forget('posts:all'));

// 3. Manual: Clear specific cache
Cache::forget('posts:all');

// Multi-level caching
function getDashboardData()
{
    return Cache::remember('dashboard', 3600, function () {
        // Expensive computation here
        return $this->computeDashboard();
    });
}
Interview Tip

Discuss cache invalidation: "There are two hard things in computer science: cache invalidation and naming things." Explain when each strategy is appropriate.

View full answer →

Deploy via Git pull, composer install, migrations, config caching, and queue worker restart. Use Supervisor for processes, Nginx for web server.

Laravel deployment steps: 1. Server setup: Ubuntu + Nginx + PHP-FPM + MySQL/Redis 2. Code deployment: Git pull, composer install --no-dev 3. Environment: Configure .env with production values 4. Database: Run migrations with --force 5. Caching: config:cache, route:cache, view:cache 6. Permissions: Set storage and bootstrap/cache permissions 7. Queue workers: Start with Supervisor 8. Scheduler: Cron job running schedule:run 9. SSL: Configure HTTPS with Let's Encrypt 10. Monitoring: Set up logging and error tracking Deployment tools: Laravel Forge, Envoyer, GitHub Actions, Deployer.
Example
# Deployment script
#!/bin/bash
set -e

cd /var/www/laravelhub

git pull origin main
composer install --no-dev --optimize-autoloader

php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache

php artisan storage:link
php artisan queue:restart

chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache

# Supervisor config
[program:laravel-worker]
command=php artisan queue:work --sleep=3 --tries=3
user=www-data
autostart=true
autorestart=true
numprocs=2

# Cron
* * * * * cd /var/www/laravelhub && php artisan schedule:run
Interview Tip

Mention deployment tools (Forge, Envoyer) and CI/CD pipelines. Emphasize --force on migrations and the importance of Supervisor for queue workers.

View full answer →

Supervisor is a process control system that keeps queue workers running 24/7, automatically restarting them if they crash or the server reboots.

Supervisor manages long-running processes on Linux: Purpose: - Keeps queue workers alive (they die after max-time or on crash) - Auto-restarts failed processes - Manages multiple worker processes - Logs output for debugging Configuration: - /etc/supervisor/conf.d/laravel-worker.conf - Defines command, user, numprocs, logging - Uses process_name for managing individual workers Operations: - supervisorctl reread: Read new configurations - supervisorctl update: Apply changes - supervisorctl status: Check worker status - supervisorctl restart: Restart workers
Example
# Install Supervisor
sudo apt-get install supervisor

# Create config
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/laravelhub/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/laravelhub/storage/logs/worker.log
stopwaitsecs=3600

# Start workers
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start "laravel-worker:*"

# Monitor
sudo supervisorctl status
Interview Tip

Explain why Supervisor is needed: "Without it, queue workers die when the terminal closes. Supervisor keeps them alive and restarts on failure."

View full answer →

Repository pattern adds an abstraction layer over Eloquent. Eloquent itself is already a repository-like Active Record implementation.

Eloquent: - Active Record pattern: model represents both data and behavior - Direct database interaction - Built into Laravel, rich feature set - Tightly coupled to the database Repository Pattern: - Separates data access from business logic - Interface-based, swappable implementations - Easier to test (mock the interface) - Adds complexity When to use Repository: - Multiple data sources (API + database) - Complex business logic - Heavy testing requirements - Team preferences for clean architecture When Eloquent alone is fine: - Small to medium applications - Rapid prototyping - Simple CRUD operations - Team is experienced with Eloquent
Example
// Eloquent only (simple, effective)
class PostController extends Controller
{
    public function index()
    {
        return Post::with('user')->latest()->paginate();
    }
}

// Repository pattern (more structure)
class PostController extends Controller
{
    public function __construct(
        private PostRepositoryInterface $posts
    ) {}
    
    public function index()
    {
        return $this->posts->paginated();
    }
}

// Repository implementation
class EloquentPostRepository implements PostRepositoryInterface
{
    public function paginated(int $perPage = 15): LengthAwarePaginator
    {
        return Post::with(['user', 'tags'])->latest()->paginate($perPage);
    }
}
Interview Tip

Be balanced: "Eloquent is sufficient for most applications. The repository pattern adds value when you need abstraction for testing or multiple data sources."

View full answer →

Service classes encapsulate business logic that doesn't belong in controllers or models, keeping controllers thin and models focused on data.

Service classes handle business logic that spans multiple models or involves external services: When to create a service: - Business logic spans multiple models - Controller is getting too long - Logic needs to be reused - Integration with external APIs - Complex operations (payments, notifications) Service class benefits: - Thin controllers (HTTP concerns only) - Fat models avoided (business logic extracted) - Testable in isolation - Reusable across controllers Naming convention: {Entity}Service (PostService, PaymentService, NotificationService)
Example
<?php
// Service class
class PostService
{
    public function __construct(
        private PostRepositoryInterface $posts,
        private Mailer $mailer,
        private Cache $cache
    ) {}
    
    public function create(array $data): Post
    {
        $post = $this->posts->create($data);
        
        // Business logic
        if ($post->user->isVerified()) {
            $this->mailer->to($post->user)->send(new PostPublishedMail($post));
        }
        
        $this->cache->forget('posts:all');
        event(new PostCreated($post));
        
        return $post;
    }
    
    public function publish(Post $post): Post
    {
        $post->update(['published_at' => now()]);
        $this->cache->forget('posts:all');
        event(new PostPublished($post));
        return $post;
    }
}

// Thin controller
class PostController extends Controller
{
    public function store(StorePostRequest $request, PostService $service)
    {
        $post = $service->create($request->validated());
        return redirect()->route('posts.show', $post);
    }
}
Interview Tip

Give an example of when a service is needed: "Creating an order involves creating the order, deducting inventory, sending emails, and processing payment — that's a service, not a controller or model."

View full answer →

Blade directives are shorthand syntax for PHP control structures in templates, starting with @ symbol: @if, @foreach, @extends, @yield, @csrf.

Blade provides directives that compile to clean PHP: Conditional directives: - @if / @elseif / @else / @endif - @unless (opposite of @if) - @isset / @empty Loop directives: - @foreach / @endforeach - @for / @endfor - @while / @endwhile - @forelse (empty collection handling) Layout directives: - @extends (inherit layout) - @section / @endsection (define content) - @yield (output section) - @include (embed view) Other: - @csrf (CSRF token) - @method (HTTP method spoofing) - @json (output JSON) - @vite (Vite asset loading) - @error (validation errors) - @can / @endcan (authorization)
Example
@extends('layouts.app')

@section('content')
    @if($posts->isNotEmpty())
        @foreach($posts as $post)
            <article>
                <h2>{{ $post->title }}</h2>
                @if($post->is_featured)
                    <span class="badge">Featured</span>
                @endif
                @unless(auth()->guest())
                    @can('update', $post)
                        <a href="{{ route('posts.edit', $post) }}">Edit</a>
                    @endcan
                @endunless
            </article>
        @endforeach
    @else
        <p>No posts found.</p>
    @endif
    
    {{ $posts->links() }}
@endsection

@error('title')
    <span class="error">{{ $message }}</span>
@enderror
Interview Tip

Mention that Blade compiles to plain PHP for performance. Show awareness of @once, @production, @env directives for environment-specific content.

View full answer →

Authentication verifies WHO you are (login). Authorization determines WHAT you can do (permissions).

Authentication: - Verifies identity: Are you who you say you are? - Happens at login (username/password, token, OAuth) - Managed by guards and providers - Uses auth()->attempt(), auth()->check() - Returns: authenticated user or guest Authorization: - Determines access: Can this user perform this action? - Happens after authentication - Managed by Gates and Policies - Uses $this->authorize(), @can, can() - Returns: allowed or denied (403) In Laravel: - Authentication: auth middleware, Breeze, Sanctum - Authorization: Gates, Policies, Spatie Permission
Example
// Authentication - verifying identity
if (auth()->attempt($credentials)) {
    // User is authenticated
}

Route::middleware('auth')->group(function () {
    // Only authenticated users
});

// Authorization - checking permissions
public function update(Post $post)
{
    $this->authorize('update', $post); // Throws 403 if denied
    // User is authorized
}

// Gate definition
Gate::define('edit-settings', function (User $user) {
    return $user->isSuperAdmin();
});

// Blade authorization
@can('update', $post)
    <button>Edit</button>
@endcan

// Check in code
if ($user->can('delete', $post)) {
    // User can delete
}
Interview Tip

This is a very common interview question. Keep it simple: "Authentication is login, authorization is permissions. Laravel handles both differently."

View full answer →

Factories generate test data with faker values. Seeders populate the database with specific known data for development.

Factories and Seeders: Factories: - Define a blueprint for generating model instances - Use fake() for realistic test data - Define states for variations (published, admin) - Used in tests and seeders - php artisan make:factory PostFactory Seeders: - Populate database with specific data - Used for roles, categories, demo accounts - Run with php artisan db:seed - Called in DatabaseSeeder Best practices: - Use factories for bulk test data - Use seeders for reference/lookup data - Seeders should be idempotent (safe to run multiple times) - Use create() for database, make() for memory
Example
// Factory
public function definition(): array
{
    return [
        'title' => fake()->sentence(),
        'slug' => fake()->unique()->slug(),
        'body' => fake()->paragraphs(3, true),
        'status' => fake()->randomElement(['draft', 'published']),
    ];
}

public function published(): static
{
    return $this->state(fn () => [
        'status' => 'published',
        'published_at' => fake()->dateTimeBetween('-1 year'),
    ]);
}

// Usage
Post::factory()->count(20)->published()->create();

// Seeder
public function run(): void
{
    Role::create(['name' => 'Admin', 'slug' => 'admin']);
    Role::create(['name' => 'Editor', 'slug' => 'editor']);
}

// Run
php artisan migrate:fresh --seed
Interview Tip

Explain that factories are for test data (random, bulk) while seeders are for setup data (specific, known). Mention factory states for data variations.

View full answer →