Introduction and Architecture

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Laravel 13.x (13.34.0) on PHP 8.3 to 8.5, checked on 2026-10-05; where Laravel 12 differs, the difference is noted. It explains where Laravel sits: how PHP handles a web request, the architectural patterns every PHP framework uses, how Laravel is assembled from its own components and from Symfony’s, and how it compares with the other frameworks documented on this site. The language itself is covered in the PHP Reference; this section is about building web applications with it.

How PHP serves the web

A PHP web application does not run as one long-lived program that listens on a port. A web server (Nginx, Apache or Caddy) receives the HTTP request and hands it to PHP, usually through PHP-FPM, a pool of worker processes. A worker runs the application’s entry script from the top, builds the response, sends it back and then forgets everything: objects, database connections and variables are freed at the end of every request. This shared-nothing model makes PHP applications simple to reason about and easy to scale horizontally (any worker on any server can handle any request), at the cost of rebuilding the application state on every request. OPcache removes the cost of compiling the code, and worker-mode servers such as FrankenPHP and Laravel Octane remove the cost of booting the framework.

The details (SAPIs, PHP-FPM pools, Nginx configuration, worker mode and its pitfalls) are on The PHP Request Model and Servers.

MVC and the front controller

Almost every PHP framework, Laravel included, combines two patterns:

  • Front controller: every request enters through one script (public/index.php), which loads the application and decides what to run. The web server sends all requests that are not static files to it. This replaced the older "one PHP file per URL" style (books.php, book.php?id=1) that the older books use.

  • Model-View-Controller (MVC): a router maps the HTTP method and path to a controller; the controller uses models (data and business rules) and returns a view (the HTML, or a JSON representation for an API).

The same structure fits in a few lines of plain PHP, which is what Bookshelf Lite, the framework-free version of this section’s example application, is built from:

<?php
// public/index.php: every request enters here (the front controller)
declare(strict_types=1);

$books = [1 => ['title' => 'Kindred', 'author' => 'Octavia E. Butler']];   // the "model"

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$method = $_SERVER['REQUEST_METHOD'];

// the router maps method + path to a controller (here: a closure)
$routes = [
    'GET /books' => fn(): string => render('list', ['books' => $books]),
    'GET /books/1' => fn(): string => render('show', ['book' => $books[1]]),
];

function render(string $view, array $data): string           // the "view"
{
    return match ($view) {
        'list' => implode("\n", array_map(fn($b) => htmlspecialchars($b['title']), $data['books'])),
        'show' => htmlspecialchars("{$data['book']['title']} by {$data['book']['author']}"),
    };
}

$controller = $routes["$method $path"] ?? null;
if ($controller === null) {
    http_response_code(404);
    echo "Not found\n";
    return;
}
echo $controller(), "\n";
$ php -S localhost:8000 public/index.php
$ curl localhost:8000/books/1
Kindred by Octavia E. Butler
$ curl -o /dev/null -w '%{http_code}\n' localhost:8000/nope
404

In Laravel the same pieces have fixed places. public/index.php is the front controller, routes live in routes/web.php, controllers in app/Http/Controllers, Eloquent models in app/Models and Blade views in resources/views. This is the Bookshelf application that the rest of the section builds:

<?php
// routes/web.php
use App\Http\Controllers\BookController;
use Illuminate\Support\Facades\Route;

Route::get('/books', [BookController::class, 'index'])->name('books.index');
Route::get('/books/{book}', [BookController::class, 'show'])->name('books.show');
<?php
// app/Http/Controllers/BookController.php
namespace App\Http\Controllers;

use App\Models\Book;
use Illuminate\View\View;

class BookController extends Controller
{
    public function index(): View
    {
        $books = Book::with('author')->orderBy('title')->paginate(10);

        return view('books.index', ['books' => $books]);
    }

    public function show(Book $book): View              // route model binding: Book::findOrFail($id)
    {
        return view('books.show', ['book' => $book->load('author', 'reviews')]);
    }
}
{{-- resources/views/books/show.blade.php --}}
<h1>{{ $book->title }}</h1>
<p>by {{ $book->author->name }}, {{ $book->published_year }}</p>
<p>{{ $book->reviews->count() }} review(s)</p>

GET /books/1 now returns <h1>Kindred</h1> with the author and the review count, a missing book returns a 404 page, and {{ }} escapes every value for HTML. What took a hand-written router, htmlspecialchars() calls and an http_response_code(404) in plain PHP is now convention. The request’s full path through the framework is on Request Lifecycle.

Framework or library?

A library is code you call: a PDF generator, an HTTP client, a Markdown parser. A framework calls your code: it owns the request lifecycle, decides when your controller runs and gives you conventions for where everything lives (inversion of control). A framework pays off when an application needs many of the things it provides at once: routing, validation, sessions, authentication, an ORM and migrations, queues, mail, testing helpers. Writing and securing those by hand is the work that the framework-free pages of this section show, and then replace.

A full framework is the wrong tool when:

  • the program is a script, a cron job or a command-line tool with no HTTP (plain PHP and Symfony Console are enough);

  • a single small endpoint or webhook is all there is (a micro-framework such as Slim, or plain PHP, is simpler);

  • the application is mostly content editing, which a CMS such as WordPress or Drupal already does;

  • the team must assemble its own stack from components (Symfony components, Mezzio with PSR-15 middleware).

These are the same criteria the older framework books apply, but the stack has changed completely: Composer, PSR standards and components shared between frameworks make it practical to start small and grow, and Laravel itself can be used for an API only, without Blade.

Active Record and Data Mapper

PHP has two ORM styles, and the choice shapes the whole data layer.

Active Record (Eloquent, also Rails' ORM): a model class is a table row and knows how to load and save itself. It is concise and quick to work with; the price is that domain objects inherit from the framework’s Model class and carry database behaviour.

<?php
// Eloquent (Active Record): the model saves itself
$book = Book::create(['author_id' => $author->id, 'title' => 'Kindred', 'isbn' => '978-0-8070-8305-4']);
$book->title = 'Kindred (25th anniversary edition)';
$book->save();                                            // UPDATE books SET title = ... WHERE id = ...

$found = Book::where('title', 'like', 'Kindred%')->first();

Data Mapper (Doctrine ORM, the default in Symfony): entities are plain objects with mapping metadata in attributes, and a separate entity manager loads and saves them, tracking changes in a unit of work and writing them in one flush(). It keeps the domain model independent of the database, at the cost of more code and concepts.

<?php

declare(strict_types=1);

namespace Lite;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'books')]
class Book                                   // a plain object: no base class, no database code
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    private ?int $id = null;

    public function __construct(
        #[ORM\Column(length: 255)]
        private string $title,
    ) {}

    public function title(): string
    {
        return $this->title;
    }
}

// elsewhere: the entity manager (the mapper) does the persistence
$em->persist(new Book('Kindred'));
$em->flush();                                // one unit of work: the INSERT runs here
$book = $em->getRepository(Book::class)->findOneBy(['title' => 'Kindred']);

The Doctrine example was run with Doctrine ORM 3.7 on PHP 8.5 against SQLite. Laravel applications use Eloquent; the rest of this section does too, from Eloquent Models onwards.

What Laravel is built from

Laravel’s own packages (illuminate/*, all shipped in laravel/framework) provide the container, routing layer, Eloquent, Blade, queues and the rest. Underneath, it reuses established libraries instead of reinventing them. The require section of laravel/framework 13.34 lists, among others:

  • Symfony components (^7.4 or ^8.0): HttpFoundation (the Request and Response classes that Laravel’s extend), HttpKernel, Routing, Console (under Artisan), ErrorHandler, Mailer and Mime (mail), Process, Finder, VarDumper (dump()/dd()) and Uid. Symfony 8 requires PHP 8.4, so a Laravel 13 application on PHP 8.3 installs the 7.4 components.

  • Monolog (logging), Carbon (dates), Flysystem (file storage), Guzzle (the HTTP client), phpdotenv (.env files), league/commonmark (Markdown), ramsey/uuid, dragonmantank/cron-expression (the scheduler) and brick/math.

  • The PSR interfaces for containers (PSR-11), logging (PSR-3), caching (PSR-16) and HTTP messages (PSR-7).

Laravel’s component map. The application (routes, controllers, models, views, config, migrations, tests) sits on four groups of framework components: HTTP and views (routing, middleware, controllers, requests and validation, responses and sessions, Blade, Vite, Livewire or Inertia), data (connections, query builder, Eloquent, casts and API resources, migrations, seeders and factories, collections, pagination, Redis), services (queues, events, broadcasting, mail and notifications, cache and locks, file storage, scheduler, HTTP client, processes and localization) and security (authentication, gates and policies, CSRF protection, encryption, hashing, rate limiting, signed URLs, email verification and password reset). Below them is the foundation (service container, providers, facades, configuration, Artisan, exceptions and logging, testing helpers), built on Symfony components and other libraries such as Monolog, Carbon, Flysystem and Guzzle, running on PHP 8.3 to 8.5 under PHP-FPM, FrankenPHP or Octane. First-party packages such as Sanctum, Horizon and Reverb are installed when needed.

First-party packages (Sanctum, Passport, Fortify, Socialite, Horizon, Reverb, Octane, Scout, Telescope, Pulse, Pennant, Cashier, Pint, Sail, Boost and the AI SDK) are separate Composer packages that an application installs only when it needs them. Each has a page or a section in this guide.

Laravel among PHP frameworks and other stacks

Versions are the latest releases on 2026-10-05, read from each project’s release tags.

Framework What it is Shares with Laravel Choose it when

Laravel 13.34

Full-stack MVC framework; Eloquent (Active Record), Blade, Artisan, queues, broadcasting and a large first-party ecosystem. PHP 8.3 to 8.5.

 — 

You want one framework that covers web pages, APIs, background jobs and real-time features with strong conventions.

Symfony 8.1

Full-stack framework and a set of reusable components; Doctrine (Data Mapper), Twig, attribute-based configuration. Symfony 8 requires PHP 8.4.

Laravel is built on Symfony components; both are full-stack.

You prefer explicit configuration and Data Mapper, long-term-support releases, or components assembled to measure.

Slim 4.15

Micro-framework: routing, PSR-7 requests and PSR-15 middleware, little else.

Routing and middleware ideas.

Small APIs and services where you choose every other library yourself.

Laminas Mezzio 3.28

PSR-15 middleware framework from the Laminas project (the successor of Zend Framework).

PSR-based HTTP layer.

Middleware-pipeline architectures and teams with Laminas or Zend Framework history.

CakePHP 5.4

Convention-over-configuration MVC with an Active Record-style ORM and code generation (bake).

Conventions, ORM with migrations, scaffolding.

Teams already invested in CakePHP; similar philosophy to Laravel, smaller ecosystem.

CodeIgniter 4.7

Lightweight MVC framework with few dependencies and a small footprint.

MVC structure, query builder.

Simple applications on constrained hosting, or upgrading existing CodeIgniter code.

Yii 3 (app template 1.4) and 2.0.55

Yii 3 is a set of PSR-based packages with an application template; Yii 2 is the classic full-stack framework with Gii code generation.

Full-stack MVC, Active Record (Yii 2).

Existing Yii applications, or Yii 3’s package-based design.

Django 6.1

Python full-stack framework; ORM with migrations, templates, forms and a built-in admin.

The closest equivalent in Python: batteries included, ORM and migrations, server-rendered pages.

A Python team, data-heavy applications, or when the built-in admin matters.

Ruby on Rails 8.1

Ruby MVC framework that inspired Laravel: Active Record, migrations, generators, "convention over configuration".

Almost everything: Eloquent, Artisan generators and migrations follow Rails ideas.

A Ruby team.

ASP.NET Core (.NET 10)

C# framework with MVC, Razor Pages, Minimal APIs and Blazor; Entity Framework Core.

MVC, middleware pipeline, dependency injection, an ORM with migrations.

A .NET organization, or compile-time type checking across the stack.

Spring Boot 4.1

Java or Kotlin on the JVM; Spring MVC or WebFlux, Spring Data JPA, auto-configuration.

Dependency injection container, controllers, an ORM (Data Mapper style).

A JVM organization and long-lived enterprise services.

Source: Laravel — Why Laravel?, Laravel — Release Notes and each framework’s official site linked in the table

For readers moving between stacks, the concept mapping matters more than the feature lists. Laravel names on the left; the closest counterparts on the right are approximate, because the frameworks differ in detail:

Laravel Django ASP.NET Core NestJS FastAPI Spring Boot

routes/web.php, Route::get()

URLconf, path()

Endpoint routing, [HttpGet]

@Controller, @Get()

@app.get()

@GetMapping

Controller method

View function or class

Controller action, Minimal API handler

Controller method

Path operation function

@RestController method

Middleware

Middleware

Middleware pipeline

Middleware, guards, interceptors

Middleware, dependencies

Servlet filters, interceptors

Service container and providers

(settings and apps; no container)

Built-in DI container

Modules and providers

Depends()

Application context, beans

Eloquent model and migrations

Model, QuerySet, migrations

EF Core entity, migrations

TypeORM or Prisma entity

SQLAlchemy or SQLModel (external)

JPA entity, Flyway or Liquibase

Blade view

Django template

Razor view

(none: API-first)

Jinja2 (optional)

Thymeleaf

Form request validation

Forms and validators

Model binding, data annotations

ValidationPipe with class-validator

Pydantic models

Bean Validation

Queued jobs

Celery (external)

Background services, Hangfire

BullMQ queues

Background tasks, Celery

@Async, Spring Batch

Artisan

manage.py

dotnet CLI

Nest CLI

fastapi CLI

Spring Boot CLI, Maven or Gradle

Source: the official documentation linked from each section: Django, ASP.NET, NestJS, FastAPI and Spring Boot

A broader comparison of back-end and web frameworks across languages is planned in a separate guide (issue #263 of this documentation project); until then the tables above and the introduction pages of each section are the reference.

WordPress, Drupal and other platforms

Much of the PHP code on the web runs inside platforms rather than frameworks: WordPress (blogs and sites, with themes and plugins), Drupal (structured content and large sites, built on Symfony components), e-commerce platforms such as Adobe Commerce (Magento) and WooCommerce, and Laravel-based CMSs such as Statamic. They have their own APIs, hook systems and extension models, and their own documentation; this section does not cover them. What it does cover (the request model, PDO, sessions, security, Composer and modern PHP) applies to working on any of them.

Where older books and tutorials are outdated

  • Symfony 1.4, CakePHP 1.3 and Zend Framework 1 (the frameworks of the Wiley book, 2011) are all long discontinued. Symfony is at 8.x, CakePHP at 5.x, and Zend Framework became the Laminas project (2019-2020: Mezzio and laminas-mvc). The framework-agnostic ideas (MVC, front controller, ORM, scaffolding) still hold.

  • The framework landscape and "a short Laravel introduction" in Kromann ch. 21 predate Laravel 6 and most of the current ecosystem; the comparison above replaces it.

  • Laravel 5.8 (Stauffer, 2019) differs from Laravel 13 in structure: models are in app/Models (8), the HTTP and console kernels and RouteServiceProvider are gone in favour of bootstrap/app.php (11), and routes use [Controller::class, 'method'] instead of 'Controller@method' strings (8). See What’s Changed and Upgrading.

  • One PHP file per page (books.php, book.php?id=1), as in Nixon and Powers, works but leaves routing, escaping and access control to every file; a front controller centralises them.