Laravel 13 Expert
Complete reference for building production-grade Laravel 13.x applications. Laravel 13 was released March 17, 2026 and requires PHP 8.3+.
When to read reference files
This skill is organized with a main SKILL.md (this file) covering core concepts, and reference files for deep-dive topics. Read the appropriate reference file when the task involves:
- AI SDK, agents, tools, embeddings, images, audio, MCP → Read
references/ai-features.md - Upgrading from Laravel 12 to 13 → Read
references/upgrade-guide.md
What's New in Laravel 13
Laravel 13 focuses on AI-native workflows, stronger defaults, and more expressive APIs. Key additions:
First-Party AI SDK (laravel/ai)
A unified API for text generation, tool-calling agents, embeddings, audio, images, and vector-store integrations. Provider-agnostic (OpenAI, Anthropic, Gemini, xAI, Mistral, Ollama, etc.).
use App\Ai\Agents\SalesCoach;
$response = SalesCoach::make()->prompt('Analyze this sales transcript...');
return (string) $response;
Laravel MCP (laravel/mcp)
Build Model Context Protocol servers so AI clients can interact with your Laravel app. Define servers, tools, resources, and prompts.
use Laravel\Mcp\Facades\Mcp;
Mcp::web('/mcp/weather', WeatherServer::class);
Laravel Boost (laravel/boost)
MCP server that gives AI agents Laravel-specific context, 15+ tools, 17,000+ pieces of vectorized docs, and AI guidelines.
composer require laravel/boost --dev
php artisan boost:install
JSON:API Resources
First-party JSON:API compliant resource serialization with relationship inclusion, sparse fieldsets, and links.
Queue Routing
Central routing rules for jobs by class:
Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'podcasts');
Expanded PHP Attributes
Declarative controller and job configuration:
#[Middleware('auth')]
class CommentController
{
#[Middleware('subscribed')]
#[Authorize('create', [Comment::class, 'post'])]
public function store(Post $post) { /* ... */ }
}
Job attributes: #[Tries(3)], #[Backoff(5)], #[Timeout(120)], #[FailOnTimeout]
Cache TTL Extension
Cache::touch('key', seconds: 3600); // Extend TTL without re-storing
Semantic / Vector Search
Native vector query support with pgvector:
$documents = DB::table('documents')
->whereVectorSimilarTo('embedding', 'Best wineries in Napa Valley')
->limit(10)
->get();
PreventRequestForgery Middleware
Enhanced CSRF protection with origin-aware request verification.
Embeddings via Str helper
use Illuminate\Support\Str;
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings();
Installation & Setup
Requirements
- PHP 8.3 - 8.5
- Composer
- Node/NPM or Bun
Quick Install
# Install PHP + Composer + Laravel installer
/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.4)" # macOS
/bin/bash -c "$(curl -fsSL https://php.new/install/linux/8.4)" # Linux
# Create new app
laravel new example-app
cd example-app
npm install && npm run build
composer run dev
The composer run dev command starts the local dev server, queue worker, and Vite dev server. App accessible at http://localhost:8000.
Default Database
Laravel 13 defaults to SQLite. The installer creates database/database.sqlite and runs migrations automatically. To switch to MySQL:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
Then run php artisan migrate.
Laravel Herd
Native dev environment for macOS/Windows. Includes PHP, Nginx, Composer. Parked directories at ~/Herd auto-served on .test domain.
Configuration
All config in config/ directory. Key patterns:
- Environment-based via
.envfile (never commit to source control) - Access with
env('KEY', 'default')in config files,config('app.name')elsewhere - Cache config in production:
php artisan config:cache
Laravel 13 Cache Changes
Default cache/Redis prefixes now use hyphens instead of underscores:
// Old: laravel_cache_
// New: laravel-cache-
If relying on generated defaults, explicitly set CACHE_PREFIX, REDIS_PREFIX, SESSION_COOKIE in .env.
New: serializable_classes Cache Config
Default is false to prevent deserialization attacks. If storing PHP objects in cache, allow-list them:
'serializable_classes' => [
App\Data\CachedStats::class,
],
Directory Structure
Standard Laravel 13 structure:
app/
├── Ai/ # NEW in 13 - AI agents and tools
│ ├── Agents/
│ └── Tools/
├── Console/Commands/
├── Events/
├── Http/
│ ├── Controllers/
│ ├── Middleware/
│ └── Requests/
├── Jobs/
├── Listeners/
├── Mail/
├── Mcp/ # NEW in 13 - MCP servers
│ └── Servers/
├── Models/
├── Notifications/
├── Policies/
├── Providers/
├── Repositories/
├── Services/
└── View/Components/
routes/
├── ai.php # NEW in 13 - MCP server routes
├── web.php
├── console.php
└── channels.php
config/
├── ai.php # NEW in 13 - AI provider config
└── ...
Routing
// Basic routes
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
// Resource routes
Route::resource('posts', PostController::class);
Route::apiResource('posts', PostController::class);
// Route groups
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});
// Route model binding
Route::get('/posts/{post}', fn (Post $post) => view('posts.show', compact('post')));
Laravel 13: Domain Route Registration Precedence
Domain routes now have stricter precedence. Register domain-specific routes before catch-all routes.
Controllers
With PHP Attributes (Laravel 13)
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;
#[Middleware('auth')]
class PostController
{
public function index()
{
$posts = Post::latest()->paginate(15);
return view('posts.index', compact('posts'));
}
#[Middleware('subscribed')]
#[Authorize('create', Post::class)]
public function store(StorePostRequest $request)
{
$post = $request->user()->posts()->create($request->validated());
return redirect()->route('posts.show', $post);
}
#[Authorize('update', 'post')]
public function update(UpdatePostRequest $request, Post $post)
{
$post->update($request->validated());
return redirect()->route('posts.show', $post);
}
}
Form Requests
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'content' => ['required', 'string'],
'category_id' => ['required', 'exists:categories,id'],
'tags' => ['nullable', 'array'],
'tags.*' => ['exists:tags,id'],
];
}
}
Eloquent ORM
Model Basics
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = ['title', 'slug', 'content', 'category_id', 'published_at'];
protected function casts(): array
{
return [
'published_at' => 'datetime',
'metadata' => 'array',
];
}
// Relationships
public function author()
{
return