Introduction to Laravel
Laravel is an open-source PHP web framework built around the MVC (Model-View-Controller) architectural pattern. Created by Taylor Otwell in 2011, it has grown into the most popular PHP framework in the world, with over 80,000 GitHub stars and millions of applications running in production across every industry.
Laravel's appeal comes from its philosophy: make common tasks — routing, authentication, sessions, caching, queues, testing — as painless as possible, without sacrificing power or flexibility. The framework ships with an expressive query builder, a world-class ORM (Eloquent), a template engine (Blade), first-party authentication scaffolding, real-time WebSocket support via Reverb, and now in version 13, a native AI SDK.
This guide is written for beginner to intermediate developers who want a practical, no-fluff understanding of Laravel 13 — what it is, what's new, how to install it, and how to use its key features. If you already know Laravel 12, the "What's New" section is the one to jump to first.
Laravel 13 was released on March 17, 2026. It requires PHP 8.3 or newer and follows the framework's annual Q1 release schedule. Bug fixes are supported until Q3 2027; security fixes until Q1 2028.
What's New in Laravel 13
Laravel 13 is officially described as a "relatively minor upgrade in terms of breaking changes, while still delivering substantial new capabilities." In practice, this means most applications upgrade in under 10 minutes — but there are genuinely significant additions that change how you write Laravel code going forward.
PHP Attributes for Model Configuration
This is the biggest quality-of-life improvement in three versions. Previously, every Eloquent model started
with a wall of protected arrays — $fillable, $hidden, $guarded,
$table, $connection. Laravel 13 replaces all of this with native PHP 8 Attributes.
The change is entirely non-breaking — the old syntax still works.
use Illuminate\Database\Eloquent\Attributes\Table; use Illuminate\Database\Eloquent\Attributes\Fillable; use Illuminate\Database\Eloquent\Attributes\Hidden; // Before (Laravel 12 and earlier) class User extends Model { protected $table = 'users'; protected $fillable = ['name', 'email']; protected $hidden = ['password']; } // After (Laravel 13 — clean attributes) #[Table('users')] #[Fillable('name', 'email')] #[Hidden('password')] class User extends Model { // Your actual business logic lives here now public function posts(): HasMany { ... } }
Attributes are also available for Commands, Form Requests, Mailables, Listeners, Notifications, API Resources, and Factories. Every class that previously used property-based configuration now has an attribute alternative.
Typed Eloquent Properties
For years, model properties were magic. $user->email could be a string, could be null — your IDE
just returned mixed. Laravel 13 adds support for fully typed Eloquent properties, which
immediately improves IDE autocomplete and lets PHPStan catch type errors at development time rather than in
production.
use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; class User extends Model { public int $id; public string $name; public string $email; public ?Carbon $email_verified_at; public bool $is_active; public float $account_balance; }
First-Party Laravel AI SDK
The biggest platform addition in Laravel 13 is the first-party AI SDK. It provides a unified, provider-agnostic API for text generation, tool-calling agents, embeddings, image generation, audio synthesis, and vector-store integrations — all with a clean Laravel-native developer experience.
use App\Ai\Agents\SalesCoach; use Laravel\Ai\Audio; use Illuminate\Support\Facades\DB; // Text generation via a custom agent $response = SalesCoach::make() ->prompt('Analyze this sales transcript...'); // Audio synthesis from text $audio = Audio::of('I love coding with Laravel.')->generate(); // Semantic vector similarity search (PostgreSQL + pgvector) $docs = DB::table('documents') ->whereVectorSimilarTo('embedding', 'Best wineries in Napa Valley') ->limit(10) ->get();
First-Party JSON:API Resources
Laravel 13 ships native JSON:API resources. If you build APIs for mobile apps or third-party
clients, this eliminates the need to assemble JSON:API-shaped responses by hand. The new
JsonApiResource handles resource objects, relationships, sparse fieldsets, includes, links, and
the correct application/vnd.api+json response header automatically.
Enhanced CSRF — PreventRequestForgery
Laravel's CSRF middleware has been formalized as PreventRequestForgery. In modern browsers, it
first checks the Sec-Fetch-Site header to verify same-origin requests, then falls back to normal
CSRF token validation. This is a genuine security improvement, not just a rename.
Cache::touch() — Extend TTL Without Fetch
A small but highly impactful addition: Cache::touch($key, $seconds) extends a cached item's
time-to-live without fetching or re-storing its value. This eliminates the old get-then-put pattern in
high-traffic systems.
// Before — get value, then re-store it just to extend TTL $value = Cache::get('session:123'); Cache::put('session:123', $value, now()->addMinutes(30)); // After (Laravel 13) — one line, no fetch required Cache::touch('session:123', now()->addMinutes(30));
Queue Routing by Job Class
Laravel 13 adds Queue::route(), which lets you define default queue and connection routing rules
for specific job classes centrally — instead of scattering onQueue() calls throughout your
codebase.
use App\Jobs\ProcessPodcast; use Illuminate\Support\Facades\Queue; Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'media'); Queue::route(SendWelcomeEmail::class, queue: 'emails');
Dropping PHP 8.2 support is the main breaking change. Additionally review:
PreventRequestForgery if you have custom CSRF logic, cache serializable_classes (Laravel now
hardens cache unserialization by default), and MySQL DELETE queries with JOIN + ORDER BY which may now throw
errors on unsupported engines.
Installation and Setup
Before installing Laravel 13, make sure your system meets the requirements. PHP 8.3 is the minimum — there is no way around this for Laravel 13.
System Requirements
- PHP 8.3, 8.4, or 8.5
- Composer 2.x
- A database: MySQL 8+, PostgreSQL 13+, SQLite 3.26+, or SQL Server 2017+
- Node.js 18+ and npm (for frontend assets with Vite)
Install via Laravel Installer
The Laravel installer is the fastest way to scaffold a new project.
# Install the Laravel installer globally composer global require laravel/installer # Create a new Laravel 13 project laravel new my-project # Or use Composer directly composer create-project laravel/laravel my-project "^13.0"
Configure Your Environment
Copy .env.example to .env and fill in your database credentials.
APP_NAME=MyProject
APP_ENV=local
APP_KEY= # generated by php artisan key:generate
APP_DEBUG=true
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_project
DB_USERNAME=root
DB_PASSWORD=
Run Migrations and Start the Server
Laravel ships with starter migrations for users and sessions. Run them, then start the dev server.
php artisan key:generate php artisan migrate # Start built-in dev server php artisan serve # Or use Laravel Herd (macOS/Windows) for zero-config local dev # https://herd.laravel.com
Project Structure
A fresh Laravel 13 project follows a well-organised folder layout. Understanding this is fundamental before writing any code. Laravel uses the MVC pattern — Models handle data, Views handle presentation, Controllers handle the logic between them.
my-project/ ├── app/ │ ├── Http/ │ │ ├── Controllers/ # Handle requests, return responses │ │ └── Middleware/ # Filter HTTP requests │ ├── Models/ # Eloquent models (database layer) │ ├── Providers/ # Service container bindings │ └── Ai/ # NEW in L13: AI agents & tools ├── routes/ │ ├── web.php # Browser routes (session, CSRF) │ └── api.php # API routes (stateless) ├── resources/ │ ├── views/ # Blade templates (.blade.php) │ └── js/ # Frontend JS (Vite bundled) ├── database/ │ ├── migrations/ # Schema version control │ ├── factories/ # Test data generators │ └── seeders/ # Database seeders ├── config/ # All framework configuration ├── storage/ # Logs, cache, uploaded files └── tests/ # Unit and feature tests
Routing
In Laravel, all routes are defined in the routes/ directory. Web routes go in
web.php, API routes in api.php. Routes map a URL + HTTP method to a controller
action or a closure.
use App\Http\Controllers\JobController; use Illuminate\Support\Facades\Route; // Basic route — closure Route::get('/', fn() => view('welcome')); // Route with parameter Route::get('/jobs/{id}', [JobController::class, 'show']); // Named route — reference by name anywhere Route::get('/jobs', [JobController::class, 'index'])->name('jobs.index'); // Route group with middleware Route::middleware('auth')->group(function () { Route::get('/dashboard', [DashboardController::class, 'index']); Route::resource('jobs', JobController::class); // full CRUD });
Blade Templating Engine
Blade is Laravel's built-in templating engine. Unlike some template systems, Blade does not restrict you from using plain PHP in your views — every Blade template is compiled to plain PHP and cached, so there is no performance penalty.
<!DOCTYPE html>
<html>
<head>
<title>{{ $title ?? 'My App' }}</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
@include('partials.navbar')
<!-- Child views render here -->
@yield('content')
@stack('scripts')
</body>
</html>
<!-- resources/views/jobs/index.blade.php -->
@extends('layouts.app')
@section('content')
@foreach($jobs as $job)
<div>{{ $job->title }} — {{ $job->company }}</div>
@endforeach
@endsection
Database and Eloquent ORM
Eloquent is Laravel's ORM (Object-Relational Mapper). Each database table has a corresponding Model class, and you interact with the table through that model rather than writing raw SQL. Migrations act as version control for your database schema.
return new class extends Migration { public function up(): void { Schema::create('jobs', function (Blueprint $table) { $table->id(); $table->string('title'); $table->string('company'); $table->text('description'); $table->decimal('salary', 10, 2)->nullable(); $table->foreignId('user_id')->constrained(); $table->timestamps(); }); } };
#[Fillable('title', 'company', 'description', 'salary')] class Job extends Model { public int $id; public string $title; public string $company; // Relationships public function user(): BelongsTo { return $this->belongsTo(User::class); } // Scope — reusable query filter public function scopePublished($query) { return $query->where('is_published', true); } } // CRUD operations Job::create(['title' => 'Laravel Dev', 'company' => 'Acme']); $jobs = Job::published()->with('user')->latest()->paginate(20); Job::find(1)->update(['title' => 'Senior Laravel Dev']); Job::destroy(1);
Authentication and Authorization
Laravel 13 ships with multiple starter kits for authentication. Laravel Breeze is the minimal option — it scaffolds login, registration, password reset, and email verification using Blade views or Inertia.js. Jetstream adds two-factor authentication, API token management, and team support.
composer require laravel/breeze --dev
php artisan breeze:install # choose: blade / react / vue / api
npm install && npm run dev
php artisan migrate
For authorization (what a logged-in user is allowed to do), Laravel provides Policies and Gates:
class JobPolicy { public function update(User $user, Job $job): bool { return $user->id === $job->user_id; } } // In a controller $this->authorize('update', $job); // throws 403 if not authorized
API Development
Laravel is an excellent choice for building RESTful APIs. API routes live in routes/api.php, are
stateless by default (no session, no CSRF token), and are prefixed with /api. Use Resource
Controllers for standard CRUD endpoints and API Resources to control exactly what JSON shape is returned.
// routes/api.php Route::middleware('auth:sanctum')->apiResource('jobs', JobController::class); // app/Http/Controllers/JobController.php class JobController extends Controller { public function index(): JsonResponse { return JobResource::collection( Job::with('user')->latest()->paginate(20) ); } public function store(StoreJobRequest $request): JsonResponse { $job = Job::create($request->validated()); return (new JobResource($job))->response()->setStatusCode(201); } } // app/Http/Resources/JobResource.php class JobResource extends JsonResource { public function toArray($request): array { return [ 'id' => $this->id, 'title' => $this->title, 'company' => $this->company, 'author' => $this->whenLoaded('user', fn() => $this->user->name), ]; } }
Security Features
Laravel has security built into its foundation. Most common web vulnerabilities are handled automatically, so you are protected by default without writing any security-specific code.
- CSRF Protection: Every form submitted to a web route must include a CSRF token via
@csrf. In Laravel 13, this is enhanced with origin-aware verification viaPreventRequestForgery. - Input Validation: Use Form Requests to validate all incoming data before it reaches your
controller. The
validated()method returns only the data that passed your rules — nothing else. - Password Hashing: Passwords are hashed with bcrypt by default. Never store plain-text
passwords. Use
Hash::make($password)to hash andHash::check($input, $hash)to verify. - SQL Injection: Eloquent and the Query Builder use PDO parameter binding. As long as you
use the ORM or
where()methods, injection is prevented automatically. - XSS Prevention: Blade's
{{ }}syntax automatically escapes output. Use{!! !!}only when you intentionally render trusted HTML.
Performance Optimization
A default Laravel install is not automatically optimized for production. These are the techniques that have the biggest real-world impact:
Caching
Cache your configuration, routes, views, and events for production. One command does all four:
php artisan config:cache # merge all config files into one php artisan route:cache # compile route definitions php artisan view:cache # pre-compile all Blade templates php artisan event:cache # cache event-listener discovery # Or run all at once php artisan optimize
Eager Loading — Preventing the N+1 Problem
The single most common performance bug in Laravel applications is the N+1 query problem. If you loop over 100
jobs and access $job->user inside the loop without eager loading, you trigger 101 separate SQL
queries. Use with() to fix this:
// BAD — 1 query to get jobs + 1 query per job for user = N+1 $jobs = Job::all(); foreach ($jobs as $job) { echo $job->user->name; // queries the DB on every iteration } // GOOD — 2 queries total: one for jobs, one for all users $jobs = Job::with('user')->all();
Testing
Laravel ships with PHPUnit pre-configured and adds its own expressive testing helpers on top. Every new
Laravel project contains a tests/Unit and tests/Feature directory. Feature tests are
the most valuable in Laravel — they test real HTTP requests against your application's routes, middleware, and
database.
use Illuminate\Foundation\Testing\RefreshDatabase; class JobApiTest extends TestCase { use RefreshDatabase; // fresh DB for each test public function test_unauthenticated_cannot_create_job(): void { $this->postJson('/api/jobs', ['title' => 'Test']) ->assertStatus(401); } public function test_authenticated_user_can_create_job(): void { $user = User::factory()->create(); $this->actingAs($user) ->postJson('/api/jobs', [ 'title' => 'Laravel Developer', 'company' => 'Acme Corp', 'description' => 'Remote position', ]) ->assertStatus(201) ->assertJsonPath('data.title', 'Laravel Developer'); $this->assertDatabaseHas('jobs', ['title' => 'Laravel Developer']); } } # Run tests php artisan test php artisan test --parallel # faster — runs suites in parallel
Deployment
Laravel applications can be deployed on shared hosting, a VPS, or a cloud platform. For serious production workloads, a VPS with Nginx + PHP-FPM or a managed platform like Laravel Forge is the standard choice.
# 1. Set production environment APP_ENV=production APP_DEBUG=false # 2. Install production dependencies only composer install --optimize-autoloader --no-dev # 3. Run migrations php artisan migrate --force # 4. Optimize framework php artisan optimize # 5. Build frontend assets npm ci && npm run build # 6. Set correct file permissions chmod -R 755 storage bootstrap/cache
Laravel 12 vs Laravel 13
| Feature | Laravel 12 | Laravel 13 |
|---|---|---|
| Min PHP Version | PHP 8.2 | PHP 8.3 |
| AI SDK | Not included | First-party, stable |
| PHP Attributes for Models | Not available | Full support |
| Typed Eloquent Properties | Not available | Supported |
| JSON:API Resources | Third-party only | First-party |
| CSRF Middleware | VerifyCsrfToken | PreventRequestForgery |
| Cache::touch() | Not available | Available |
| Queue Routing by Class | Not available | Queue::route() |
| Vector Similarity Search | Not available | Native support |
| Breaking Changes | — | Minimal (~10 min upgrade) |
Conclusion
Laravel 13 does not reinvent the wheel — and that is exactly the right call. The framework has matured to a point where stability and developer experience improvements compound more value than disruptive new paradigms. PHP Attributes clean up model code that every Laravel developer has written for years. Typed Eloquent properties bring IDE and static-analysis support that should have existed a long time ago. The AI SDK positions Laravel as the first PHP framework with a genuine, production-ready path for building AI-powered features.
If you are starting a new project in 2026, Laravel 13 with PHP 8.3 is the clear choice. If you are on Laravel 12, the upgrade path is intentionally smooth — plan an afternoon, run your test suite, and you will be on 13 by end of day.
Use Laravel 13 for all new PHP projects. The AI SDK alone makes it worth adopting for any
team building modern web applications. The PHP Attributes syntax reduces model boilerplate significantly,
typed properties dramatically improve IDE experience, and the framework-wide security improvements in
PreventRequestForgery are meaningful for production applications.
For existing Laravel 12 applications: upgrade is low-risk and the main prerequisite is PHP 8.3. Audit your third-party packages for compatibility, run your test suite in a staging environment, then deploy. Most teams complete this in well under a day.