Skip to content

What it is

Laravel is the most popular PHP framework, providing routing, an ORM, queues, caching, authentication, validation and testing in one coherent package.

Routes map to controllers or closures. Eloquent handles the database, Blade the templates, and artisan the command line. Convention covers most decisions.

Installation

composer create-project laravel/laravel my-app

Getting started

The smallest useful thing you can do with it, and what each part means.

Routes, validation and Eloquent
<?php
Route::get('/books/{book}', function (Book $book) {
    return $book->load('author');   // route model binding: 404 automatic
});

Route::post('/books', function (Request $request) {
    $validated = $request->validate([
        'title' => ['required', 'string', 'max:200'],
        'year'  => ['required', 'integer', 'between:1400,2100'],
        'isbn'  => ['nullable', 'string', 'unique:books,isbn'],
    ]);

    return Book::create($validated);   // 422 with errors if validation failed
});
Route model binding resolves {book} to a model and returns 404 automatically, so the handler never sees a missing record. Failed validation throws and becomes a 422 without a manual check.
Eloquent relationships and eager loading
<?php
class Book extends Model
{
    protected $fillable = ['title', 'year', 'author_id'];
    protected $casts = ['published_at' => 'datetime'];

    public function author(): BelongsTo { return $this->belongsTo(Author::class); }
    public function reviews(): HasMany  { return $this->hasMany(Review::class); }

    public function scopeRecent(Builder $q): Builder {
        return $q->where('year', '>=', now()->year - 5);
    }
}

// with() avoids N+1 — one query for books, one for authors.
$books = Book::with('author')->recent()->latest('year')->paginate(20);
Omitting with() issues one query per row when the loop touches $book->author. Enabling Model::preventLazyLoading() in development turns that into an exception instead of a silent slowdown.

Advanced usage

Where the library earns its place over a simpler alternative.

Queues, events and transactions
<?php
class ProcessImport implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public int $backoff = 60;

    public function __construct(public int $importId) {}

    public function handle(ImportService $service): void
    {
        DB::transaction(function () use ($service) {
            $service->run($this->importId);
        });
    }
}

ProcessImport::dispatch($import->id)->onQueue('imports');

// Only dispatch after the transaction actually commits.
DB::transaction(function () use ($order) {
    $order->save();
    OrderPlaced::dispatch($order);   // Laravel defers this with afterCommit
});
Passing the id rather than the model keeps the serialised job small and avoids stale data. Dispatching inside a transaction without afterCommit is a classic bug — the worker can pick the job up before the row exists.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

SQLSTATE[42S22]: Column not found
A migration has not run, or a column was renamed. Run php artisan migrate and check the model's $fillable and $casts.
Target class does not exist
A controller namespace or class name is wrong. Run composer dump-autoload after moving files.

Best practices

  • Always eager load relations you will access; enable preventLazyLoading in development to catch N+1 early.
  • Pass ids to queued jobs, not models, so the worker reads current data.
  • Use form request classes for anything beyond trivial validation.
  • Never disable mass-assignment protection; keep $fillable accurate.

Background

Why it exists, and what it was reacting to.

Created by Taylor Otwell in 2011, Laravel combined Symfony's components with an API designed for developer experience. Its documentation and first-party ecosystem are a large part of why it dominates modern PHP.