Getting Started Last updated: Feb. 28, 2026, 10:07 p.m.

Laravel is designed to lower the barrier to entry for web development while providing powerful tools for seasoned professionals. The "Getting Started" phase focuses on the Local Development Environment, emphasizing that a consistent setup is the foundation of a stable project. By utilizing tools like Laravel Sail (Docker-based) or Herd, the framework ensures that your local machine perfectly mirrors your production server, eliminating the "it works on my machine" syndrome.

Beyond installation, this section introduces the Laravel Philosophy: prioritizing developer happiness and expressive code. It sets the stage for the Model-View-Controller (MVC) pattern and the Artisan CLI, which serves as the command center for your application. This phase is less about writing code and more about understanding the ecosystem, directory structure, and the "batteries-included" nature of the framework.

Introduction to Laravel

Laravel is a sophisticated PHP web framework designed for the development of web applications following the Model-View-Controller (MVC) architectural pattern. Since its inception, Laravel has focused on "Developer Happiness," prioritizing a syntax that is both expressive and elegant. It abstracts the complex, repetitive aspects of web development—such as authentication, routing, sessions, and caching—allowing developers to focus on building unique application logic rather than reinventing foundational components.

By leveraging a modular packaging system and a robust dependency manager (Composer), Laravel provides a rich ecosystem of tools that scale from small hobby projects to enterprise-level applications. It stands out in the PHP ecosystem by integrating modern functional programming concepts, a powerful inversion of control (IoC) container, and an intuitive database abstraction layer.

Core Architectural Philosophy

At its heart, Laravel is built upon the concept of Service Providers. These are the central place where the application is "bootstrapped." When a request enters a Laravel application, the framework initializes these providers to bind various components into the Service Container. This architecture ensures that the framework remains highly decoupled and testable. Developers can easily swap out core framework behavior by re-binding interfaces to custom implementations within the container.

Laravel also emphasizes the use of Middleware, which provides a convenient mechanism for filtering HTTP requests entering your application. For example, Laravel includes middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application.

Key Technical Features

Laravel provides a comprehensive suite of built-in features that handle the heavy lifting of modern web development. The following table details the primary components that form the framework's backbone:

Component Description Primary Benefit
Eloquent ORM An advanced ActiveRecord implementation for working with your database. Allows database interaction using PHP syntax instead of raw SQL.
Blade Templating A powerful, zero-overhead templating engine. Compiles into plain PHP code and caches for high performance.
Artisan CLI A built-in command-line interface. Automates repetitive programming tasks and manages migrations.
Migrations Version control for your database schema. Allows teams to modify and share the application's database structure.
Routing A simple and intuitive way to define application endpoints. Supports RESTful controllers and route grouping with ease.

The Service Container

The Laravel Service Container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

<?php

namespace App\Http\Controllers;

import App\Services\TranslatonService;
import App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * The translation service instance.
     */
    protected $translations;

    /**
     * Create a new controller instance.
     *
     * @param  \App\Services\TranslatonService  $translations
     * @return void
     */
    public function __construct(TranslatonService $translations)
    {
        // The Service Container automatically resolves the dependency 
        // and injects it into the constructor.
        ???? $this->translations = $translations;
    }
}

Note

Modern PHP Requirements: Laravel is built to take advantage of the latest PHP features. Ensure your environment meets the minimum version requirements (typically PHP 8.2 or higher for current releases) to utilize features like union types, readonly properties, and attributes.

Server Requirements

While Laravel provides a local development environment via Laravel Sail (a Docker-based solution), the framework has specific system requirements when deploying to a production server.

Requirement Minimum Specification Reason
PHP Version PHP >= 8.2.0 Core framework dependencies and type-safety.
BCMath Extension Enabled Required for high-precision arithmetic.
Ctype Extension Enabled Character type checking.
JSON Extension Enabled Handling API responses and configuration.
OpenSSL Extension Enabled Secure communication and encryption.
PDO Extension Enabled Database abstraction and security.

Warning: Security Configuration

After installing Laravel, you must set your application key using the php artisan key:generate command. Typically, this key is stored in your .env file. If the application key is not set, your user sessions and other encrypted data will not be secure!

Installation (Composer & Laravel Sail)

Installing Laravel involves setting up a modern PHP environment that can manage dependencies and provide a consistent runtime. The framework offers two primary paths for installation: Composer, the industry-standard PHP dependency manager, and Laravel Sail, a light-weight command-line interface for interacting with Laravel's default Docker development environment. While Composer is suitable for developers who prefer managing their own local server stack (such as Homebrew, Valet, or Herd), Sail is the recommended starting point for developers who want a pre-configured environment that mirrors production settings without manual configuration.

Installation via Composer

Before utilizing Composer to create a new project, you must ensure your local machine has PHP and Composer installed. The create-project command instructs Composer to download the Laravel framework skeleton and all necessary vendor dependencies. This method is ideal if you are using a local PHP installation and a dedicated database server like MySQL or PostgreSQL already running on your host machine.

# Install the Laravel installer globally
composer global require laravel/installer

# Create a new application using the installer
laravel new my-app

# OR: Create a new application directly via Composer
composer create-project laravel/laravel my-app

Once the installation is complete, you should navigate to the project directory. If you are not using a specialized local development environment like Laravel Herd, you may start Laravel's local development server using the Artisan CLI. This server will run by default at http://localhost:8000.

cd my-app
php artisan serve

Installation via Laravel Sail (Docker)

Laravel Sail provides a robust Docker-based development environment that requires no prior experience with Docker. It automatically configures a container stack including PHP, MySQL, Redis, and Selenium. This is the preferred method for ensuring that every member of a development team is working within an identical environment, regardless of their operating system (macOS, Linux, or Windows via WSL2).

To initiate a new Sail project, you can use a simple terminal command that pulls the installation script from laravel.build. This script allows you to choose which "services" (databases or tools) you want to include in your stack.

Service Description Purpose
mysql Relational Database Primary data storage.
redis In-memory Data Store Caching and session management.
meilisearch Search Engine Lightning-fast full-text search.
mailpit Email Testing Catches outgoing emails for local preview.
selenium Browser Automation Running end-to-end browser tests.

The following command demonstrates how to create a new project with specific services:

# Create a new project named 'blog' with mysql and redis
curl -s "https://laravel.build/blog?with=mysql,redis" | bash

# Navigate to the directory
cd blog

# Start the Docker containers in the background
./vendor/bin/sail up -d

Initial Configuration & Environment

Every Laravel application contains a .env file in the root directory. This file defines the environment-specific variables that vary between your local machine and your production server. When installing via Composer, you must manually copy the .env.example file and generate an application key. If you use Laravel Sail or the Laravel Installer, these steps are typically handled automatically.

# Copy the example environment file
cp .env.example .env

# Generate the unique application encryption key
php artisan key:generate

Note

After installation, you may need to configure permissions for the storage and bootstrap/cache directories. These directories must be writable by the web server (or the user running the Docker containers), or Laravel will throw a 500 error when attempting to write logs or compiled views.

Configuring the Sail Alias

By default, Sail commands are executed using the ./vendor/bin/sail binary included with your application. To streamline your workflow, it is a best practice to define a shell alias. This allows you to type sail instead of the full path to the binary.

# Add this to your ~/.zshrc or ~/.bashrc file
alias sail='[ -f debug.log ] && bash vendor/bin/sail || bash vendor/bin/sail'

# Now you can use sail directly
sail up
sail artisan migrate

Warning: Docker Desktop on Windows

If you are developing on Windows, you must install and enable Windows Subsystem for Linux 2 (WSL2). For optimal performance, ensure your Laravel project files are stored within the Linux file system (e.g., ~/code/my-app) rather than the Windows file system (/mnt/c/Users/...), as file system performance across the 9P protocol is significantly slower.

Configuration & Environment Variables

Laravel provides a robust and centralized system for managing application configuration. All of the configuration files for the Laravel framework are stored in the config directory. Rather than hard-coding values into your application logic, Laravel encourages the use of Environment Variables, which allow you to change configuration values based on the environment where the application is running (e.g., local development, staging, or production). This separation of configuration from code is a core tenet of the "Twelve-Factor App" methodology, ensuring security and portability across different hosting platforms.

The Environment Configuration

When Laravel is installed, the root directory will contain a .env.example file. When you create a new project via Composer or Sail, this file is automatically copied to .env. This file is not intended to be committed to your version control system (like Git), as it often contains sensitive credentials such as database passwords, API keys, and application secrets.

The .env file uses a simple KEY=VALUE syntax. Laravel utilizes the DotEnv PHP library to load these variables into the $_ENV and $_SERVER PHP superglobals whenever your application receives a request.

APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:uS6X...
APP_DEBUG=true
APP_URL=http://localhost

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=root
DB_PASSWORD=secret

Retrieving Configuration Values

While you can access environment variables directly, it is a best practice to access them through Laravel's configuration files using the config() helper function. This provides a layer of abstraction and allows you to define default values if an environment variable is missing.

In a configuration file (e.g., config/database.php), you use the env() helper to pull data from the .env file. In your application code, you then use the config() helper to retrieve that value using "dot" syntax, which corresponds to the file name and the key within the returned array.

Helper Usage Context Purpose
env('KEY', 'default') Only within config/*.php files. Pulls a raw value from the .env file.
config('file.key') Anywhere in the application (Controllers, Views, etc.). Accesses processed configuration values.

Example of Configuration Mapping:

// Within config/services.php
return [
    'stripe' => [
        'model' => App\Models\User::class,
        'key' => env('STRIPE_KEY'),
        'secret' => env('STRIPE_SECRET'),
    ],
];

// Within a Controller or Service class
$stripeSecret = config('services.stripe.secret');

Configuration Caching

To give your application a significant speed boost in production, you should cache your configuration files. When you run the config:cache Artisan command, Laravel combines all of your configuration files into a single, fast-loading PHP file. This reduces the filesystem overhead of loading multiple files and parsing the .env file on every request.

# Combine all config files into a single cached file
php artisan config:cache

# Clear the configuration cache
php artisan config:clear

Warning: The env() Helper Limitation

Once your configuration is cached using php artisan config:cache, the .env file is no longer loaded. Consequently, the env() helper will return null for all environment variables. You must ensure that you only call the env() function within your configuration files, and use the config() helper throughout the rest of your application.

Determining the Current Environment

The current application environment is determined via the APP_ENV variable in your .env file. You may access this value via the environment method on the App facade. This is particularly useful for conditionally executing code only in specific environments, such as enabling debug tools only on "local" or "staging" setups.

use Illuminate\Support\Facades\App;

// Check if the environment is specifically 'local'
if (App::environment('local')) {
    // The environment is local
}

// Check if the environment is 'local' OR 'staging'
if (App::environment(['local', 'staging'])) {
    // The environment is local OR staging
}

Maintenance Mode

When your application is in maintenance mode, a custom view will be displayed for all requests into your application. This makes it easy to "disable" your application while you are updating it or performing database migrations.

Command Action Key Feature
php artisan down Activates Maintenance Mode. Can specify a "secret" bypass cookie.
php artisan up Deactivates Maintenance Mode. Restores normal application flow immediately.
php artisan down --refresh=15 Sets a Refresh header. Instructs browsers to refresh the page every 15s.

Note

You can provide a "secret" phrase when putting the site down: php artisan down --secret="my-password". You can then visit your-site.com/my-password to receive a bypass cookie, allowing you to view the site normally while it remains "down" for the rest of the world.

Directory Structure

The default Laravel directory structure is designed to provide a standardized starting point for both small and large applications. Laravel follows a convention-over-configuration approach, meaning that while you are free to organize your application however you like, following the intended structure allows you to leverage the framework's automated features, such as service provider discovery and auto-loading, with minimal effort.

The Root Directory

The root of a Laravel project contains several folders and hidden files that manage dependencies, environment variables, and testing suites. Understanding the purpose of these top-level directories is essential for navigating the framework's lifecycle.

Directory Purpose
app/ Contains the core code of your application (Models, Controllers, Providers).
bootstrap/ Contains files that bootstrap the framework and configure autoloading.
config/ Houses all of your application's configuration files.
database/ Contains database migrations, model factories, and seeds.
public/ The document root for your web server; contains the index.php entry point.
resources/ Contains your views (Blade templates) and uncompiled assets (CSS, JS).
routes/ Defines all of the routes for your application.
storage/ Contains logs, compiled Blade templates, file uploads, and cache.
tests/ Contains your automated tests (Unit and Feature).
vendor/ Contains your Composer dependencies (do not manually edit).

The app Directory

The app directory is the "heart" of your application. By default, this directory is namespaced under App and is autoloaded by Composer using the PSR-4 autoloading standard. While many of the subdirectories (like Jobs or Events) do not exist by default, they are generated automatically when you use Artisan make commands.

  • Http/: This folder contains your Controllers, Middleware, and Form Requests. Almost all logic for handling incoming web requests will reside here.
  • Models/: This is the default location for all your Eloquent model classes. These classes allow you to query and store data in your database tables.
  • Providers/: Contains all of the Service Providers for your application. Service providers bootstrap your application by binding services into the service container and registering events.
// Example of a typical Controller location: app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\View\View;

class UserController extends Controller
{
    public function show(string $id): View
    {
        return view('user.profile', [
            'user' => User::findOrFail($id)
        ]);
    }
}

The routes Directory

Laravel separates routes based on their entry point and intended behavior. This ensures that middleware—like CSRF protection or session state—is only applied where necessary.

File Description
web.php Routes for your web interface. These include session state and CSRF protection.
api.php Routes for your API. These are stateless and usually prefixed with /api.
console.php Where you define your closure-based console commands.
channels.php Where you register the event broadcasting channels your app supports.

The storage Directory

The storage directory is divided into three subdirectories. The app folder is used to store any files generated by your application. The framework folder stores framework-generated files and caches. Finally, the logs folder contains your application's log files.

Note

The storage/app/public directory may be used to store user-generated files, such as profile avatars, that should be publicly accessible. You should create a symbolic link at public/storage which points to this directory using the php artisan storage:link command.

The bootstrap Directory

The bootstrap directory contains the app.php file which bootstraps the framework. This directory also houses a cache directory which contains framework-generated files for performance optimization such as the route and config cache files. You should typically not need to modify any files in this directory unless you are performing advanced framework customizations.

Warning: Permissions

As mentioned in the installation guide, the storage and bootstrap/cache directories must be writable by your web server. In a production environment, failing to set the correct chmod or chown permissions will result in a fatal error during the application boot process.

The public Directory

The public directory contains the index.php file, which is the entry point for all requests entering your application and configures autoloading. This directory also houses your assets such as images, JavaScript, and CSS.

# Typical public directory structure
public/
??? css/
??? js/
??? images/
??? .htaccess
??? favicon.ico
??? index.php

Deployment Strategies

Deploying a Laravel application requires more than simply uploading files to a server via FTP. Because Laravel utilizes modern PHP features, a dependency manager (Composer), and a build pipeline for frontend assets (Vite), a robust deployment strategy must account for environment synchronization, database migrations, and performance optimization. A "zero-downtime" deployment is the gold standard, ensuring that users do not experience errors or maintenance screens while new code is being propagated.

The Deployment Lifecycle

A standard Laravel deployment follows a specific sequence of operations. This lifecycle ensures that the application environment is prepared, dependencies are isolated, and the framework is tuned for production speeds.

Phase Action Purpose
Preparation git pull Retrieve the latest stable code from the repository.
Dependency Management composer install --optimize-autoloader --no-dev Install production-ready PHP packages and optimize the class map.
Asset Compilation npm install && npm run build Compile CSS and JavaScript assets using Vite.
Database Synchronization php artisan migrate --force Apply schema changes to the production database.
Optimization php artisan config:cache,
php artisan route:cache
Flatten configuration and routes into single files for speed.

Automated Deployment Tools

While manual deployments are possible, they are prone to human error. Laravel developers typically utilize specialized tools that automate the symlinking of directories to achieve zero-downtime transitions.

  • Laravel Forge: A server management tool that handles the provisioning of PHP, Nginx, and MySQL, while providing a "Push to Deploy" feature triggered by Git webhooks.
  • Laravel Vapor: A serverless deployment platform for Laravel, powered by AWS Lambda. It scales automatically based on demand and removes the need to manage traditional server software.
  • Envoy: A clean, minimal syntax for defining common tasks you run on your remote servers. It uses Blade-style syntax to define SSH tasks.

Example Envoy Task (Envoy.blade.php):

@servers(['web' => 'user@192.168.1.1'])

@task('deploy', ['on' => 'web'])
    cd /var/www/html
    git pull origin main
    composer install --no-interaction --quiet --optimize-autoloader --no-dev
    php artisan migrate --force
    php artisan config:cache
    php artisan route:cache
    php artisan view:cache
@endtask

Zero-Downtime Deployment Structure

To prevent users from seeing a broken site during a file transfer, modern strategies use a "releases" folder structure. A symlink (symbolic link) points the web server's document root to the "current" release. When a new deployment finishes, the symlink is updated to point to the new folder instantaneously.

/var/www/app
??? releases
?   ??? 20260101120000
?   ??? 20260226200000 (Newest)
??? shared
?   ??? .env
?   ??? storage
??? current -> /var/www/app/releases/20260226200000

Optimizing for Production

In a development environment, Laravel is configured to be helpful and descriptive. In production, these same features become performance bottlenecks or security risks.

  1. Disable Debug Mode: Ensure APP_DEBUG is set to false in your production .env file to prevent sensitive stack traces from being exposed to users.
  2. Route & Config Caching: Accessing the filesystem to parse multiple files is slow. Caching these into a single PHP array significantly reduces overhead.
  3. Autoloader Optimization: Composer's default autoloader is designed for development flexibility. Using the --optimize-autoloader flag creates a "class map" for faster file discovery.

Warning: The migrate --force Flag

When running migrations in production via a deployment script, you must use the --force flag. Laravel will natively prompt for confirmation when running destructive database operations in a production environment; this flag bypasses the prompt to allow the automated script to finish.

Queue Workers in Production

If your application uses Laravel Queues to handle background tasks (like sending emails), you must ensure that your queue workers are restarted during deployment. Since queue workers are long-lived processes, they store the application state in memory. If you update your code but do not restart the workers, they will continue to execute the old version of your logic.

# Restart the queue workers to load the new code
php artisan queue:restart

Note

You should use a process monitor like Supervisor on your production server. Supervisor will monitor your php artisan queue:work commands and automatically restart them if they fail or if the server reboots.

Frontend Tooling (Vite, Livewire, Inertia)

Laravel does not dictate a single way to build your frontend; instead, it provides a versatile ecosystem that caters to different architectural preferences. Whether you prefer traditional server-side rendering, modern reactive components, or a hybrid approach, Laravel’s tooling—centered around Vite, Livewire, and Inertia.js—streamlines the transition from backend logic to user interface. These tools are designed to bridge the gap between PHP and JavaScript, ensuring high performance and a seamless developer experience.

Vite: The Modern Asset Bundler

Vite is the default build tool for Laravel applications, replacing the older Webpack-based Laravel Mix. It serves as an extremely fast development server that provides Hot Module Replacement (HMR). During development, Vite does not bundle your assets; instead, it serves your JavaScript modules via native ES modules, making the feedback loop nearly instantaneous. When you are ready to deploy, Vite bundles your assets into highly optimized static files for production.

To use Vite within your Blade templates, you use the @vite directive, which automatically handles the inclusion of the necessary scripts and styles based on whether you are in development or production mode.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
    <div id="app"></div>
</body>
</html>

Choosing Your Frontend Stack

Laravel officially supports two primary "stacks" for building interactive applications. Your choice depends on whether you prefer to stay within the PHP ecosystem or if you want to leverage a full JavaScript framework like Vue or React.

Tool Architectural Style Primary Language Use Case
Livewire Full-stack Framework PHP High interactivity without leaving Laravel/Blade.
Inertia.js Classic Monolith with modern SPA JS (Vue/React) Using JS frameworks without the complexity of a separate API.
Blade Server-side Rendering PHP/HTML Traditional multi-page applications with minimal JS.

Livewire: Reactive PHP

Livewire is a full-stack framework for Laravel that makes building dynamic interfaces as simple as writing standard Laravel classes. It allows you to create components that "live" on the server but feel like a Single Page Application (SPA) to the user. When a user interacts with a Livewire component (e.g., clicks a button), an AJAX request is sent to the server, the PHP component re-renders, and Livewire intelligently updates only the changed portion of the DOM.

// app/Livewire/Counter.php
namespace App\Livewire;

use Livewire\Component;

class Counter extends Component
{
    public $count = 0;

    public function increment()
    {
        $this->count++;
    }

    public function render()
    {
        return view('livewire.counter');
    }
}
<div>
    <h1>{{ $count }}</h1>
    <button wire:click="increment">+</button>
</div>

Inertia.js: The Modern Monolith

Inertia allows you to build single-page apps using Vue, React, or Svelte, but without the complexity of building a separate REST or GraphQL API. It acts as the "glue" between your Laravel backend and your JavaScript frontend. When using Inertia, you define your routes and controllers in Laravel as usual, but instead of returning a Blade view, you return an Inertia component.

// app/Http/Controllers/UserController.php
use Inertia\Inertia;

public function show(User $user)
{
    // Pass data directly to a Vue/React component
    return Inertia::render('User/Profile', [
        'user' => $user
    ]);
}

Note

For rapid scaffolding, Laravel offers Breeze and Jetstream. Breeze provides a minimal, simple implementation of all Laravel's authentication features using either Blade/Livewire or Vue/React via Inertia. Jetstream is a more robust application scaffolding that includes team management, API support, and two-factor authentication.

Warning: Production Asset Building

In your development environment, you typically run npm run dev to start the Vite development server. However, before deploying to production, you must run npm run build. This command generates the versioned manifest and minified assets required for the @vite directive to function correctly on a live server.

# Development (with HMR)
npm run dev

# Production Build
npm run build   

Architecture Concepts Last updated: Feb. 28, 2026, 10:07 p.m.

The true power of Laravel lies in its internal plumbing, specifically the Service Container and Service Providers. This section explains how Laravel manages class dependencies and performs "dependency injection." Understanding the lifecycle of a request—from the moment it hits public/index.php to the final response—is crucial for building scalable apps. It teaches you how the framework "boots" itself and how you can hook into that process to register your own services.

Furthermore, this section covers Facades and Contracts, which provide the balance between ease of use and architectural purity. Facades offer a "static" interface to classes in the container, providing a clean, expressive syntax, while Contracts act as interfaces that allow you to swap out core framework components (like the Cache or Queue drivers) without breaking your code. This architectural foundation is what allows Laravel to remain both flexible and simple.

The Request Lifecycle

Understanding the entry point and exit of a request is fundamental to mastering Laravel. When a user interacts with your application, the request travels through a meticulously orchestrated series of layers. This lifecycle ensures that the environment is prepared, security is enforced, and dependencies are resolved before your specific application logic is ever executed. By understanding this flow, you can pinpoint exactly where to hook into the framework to modify behavior, handle global exceptions, or inject custom functionality.

The Entry Point: public/index.php

All requests to a Laravel application are directed by your web server (Apache or Nginx) to the public/index.php file. This file is the "front controller" for the entire application. It serves a humble but critical purpose: it loads the Composer-generated autoloader definitions and then retrieves an instance of the Laravel application from bootstrap/app.php. This initial step transitions the process from a raw PHP script into the managed environment of the Laravel framework.

HTTP and Console Kernels

Once the application instance is created, the request is handed off to either the HTTP Kernel or the Console Kernel, depending on the nature of the entry. For web requests, the HTTP Kernel (Illuminate\Foundation\Http\Kernel) is responsible for the heavy lifting. The Kernel defines a list of Bootstrappers that are run before the request is executed. These bootstrappers perform high-level tasks such as:

  • Configuring error handling and logging.
  • Detecting the application environment.
  • Loading and parsing configuration files.
  • Initializing the Service Container.

The HTTP Kernel also defines the Middleware stack. All requests must pass through these global middleware layers (such as session handling and CSRF protection) before reaching your routes.

Service Providers: The Heart of Bootstrapping

One of the most important actions the Kernel performs is starting the Service Providers. Service providers are the central place to configure and bootstrap all of Laravel's core components, such as the database, queue, and validation. The lifecycle of a service provider occurs in two distinct phases, detailed in the table below:

Phase Method Description
Registration register() Binds things into the Service Container. No other services should be used here, as they may not be loaded yet.
Bootstrapping boot() Called after all providers are registered. You can safely access any service or dependency here.

Once all providers have been registered and booted, the request is finally handed to the Router for dispatching.

Routing and Controller Execution

The Router matches the incoming HTTP request to a specific route defined in your routes/web.php or routes/api.php files. If the route points to a Controller, the Service Container automatically resolves the controller class and its dependencies, injecting them via the constructor.

// Example of a route dispatching to a controller
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;

// The Router directs the request here
Route::get('/profile/{id}', [UserController::class, 'show']);

// Inside the Controller
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * The Request has passed through the Kernel and Middleware 
     * and is now being handled by the specific logic below.
     */
    public function show(string $id): View
    {
        $user = User::findOrFail($id);

        return view('user.profile', ['user' => $user]);
    }
}

The Final Response

After your controller or route closure returns a value (like a View, a String, or a JSON object), it travels back out through the middleware stack. This "outgoing" trip allows middleware to modify the response—for example, by adding HTTP headers or minifying the HTML output. Finally, the response is sent back to the user's browser, and the HTTP Kernel executes any "terminable" middleware (tasks that happen after the response is sent, like logging or clean-up).

Note

The Singleton Pattern

Many of the core objects in the request lifecycle, such as the Application and Kernel instances, are registered as Singletons. This means the same instance is used throughout the entire lifecycle of a single request, ensuring consistent state across different parts of your code.

Warning: Statelessness in Octane

If you are using Laravel Octane (running on Swoole or RoadRunner), the request lifecycle changes slightly. The application is "booted" once and stays in memory across multiple requests. You must be careful not to store request-specific data in static variables or singletons, as that data will persist into subsequent users' requests.

Service Container (Dependency Injection)

The Laravel Service Container is the most critical component of the framework’s architecture. It is a powerful tool for managing class dependencies and performing Dependency Injection. At its core, the container is a registry that stores "blueprints" for how to create objects. When you request an object from the container, it automatically inspects the class's constructor, identifies its dependencies, and recursively resolves them until the final object is fully instantiated and ready for use.

By centralizing how objects are created, the Service Container promotes a decoupled, testable, and maintainable architecture. It allows you to swap implementations of a specific interface without modifying the high-level code that consumes it.

Understanding Dependency Injection

Dependency Injection (DI) is a design pattern where an object's dependencies are "injected" into it rather than the object creating them itself. This is primarily achieved through Constructor Injection. Instead of using the new keyword inside a class, you type-hint the required dependency in the constructor. Laravel's container uses PHP's Reflection API to automatically provide the correct instance.

Concept Manual Instantiation Dependency Injection (Laravel)
Control Class creates its own dependencies. Dependencies are provided externally.
Coupling High (Hard-coded to a specific class). Low (Code against an Interface).
Testability Difficult; cannot easily mock objects. Easy; can inject mock versions of dependencies.

Example of Constructor Injection:

<?php

namespace App\Http\Controllers;

use App\Services\PaymentGateway;
use Illuminate\Http\Request;

class OrderController extends Controller
{
    /**
     * The payment gateway instance.
     */
    protected $paymentGateway;

    /**
     * Create a new controller instance.
     * The Service Container automatically injects the PaymentGateway.
     */
    public function __construct(PaymentGateway $paymentGateway)
    {
        $this->paymentGateway = $paymentGateway;
    }

    public function store(Request $request)
    {
        // Use the injected service
        $this->paymentGateway->charge($request->amount);
    }
}

Binding into the Container

While Laravel can automatically resolve simple classes that do not require configuration, you often need to tell the container how to build more complex objects. This is done via Bindings within a Service Provider.

There are three primary ways to bind a service into the container:

  1. Simple Bindings: A new instance is created every time the service is resolved.
  2. Singletons: The instance is created once and shared throughout the entire request lifecycle.
  3. Scoped: Similar to singletons, but specific to a lifecycle (useful in Laravel Octane).
// app/Providers/AppServiceProvider.php

public function register(): void
{
    // Simple Binding
    $this->app->bind(TranslatonService::class, function ($app) {
        return new TranslatonService(config('app.locale'));
    });

    // Singleton Binding
    $this->app->singleton(DatabaseConnection::class, function ($app) {
        return new DatabaseConnection(config('db.host'));
    });
}

Binding Interfaces to Implementations

One of the most powerful features of the Service Container is the ability to bind an interface to a concrete implementation. This allows your application to remain agnostic of the specific library or service being used.

Interface Implementation A Implementation B
Filesystem LocalFilesystem S3Filesystem
SmsGateway TwilioService VonageService

Example of Interface Binding:

// In a Service Provider
$this->app->bind(
    \App\Contracts\SmsGateway::class, 
    \App\Services\TwilioSmsGateway::class
);

// In your Controller
// Even if you change the binding to Vonage, this code remains the same.
public function __construct(\App\Contracts\SmsGateway $sms)
{
    $this->sms = $sms;
}

Contextual Binding

Occasionally, you may have two classes that use the same interface, but you wish to inject different implementations into each class. This is handled via Contextual Binding.

use App\Http\Controllers\PhotoController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('local');
          });

$this->app->when(VideoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('s3');
          });

Note

Zero-Configuration Resolution

If a class has no dependencies or only depends on other concrete classes (not interfaces), the container does not need to be told how to resolve that class. You can simply type-hint it, and Laravel will instantiate it for you automatically.

Warning: Avoiding the "Service Locator" Pitfall

While you can manually resolve classes using app()->make() or resolve(), this is generally considered an anti-pattern known as "Service Location." It hides your class's dependencies and makes testing harder. Always prefer Constructor Injection whenever possible.

Service Providers

Service providers are the central assembly point for all Laravel applications. While the Service Container is the "toolbox" that holds your objects, Service Providers are the "instruction manuals" that tell Laravel how to put those tools together. They are responsible for bootstrapping the core framework services as well as your own application’s custom services, such as database connections, queue workers, and event listeners.

Virtually every major component in Laravel—from the Router to the Validation engine—is initialized via a service provider. Without these providers, the framework would be a collection of disconnected classes with no knowledge of how to interact with one another.

The Provider Lifecycle

A service provider contains two primary methods: register and boot. Understanding the distinction between these two is critical for avoiding "class not found" errors or trying to use services before they have been initialized.

Method Timing Purpose
register First Phase Only used to bind things into the Service Container. You should never execute logic or use other services here.
boot Second Phase Called after all other service providers have been registered. You can safely use any service (database, routes, etc.) here.

The register Method

Within the register method, you should only ever bind implementation classes to the service container. Do not attempt to register any event listeners, routes, or any other piece of functionality within this method. If you do, you may accidentally use a service that is provided by another service provider which has not loaded yet.

<?php

namespace App\Providers;

use App\Services\RiotApiService;
use Illuminate\Support\ServiceProvider;

class RiotServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // Binding a singleton into the container
        $this->app->singleton(RiotApiService::class, function ($app) {
            return new RiotApiService(config('services.riot.key'));
        });
    }
}

The boot Method

The boot method is called after all other service providers have been registered. This means you have access to all other services that have been registered by the framework. This is the ideal place to register view composers, listen for events, or define custom validation rules.

<?php

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // Sharing a variable across all Blade views
        View::share('site_name', config('app.name'));

        // Registering a custom validation rule
        \Illuminate\Support\Facades\Validator::extend('uppercase', function ($attribute, $value) {
            return strtoupper($value) === $value;
        });
    }
}

Deferred Providers

If your provider is only registering bindings in the service container, you may choose to defer its registration until one of those bindings is actually needed. Deferring the loading of a provider improves the performance of your application because it is not loaded from the filesystem on every request.

To defer a provider, implement the \Illuminate\Contracts\Support\DeferrableProvider interface and define a provides method. The provides method should return the service container bindings registered by the provider.

<?php

namespace App\Providers;

use App\Services\PaymentGateway;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class PaymentServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(PaymentGateway::class, function ($app) {
            return new PaymentGateway();
        });
    }

    /**
     * Get the services provided by the provider.
     */
    public function provides(): array
    {
        return [PaymentGateway::class];
    }
}

Note

Registering Your Providers

In modern Laravel versions (Laravel 11+), service providers are typically registered in the bootstrap/providers.php configuration file. In older versions, they were listed in the providers array within config/app.php. When you use the php artisan make:provider command, Laravel will often offer to add the provider to this file for you automatically.

Warning: Logic in the register Method

Placing logic that relies on other services (like calling Auth::user() or Route::current()) inside the register method is a common source of "Target class [X] does not exist" errors. Always move such logic to the boot method to ensure the entire container is ready.

Core Service Providers

Every Laravel application comes with several built-in providers that handle essential framework logic:

Provider Description
AppServiceProvider The general-purpose provider for your application logic.
AuthServiceProvider Used to define authorization policies and gates.
EventServiceProvider Registers your application's event-to-listener mappings.
RouteServiceProvider Configures route loading, rate limiting, and route-model binding.

Facades

Facades provide a "static" interface to classes that are available in the application's Service Container. Laravel ships with many facades which provide access to almost all of Laravel's features. Unlike traditional static methods, Laravel facades serve as dynamic proxies to underlying objects in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.

Essentially, a facade is a class that uses the __callStatic() magic method to redirect static calls to an object resolved from the container. This allows you to use Cache::get('key') instead of manually resolving the cache repository from the container and calling the method on the instance.

How Facades Work

In a Laravel application, a facade is a class that extends the base Illuminate\Support\Facades\Facade class. The only requirement for a facade is to define the getFacadeAccessor method. This method’s job is to return the name of a service container binding (a string or an interface).

When you call a static method on a Facade, Laravel resolves the underlying instance from the service container and executes the method against that instance. This "indirection" is what allows facades to be easily mocked during testing.

Component Role
The Facade Class The static "proxy" you interact with (e.g., Route, DB).
getFacadeAccessor() Returns the string key used to look up the object in the container.
The Underlying Class The actual instance (service) performing the work.
Service Container The registry where the underlying class is stored as a singleton.

Facade vs. Dependency Injection

One of the primary advantages of facades is their brevity. They allow you to use Laravel features without needing to inject long lists of dependencies into your controller constructors. However, some developers prefer Dependency Injection because it makes a class’s dependencies explicit.

Feature Facades Dependency Injection
Syntax Terse and "Static-like" (Cache::put(...)). Verbose ($this->cache->put(...)).
Discovery Dependencies are hidden inside methods. Dependencies are visible in the constructor.
Scope Available anywhere in the application. Requires the class to be resolved by the container.
Testing Easy to mock via built-in methods. Easy to mock via standard testing libraries.

Example of Facade Usage:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Show the profile for the given user.
     */
    public function showProfile(string $id)
    {
        // Using the Cache facade to proxy a call to the Cache Manager
        $user = Cache::remember("user.{$id}", 60, function () use ($id) {
            return \App\Models\User::find($id);
        });

        return view('user.profile', ['user' => $user]);
    }
}

Creating Custom Facades

You can create your own facades for your application's services. This involves three steps: creating the service class, binding that class in a Service Provider, and creating the Facade class itself.

// 1. The Underlying Service
namespace App\Services;

class Analytics
{
    public function track(string $event) { /* Logic */ }
}

// 2. The Facade Class
namespace App\Support\Facades;

use Illuminate\Support\Facades\Facade;

class Analytics extends Facade
{
    protected static function getFacadeAccessor() { return 'analytics'; }
}

// 3. The Binding (in AppServiceProvider)
$this->app->bind('analytics', function ($app) {
    return new \App\Services\Analytics();
});

Now you can call Analytics::track('page_view') from anywhere in your application.

Testing with Facades

Because facades are proxies to objects resolved from the container, we can easily swap the real object with a "mock" or a "fake" during a test. Laravel’s built-in facades provide testing helpers that make this process incredibly simple, ensuring you don't actually hit the database or send real emails during a unit test.

use Illuminate\Support\Facades\Cache;

public function test_cache_is_called()
{
    // Instruct the facade to behave as a mock
    Cache::shouldReceive('get')
         ->once()
         ->with('key')
         ->andReturn('value');

    // This call will hit the mock, not the real cache driver
    $value = Cache::get('key');

    $this->assertEquals('value', $value);
}

Note

Real-Time Facades

Laravel allows you to treat any class in your app directory as a facade on the fly. By prefixing the namespace with Facades\, Laravel will automatically generate a facade for that class. For example: use Facades\App\Services\PaymentGateway;.

Warning: Facade "Scope Creep"

It is easy to let your controllers become bloated when using Facades, as there is no "constructor pain" to alert you that a class has too many responsibilities. If you find yourself using 10+ different facades in a single controller, consider refactoring that logic into smaller, dedicated service classes.

Starter Kits (Breeze & Jetstream)

Laravel provides two primary "starter kits" to jumpstart the development of new applications: Laravel Breeze and Laravel Jetstream. These kits are not merely templates; they are fully functional application scaffolds that include pre-configured authentication, registration, password reset, email verification, and password confirmation. By using a starter kit, developers can skip the repetitive task of building secure authentication systems and move directly to implementing their application’s unique core logic.

Laravel Breeze: The Minimalist Choice

Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features. It is built using Blade templates and Tailwind CSS, providing a "classic" development experience that is easy to customize. For developers who prefer a modern JavaScript frontend, Breeze also offers "stacks" for Vue and React via Inertia.js.

Breeze is designed to be the "starting point" for most projects. It publishes all of its controllers, routes, and views directly into your application's directory structure, allowing you to modify every line of code as if you had written it yourself from scratch.

Feature Description Implementation
Styling Utility-first CSS. Tailwind CSS
Stack Options Blade, Livewire, Vue, or React. Multi-stack support
Authentication Login, Registration, Password Reset. Standard Controllers
Complexity Low. Ideal for beginners or simple apps.

Installing Breeze:

# Install the package via Composer
composer require laravel/breeze --dev

# Run the installation command and choose your stack
php artisan breeze:install

# Migrate the database and build assets
php artisan migrate
npm install && npm run dev

Laravel Jetstream: The Advanced Ecosystem

Laravel Jetstream is a robust application starter kit that provides a more sophisticated starting point. It includes everything found in Breeze but adds advanced features required for enterprise-level SaaS (Software as a Service) applications, such as team management, API support via Laravel Sanctum, and two-factor authentication.

Jetstream offers two choices for its frontend implementation: Livewire or Inertia.js. Unlike Breeze, Jetstream utilizes "Actions"—simple classes that perform a single task—to handle application logic. This keeps your controllers thin and ensures that logic like "Create Team" can be reused across your web UI and your API.

Feature Description Implementation
Teams Create and manage user groups. Built-in Team Model & Logic
Security 2FA and Browser Session management. Integrated UI/Logic
API Support Issue API tokens to users. Laravel Sanctum
Profile Integrated profile photos and info. Pre-built UI components

Installing Jetstream:

# Install Jetstream via Composer
composer require laravel/jetstream

# Install Jetstream with the Livewire stack and Teams support
php artisan jetstream:install livewire --teams

# Finalize the installation
php artisan migrate
npm install && npm run build

Choosing the Right Kit

The decision between Breeze and Jetstream depends on the complexity of your project and your comfort level with advanced Laravel concepts.

Criteria Choose Breeze If... Choose Jetstream If...
Project Size Building a personal blog or simple MVP. Building a SaaS or complex portal.
Control You want simple, readable controllers. You prefer a structured "Action" pattern.
Features You only need basic login/register. You need Teams, 2FA, or API tokens.
Experience You are new to Laravel. You are an experienced Laravel developer.

Note

The Role of Laravel Fortify

Under the hood, both Breeze and Jetstream (specifically the Jetstream backend) rely on Laravel Fortify. Fortify is a frontend-agnostic authentication backend for Laravel. While Breeze provides its own controllers, Jetstream uses Fortify to handle the "invisible" logic of logging in and registering, allowing you to focus purely on the UI.

Warning: Modifying Starter Kit Code

Once a starter kit is installed, the code is yours. There is no "update" command to pull in new features from Breeze or Jetstream into an existing project. Because these kits publish code directly into your app/ and resources/ directories, any future framework updates must be applied manually to your customized versions of these files.

The Basics Last updated: Feb. 28, 2026, 10:08 p.m.

This section defines the core cycle of every web application: Routing, Controllers, and Responses. Laravel’s routing system is highly expressive, allowing you to define clean, SEO-friendly URLs that map directly to your logic. It moves into the concept of Middleware, which acts as a protective layer around your routes, handling tasks like authentication, CSRF protection, and logging before a request even reaches your controller.

The basics also encompass the Blade Templating Engine, which provides a powerful way to build dynamic UIs without the overhead of a heavy JavaScript framework. Blade’s template inheritance and "components" allow developers to build reusable UI elements while keeping the syntax nearly identical to plain HTML. This section bridges the gap between raw data and the end-user experience.

Routing

In Laravel, routing is the process of accepting an incoming HTTP request and redirecting it to the appropriate piece of logic, typically a controller or an anonymous function (closure). All Laravel routes are defined in your route files, which are located in the routes directory. These files are automatically loaded by the framework's App\Providers\RouteServiceProvider. This centralized approach ensures that your application's entire URL structure is visible, manageable, and highly decoupled from the underlying business logic.

Basic Routing

The most basic Laravel routes accept a URI and a closure, providing a very simple and expressive method of defining routes and behavior without complicated configuration files.

use Illuminate\Support\Facades\Route;

Route::get('/greeting', function () {
    return 'Hello World';
});

Laravel supports all standard HTTP verbs, allowing you to build RESTful interfaces efficiently. The following table illustrates the primary routing methods available:

Method HTTP Verb Purpose
Route::get($uri, $callback) GET Retrieve a resource or display a page.
Route::post($uri, $callback) POST Create a new resource or submit a form.
Route::put($uri, $callback) PUT Replace an existing resource entirely.
Route::patch($uri, $callback) PATCH Partially update an existing resource.
Route::delete($uri, $callback) DELETE Remove a resource from the system.
Route::options($uri, $callback) OPTIONS Inspect communication options for a resource.

Route Parameters

Often you will need to capture segments of the URI within your route. For example, you may need to capture a user's ID from the URL. You may do so by defining route parameters, which are always encased within curly braces {} and should consist of alphabetic characters.

Required Parameters

Route::get('/user/{id}', function (string $id) {
    return 'User '.$id;
});

Optional Parameters

Occasionally you may need to specify a route parameter, but make the presence of that parameter optional. You may do so by placing a ? mark after the parameter name. Ensure you give the corresponding variable a default value.

Route::get('/user/{name?}', function (?string $name = 'John') {
    return $name;
});

Named Routes

Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the name method onto the route definition. This is a best practice because it allows you to change the URI of a route in one place without having to update every link throughout your application.

// Defining the named route
Route::get('/user/profile', function () {
    // ...
})->name('profile');

// Generating a URL to the route
$url = route('profile');

// Generating a Redirect
return redirect()->route('profile');

Route Groups and Middleware

Route groups allow you to share route attributes, such as middleware, across a large number of routes without needing to define those attributes on every individual route.

  • Middleware: Assign middleware to all routes within a group.
  • Controllers: Assign a common controller to a group of routes.
  • Prefixes: Prefix every URI in the group with a specific string (e.g., /admin).
  • Name Prefixes: Prefix every route name in the group.
Route::middleware(['auth'])->group(function () {
    Route::get('/dashboard', function () {
        // Only authenticated users may access this...
    });

    Route::get('/account', function () {
        // ...
    });
});

Route::prefix('admin')->group(function () {
    Route::get('/users', function () {
        // Matches the "/admin/users" URL
    });
});

Note

CSRF Protection

Any HTML forms pointing to POST, PUT, PATCH, or DELETE routes that are defined in the web routes file should include a CSRF token field. Otherwise, the request will be rejected. You can use the @csrf Blade directive to generate the token field:

<form method="POST" action="/profile">
    @csrf
    ...
</form>

Warning: Route Caching

For a significant performance boost in production, you should utilize Laravel's route cache. Use the php artisan route:cache command. However, route caching does not work with Closure-based routes. To cache your routes, you must convert all Closure routes to Controller classes.

Route-Model Binding

Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name.

use App\Models\User;

// Laravel automatically finds the User instance with the ID provided in the URL
Route::get('/users/{user}', function (User $user) {
    return $user->email;
});

Middleware

Middleware provides a convenient mechanism for inspecting and filtering HTTP requests entering your application. Conceptually, you can visualize middleware as a series of "layers" that a request must pass through before it reaches your route closure or controller. Each layer can examine the request and even reject it entirely. For example, Laravel includes middleware that verifies the user of your application is authenticated; if the user is not authenticated, the middleware will redirect the user to the login screen. Conversely, if the user is authenticated, the middleware will allow the request to proceed further into the application.

How Middleware Works

All middleware in Laravel are classes that contain a handle method. This method receives the incoming $request and a $next closure. Calling the $next closure passes the request deeper into the application, eventually reaching the intended route. If a middleware performs a task before calling $next, it is considered "Before Middleware." If it performs a task after calling $next (on the response object), it is "After Middleware."

Type Timing Use Case
Before Middleware Before the request hits the controller. Authentication, CSRF check, Request logging.
After Middleware After the controller logic executes. Adding security headers, Minifying HTML, Response logging.

Example of a Basic Middleware:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureTokenIsValid
{
    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next): Response
    {
        // Before logic: Check if the request contains a specific token
        if ($request->input('token') !== 'my-secret-token') {
            return redirect('home');
        }

        // Pass the request to the next layer
        return $next($request);
    }
}

Registering Middleware

In modern Laravel (version 11 and above), middleware is primarily configured in the bootstrap/app.php file. You can define global middleware that runs on every request, or assign middleware to specific route groups.

Global Middleware

Global middleware runs on every single HTTP request to your application, including API and Web routes.

// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(EnsureTokenIsValid::class);
    })
    ->create();

Assigning Middleware to Routes

If you want to assign middleware to specific routes, you may use the middleware method when defining the route.

use App\Http\Middleware\EnsureTokenIsValid;

Route::get('/profile', function () {
    // ...
})->middleware(EnsureTokenIsValid::class);

// You can also assign multiple middleware
Route::get('/', function () {
    // ...
})->middleware([First::class, Second::class]);

Middleware Groups

To make route definitions cleaner, Laravel groups several middleware under a single key. By default, Laravel provides web and api groups. The web group includes middleware for sessions, CSRF protection, and cookie encryption, while the api group is stripped down for stateless communication.

Group Included Middleware (Partial List) Purpose
web EncryptCookies, StartSession,
VerifyCsrfToken
Standard browser-based web pages.
api ThrottleRequests,
SubstituteBindings
Stateless API endpoints.

Middleware Parameters

Middleware can also receive additional custom parameters. For example, if your application needs to verify that the authenticated user has a specific "role" before performing an action, you can pass that role name to the middleware.

// Middleware Definition
public function handle(Request $request, Closure $next, string $role): Response
{
    if (! $request->user()->hasRole($role)) {
        abort(403);
    }

    return $next($request);
}

// Route Definition
Route::put('/post/{id}', function (string $id) {
    // ...
})->middleware('role:editor');

Note

Terminable Middleware

Sometimes a middleware may need to do some work after the HTTP response has already been sent to the browser. If you define a terminate method on your middleware, it will automatically be called after the response is sent. This is useful for heavy logging or cleaning up resources that shouldn't delay the user's page load.

Warning: CSRF Middleware

The VerifyCsrfToken middleware is included in the web middleware group by default. If you are building an API that will be consumed by mobile apps or third-party services (stateless), you should place those routes in the api group or the api.php file to avoid 419 "Page Expired" errors caused by missing CSRF tokens.

CSRF Protection

Cross-Site Request Forgery (CSRF) is a type of malicious exploit where unauthorized commands are transmitted from a user that the web application trusts. Unlike Cross-Site Scripting (XSS), which aims to steal data, CSRF targets state-changing requests, such as changing a user's password, deleting data, or transferring funds. Laravel protects your application from these attacks by automatically generating a CSRF "token" for each active user session managed by the application. This token is used to verify that the authenticated user is the one actually making the requests to the application.

How CSRF Protection Works

Laravel’s VerifyCsrfToken middleware, which is included in the web middleware group by default, automatically verifies that the token in the request input matches the token stored in the user's session. When a user session is initialized, Laravel generates a random string as the CSRF token. This token is then compared against the value submitted in subsequent POST, PUT, PATCH, or DELETE requests. If the tokens do not match, the request is rejected with a 419 HTTP status code (Page Expired).

Component Responsibility
User Session Stores the "known good" token on the server side.
Blade Directive Injects a hidden input field with the token into HTML forms.
X-CSRF-TOKEN An HTTP header used by JavaScript frameworks to pass the token.
VerifyCsrfToken Middleware that intercepts requests to compare the submitted token with the session.

Protecting HTML Forms

Any time you define an HTML form in your application that points to a POST, PUT, PATCH, or DELETE route, you must include a hidden CSRF token field. This allows the CSRF protection middleware to validate the request. For convenience, Laravel provides the @csrf Blade directive to generate the hidden input field automatically:

<form method="POST" action="/profile/update">
    @csrf

    <label for="email">New Email Address:</label>
    <input type="email" name="email" id="email">

    <button type="submit">Update Email</button>
</form>

CSRF and JavaScript (AJAX)

When building JavaScript-driven applications (like those using Axios or Fetch), it is often impractical to manually pass a CSRF token as a request parameter. To solve this, Laravel stores the current CSRF token in an encrypted XSRF-TOKEN cookie. Most modern JavaScript libraries, including Axios, will automatically read this cookie and send it along with every outgoing request in the X-XSRF-TOKEN header.

If you are not using a library that handles this automatically, you can manually include a meta tag in your application's <head> and then configure your JS to read from it:

<meta name="csrf-token" content="{{ csrf_token() }}">

<script>
    // Example of manually setting the header in a fetch request
    fetch('/api/data', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
        },
        body: JSON.stringify({ data: 'example' })
    });
</script>

Excluding URIs from CSRF Protection

Occasionally, you may wish to exclude a set of URIs from CSRF protection. This is common when you are expecting incoming webhooks from external services (like Stripe or GitHub) that do not have access to your user's session tokens.

In Laravel 11, you can exclude these routes within the bootstrap/app.php file using the validateCsrfTokens method:

// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->validateCsrfTokens(except: [
            'stripe/*',
            'http://example.com/foo/bar',
            'http://example.com/foo/*',
        ]);
    })
    ->create();

Warning: Security Risk of Exclusion

You should be extremely cautious when excluding routes from CSRF protection. Only exclude routes that are strictly used for machine-to-machine communication (APIs/Webhooks). Never exclude a route that performs a state change (like updating a user profile) if that route is accessed via a standard web browser.

Note

CSRF and Sessions

CSRF protection is tied directly to the Laravel Session. If your session driver is set to null (common in stateless APIs), CSRF protection will not function correctly because there is no server-side token to compare against. For stateless APIs, you should use the api middleware group, which does not include the CSRF verification layer.

Controllers

Instead of defining all of your request handling logic as closures in your route files, you may wish to organize this behavior using controller classes. Controllers can group related request handling logic into a single class. For example, a UserController might handle all incoming requests related to users, including showing, creating, updating, and deleting users. Controllers are typically stored in the app/Http/Controllers directory.

Writing Controllers

A basic controller should extend the base controller class included with Laravel: App\Http\Controllers\Controller. This base class provides several convenience methods, such as the middleware method, which may be used to attach middleware to controller actions.

When a route points to a controller, Laravel's Service Container automatically resolves the controller and injects any type-hinted dependencies into the constructor or the specific method being called.

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\View\View;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Show the profile for a given user.
     */
    public function show(string $id): View
    {
        return view('user.profile', [
            'user' => User::findOrFail($id)
        ]);
    }
}

Once you have defined a controller, you can register a route to the controller action like so:

use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;

Route::get('/user/{id}', [UserController::class, 'show']);

Single Action Controllers

If a controller action is particularly complex, you might find it convenient to dedicate an entire controller class to that single action. To accomplish this, you may define a single __invoke method within the controller. When registering routes for single-action controllers, you do not need to specify a controller method.

<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Http\Controllers\Controller;

class ProvisionServer extends Controller
{
    /**
     * Provision a new web server.
     */
    public function __invoke()
    {
        // ... Logic for provisioning
    }
}

// Routing for the invokable controller
Route::post('/server', ProvisionServer::class);

Resource Controllers

Laravel resource routing assigns the typical "CRUD" (Create, Read, Update, Delete) routes to a controller with a single line of code. You can quickly create a resource controller using the Artisan CLI command: php artisan make:controller PhotoController --resource.

The following table demonstrates the actions handled by a resource controller and their corresponding HTTP verbs/URIs:

Verb URI Action Route Name Purpose
GET /photos index photos.index Display a list of resources.
GET /photos/create create photos.create Show the form to create a resource.
POST /photos store photos.store Store a new resource in the database.
GET /photos/{photo} show photos.show Display a specific resource.
GET /photos/{photo}/edit edit photos.edit Show the form to edit a resource.
PUT/PATCH /photos/{photo} update photos.update Update a specific resource.
DELETE /photos/{photo} destroy photos.destroy Delete a specific resource.

To register all these routes at once, use the resource method:

use App\Http\Controllers\PhotoController;

Route::resource('photos', PhotoController::class);

Dependency Injection & Controllers

Laravel's service container is used to resolve all Laravel controllers. As a result, you may type-hint any dependencies your controller may need in its constructor. The declared dependencies will automatically be resolved and injected into the controller instance.

<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * The user repository instance.
     */
    protected $users;

    /**
     * Create a new controller instance.
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }
}

Note

Controller Middleware

While middleware can be assigned to routes in your route files, you may also specify middleware within your controller's constructor. This allows you to restrict certain actions within a single controller to specific middleware. However, in modern Laravel versions, it is generally preferred to define middleware in the route files to keep the controller clean.

Warning: Bloated Controllers

A common anti-pattern in MVC development is the "Fat Controller." Controllers should ideally only be responsible for receiving a request and returning a response. Complex business logic, external API calls, or complicated database queries should be abstracted into Service Classes or Action Classes to keep your controllers slim and testable.

HTTP Requests

Laravel’s Illuminate\Http\Request class provides an object-oriented way to interact with the current HTTP request being handled by your application. Instead of using PHP superglobals like $_GET, $_POST, and $_FILES, Laravel injects a Request instance into your controller methods, allowing you to retrieve input, cookies, files, and even the request's structural metadata (like headers or the URI) through a consistent and testable API.

Accessing the Request

To obtain an instance of the current HTTP request via dependency injection, you should type-hint the Illuminate\Http\Request class on your controller method. The incoming request instance will automatically be injected by the Laravel Service Container.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Store a new user.
     */
    public function store(Request $request)
    {
        // Retrieve the 'name' input field
        $name = $request->input('name');

        // Retrieve the 'email' input field with a default value
        $email = $request->input('email', 'guest@example.com');

        return response()->json(['name' => $name, 'email' => $email]);
    }
}

Retrieving Input

Laravel provides several methods to access user input regardless of the HTTP verb used (GET, POST, PUT, etc.). Whether the data is sent via a query string or as form-encoded data, the input method will retrieve it.

Method Description Use Case
all() Retrieves an array of all input data. Bulk operations or debugging.
input('key') Retrieves a specific input value. Standard form field access.
query('key') Retrieves values specifically from the query string (?id=1). Filtering or sorting logic.
only(['a', 'b']) Retrieves a subset of the input data. Preventing mass-assignment vulnerabilities.
except(['a']) Retrieves all data except specified keys. Excluding sensitive fields like passwords.
has('key') Checks if a value is present in the request. Conditional logic.

Input Trimming & Normalization

By default, Laravel includes the TrimStrings and ConvertEmptyStringsToNull middleware in the global middleware stack. This means that any string input will automatically have whitespace trimmed from the beginning and end, and empty strings will be converted to null. This ensures data consistency before it even reaches your controller logic.

// If a user submits "   hello   ", $request->input('message') returns:
"hello"

// If a user submits an empty field, $request->input('message') returns:
null

Retrieving Files

You may retrieve uploaded files from a Illuminate\Http\Request instance using the file method or by using dynamic properties. The file method returns an instance of the Illuminate\Http\UploadedFile class, which extends the PHP SplFileInfo class and provides a variety of methods for interacting with the file.

// Check if a file was uploaded
if ($request->hasFile('photo')) {
    $file = $request->file('photo');

    // Get the original file name
    $name = $file->getClientOriginalName();

    // Store the file on the 's3' disk
    $path = $file->store('images', 's3');
}

Request Path & Method Inspection

The request instance provides a variety of methods for examining the HTTP request. This is particularly useful for conditionally executing logic based on the URI or the request method.

Method Return Example Description
path() user/profile Returns the request's path info.
is('admin/*') true Checks if the path matches a pattern.
url() https://site.com/user/profile Returns the URL without the query string.
fullUrl() https://site.com/user/profile?id=1 Returns the URL including the query string.
method() POST Returns the HTTP verb of the request.
isMethod('post') true Validates the HTTP verb.

Note

PSR-7 Requests

The PSR-7 standard specifies HTTP message interfaces for requests and responses. If you would like to use a PSR-7 request instead of a Laravel request, you will first need to install a few libraries. Laravel uses the Symfony HTTP Bridge component to convert typical Laravel requests and responses into PSR-7 compatible implementations.

Warning: Input Security

While the all() method is convenient, you should avoid passing its output directly into Eloquent models (e.g., User::create($request->all())). This creates a security risk called Mass Assignment, where a malicious user could inject fields like is_admin=1 into the request. Always use $request->only() or Form Request Validation to whitelist allowed fields.

HTTP Responses

All routes and controllers in Laravel should return a response to be sent back to the user's browser. Laravel provides several different ways to return responses. The most basic response is returning a string from a route or controller; however, most developer actions will involve returning a full Illuminate\Http\Response instance or a Redirect. Returning a full Response instance allows you to customize the response's HTTP status code and headers.

Creating Responses

While strings and arrays are automatically converted into full HTTP responses by the framework, you may often need to initiate a response manually to define custom status codes or headers. The response helper can be used to generate these instances. When you return an array, Laravel automatically converts it to a JSON response and sets the Content-Type header to application/json.

use Illuminate\Http\Response;

Route::get('/custom-response', function () {
    return response('Hello World', 200)
            ->header('Content-Type', 'text/plain')
            ->header('X-Header-One', 'Header Value');
});

Response Types

Laravel's response helper provides a variety of methods for generating different types of response instances, such as JSON, file downloads, and views.

Response Type Method Description
View view('name', $data) Returns a rendered Blade template.
JSON json(['key' => 'value']) Automatically sets the JSON content type.
Download download($pathToFile) Forces the browser to download a file.
File file($pathToFile) Displays a file (like an image/PDF) in the browser.
Stream stream($callback) Streams a response for large data sets.

Example of a JSON Response:

return response()->json([
    'name' => 'Abigail',
    'state' => 'CA',
], 201);

Redirects

Redirect responses are instances of the Illuminate\Http\RedirectResponse class and contain the proper headers needed to redirect the user to another URL. You may use the redirect helper to generate these. Common practices include redirecting to the previous location, a named route, or a specific controller action.

// Redirecting to a specific URL
return redirect('/home/dashboard');

// Redirecting to a named route
return redirect()->route('login');

// Redirecting back to the previous location (useful for form validation)
return back()->withInput();

Attaching Headers to Responses

Most response methods are chainable, allowing for the fluent construction of response instances. You may use the header method to add a series of headers to the response before sending it back to the user. You may also use the withCookie method to easily attach cookies to your outgoing response.

return response($content)
        ->header('Content-Type', $type)
        ->header('X-Header-One', 'Header Value')
        ->withCookie(cookie('name', 'value', $minutes));

Note

Response Macros

If you would like to define a custom response that you can re-use in a variety of your routes and controllers, you may use the macro method on the Response facade. This is typically done within the boot method of a Service Provider. Once defined, you can call your macro using response()->yourMacroName().

Warning: Sensitive Data in JSON

When returning Eloquent models or collections directly as JSON, Laravel automatically serializes them. Ensure you have defined the $hidden property in your Eloquent models to protect sensitive data like passwords or API tokens from being accidentally included in the response.

// In your User model
protected $hidden = [
    'password',
    'remember_token',
];

Views

Views in Laravel serve as the presentation layer of your application, separating your controller and business logic from your user interface. While controllers handle the logic of processing a request, views are responsible for the actual HTML output sent to the user's browser. Laravel views are typically written using Blade, a powerful templating engine that allows you to write plain HTML enhanced with PHP-like control structures and data binding.

All views are stored in the resources/views directory. When a view is requested, Laravel compiles the template into plain PHP code and caches it for performance, ensuring that there is zero overhead when rendering the page for subsequent users.

Creating and Rendering Views

A view file is simply a file with a .blade.php extension. To return a view from a route or controller, you use the global view helper function. This function accepts the name of the view file (relative to the resources/views directory) and an optional array of data to pass to the template.

// resources/views/greeting.blade.php
<html>
    <body>
        <h1>Hello, {{ $name }}</h1>
    </body>
</html>

// In your routes/web.php or Controller
Route::get('/welcome', function () {
    return view('greeting', ['name' => 'James']);
});

Nested view directories use "dot" notation to indicate the path. For example, if your view is stored at resources/views/admin/profile.blade.php, you should return it using view('admin.profile').

Passing Data to Views

You may pass an array of data to views using the second argument of the view helper. Once the data is passed, each key in the array becomes a variable inside the view. Alternatively, you may use the with method to chain data onto the view instance.

Method Example Syntax Best Use Case
Array Argument view('profile', ['user' => $user]) Passing multiple variables at once.
with() Method view('profile')->with('user', $user) Fluent syntax for single variables.
compact() view('profile', compact('user', 'posts')) Passing existing local variables quickly.

View Composers

View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view every time that view is rendered (such as a list of categories in a sidebar or the authenticated user's profile), a view composer can help you organize that logic into a single location rather than duplicating it in multiple controllers.

// app/Providers/ViewServiceProvider.php
use App\View\Composers\ProfileComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

public function boot(): void
{
    // Using a class-based composer...
    View::composer('profile', ProfileComposer::class);

    // Using a closure-based composer...
    View::composer('dashboard', function ($view) {
        $view->with('count', \App\Models\User::count());
    });
}

The Blade Templating Engine

Blade is the powerful templating engine provided by Laravel. Unlike other PHP templating engines, Blade does not restrict you from using plain PHP code in your templates. In fact, all Blade templates are compiled into plain PHP code and cached until they are modified.

Displaying Data

You may display data passed to your Blade views by wrapping the variable in curly braces. Blade {{ }} statements are automatically sent through PHP's htmlspecialchars function to prevent XSS attacks.

Hello, {{ $name }}.
The current UNIX timestamp is {{ time() }}.

Control Structures

Blade provides convenient shortcuts for common PHP control structures, such as conditional statements and loops. These shortcuts provide a very clean, terse way of working with PHP control structures while remaining familiar to PHP developers.

@if (count($records) === 1)
    I have one record!
@elseif (count($records) > 1)
    I have multiple records!
@else
    I don't have any records!
@endif

@foreach ($users as $user)
    <p>This is user {{ $user->id }}</p>
@endforeach

Note

Unescaped Data

By default, Blade {{ }} statements are automatically sanitized. If you need to display a variable without escaping it (for example, if it contains HTML that you trust), you may use the {!! !!} syntax: Hello, {!! $name !!}. Be extremely careful when using this with user-provided content.

Warning: View Caching in Production

In a production environment, you should run the php artisan view:cache command. This will pre-compile all your Blade templates so that they do not need to be compiled on demand, significantly improving the speed of every request that returns a view.

Blade Templates (Directives, Components)

Blade is the powerful, feature-rich templating engine provided by Laravel. Unlike other PHP templating engines, Blade does not restrict you from using plain PHP code in your templates; in fact, all Blade templates are compiled into plain PHP code and cached until they are modified. Blade adds zero overhead to your application while providing convenient shortcuts for common PHP tasks, such as displaying data, conditional logic, and looping. Its two most powerful features—Directives and Components—allow for a highly modular and readable frontend architecture.

Blade Directives

Directives are the "syntactic sugar" of Blade. They are prefixed with the @ symbol and provide a clean, terse way of working with PHP control structures. Because these directives are compiled into raw PHP, they are extremely performant.

Conditionals and Loops

Blade provides directives that map directly to PHP's native control structures but with a much cleaner syntax.

Directive PHP Equivalent Description
@if, @else, @endif if ($val) { ... } Standard conditional logic.
@unless, @endunless if (! $val) { ... } Executes if the condition is false.
@foreach, @endforeach foreach ($items as $item) The most common loop for collections.
@forelse, @empty foreach + if (empty) Loop with a fallback if the collection is empty.
@auth, @guest Auth::check() Quick checks for user authentication status.

Example of @forelse Usage:

<ul>
    @forelse ($users as $user)
        <li>{{ $user->name }}</li>
    @empty
        <p>No users found in the system.</p>
    @endforelse
</ul>

Blade Components

Components are the modern approach to building reusable UI elements in Laravel. They provide a similar experience to frontend frameworks like Vue or React, allowing you to wrap HTML and logic into a single tag. Components can be either class-based (for complex logic) or anonymous (for simple templates).

Defining and Using Components

You can create a component using the Artisan command: php artisan make:component Alert. This creates a class in app/View/Components and a view in resources/views/components.

<div class="alert alert-{{ $type }}">
    {{ $slot }}
</div>

<x-alert type="danger">
    <strong>Error!</strong> Something went wrong.
</x-alert>

Component Attributes and Slots

Components often need to accept dynamic data. You can pass data to a component via attributes. If an attribute is prefixed with a colon :, it will be treated as a PHP expression. The {{ $slot }} variable is used to render whatever content is placed inside the component tags.

Feature Syntax Purpose
Simple Attribute type="info" Passes a hard-coded string.
PHP Attribute :message="$message" Passes a PHP variable or expression.
Named Slots <x-slot:title> Allows multiple injection points in one component.
Attributes Bag {{ $attributes }} Forwards unexpected attributes (like class or id) to the root element.

Layouts via Components

Most web applications maintain the same general layout across various pages. Instead of repeating the entire HTML structure in every view, you can define a "layout" component.

<html>
    <head>
        <title>{{ $title ?? 'Default Title' }}</title>
    </head>
    <body>
        <nav>...</nav>
        {{ $slot }}
    </body>
</html>

<x-layout>
    <x-slot:title>Home Page</x-slot:title>
    <h1>Welcome to our website!</h1>
</x-layout>

Custom Directives

Laravel allows you to define your own custom Blade directives to simplify repetitive patterns in your code. This is typically done within the boot method of your AppServiceProvider.

// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    // A directive to format currency
    Blade::directive('money', function (string $amount) {
        return "<?php echo '$' . number_format($amount, 2); ?>";
    });
}

// Usage in Blade:
// @money($product->price)

Note

The $loop Variable

When iterating through a @foreach loop, a $loop variable is automatically available. This object provides useful information such as $loop->first, $loop->last, or $loop->iteration (a 1-based index), allowing you to style the first or last items of a list differently.

Warning: Directive Compilation

When you change the logic of a custom Blade directive in your Service Provider, the changes may not appear immediately in your browser. Since Blade views are compiled and cached, you must clear the view cache using php artisan view:clear to force the framework to recompile the templates with the new directive logic.

Asset Bundling (Vite)

Asset bundling is the process of taking your application's raw frontend source files—such as JavaScript, TypeScript, CSS, and images—and processing them into optimized, minified files suitable for production. In modern Laravel applications, this is handled by Vite, a next-generation frontend build tool that significantly improves the developer experience. Vite replaced the older Webpack-based "Laravel Mix" by offering nearly instantaneous Hot Module Replacement (HMR) and a highly optimized rollup-based build process.

How Vite Works with Laravel

Vite operates differently depending on your environment. During development, Vite serves your assets via native browser ES modules, meaning it does not need to bundle your code every time a file changes. It simply serves the modified file directly to the browser. In production, Vite uses Rollup to bundle your assets into highly optimized, versioned files that can be cached effectively by the browser.

Feature Development Mode (npm run dev) Production Mode (npm run build)
Speed Instantaneous; no bundling required. Optimized for delivery.
HMR Changes reflect in the browser without a refresh. N/A (Static files).
Output Served from a local dev server (default port 5173). Minified files in public/build.
Manifest N/A. A manifest.json maps source files to hashed versions.

Configuration

The configuration for Vite is stored in a vite.config.js file at the root of your Laravel project. This file tells Vite which entry points (JavaScript and CSS files) it should process.

// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: [
                'resources/css/app.css', 
                'resources/js/app.js'
            ],
            refresh: true, // Enables auto-refresh for Blade templates
        }),
    ],
});

Loading Assets in Blade

To include your Vite-processed assets in your HTML, you use the @vite Blade directive. This directive is intelligent: in development, it injects the Vite client and points to the dev server; in production, it reads the manifest.json file to inject the correct hashed filenames and CSS links.

<head>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>

Handling Common Assets

Beyond JavaScript and CSS, Vite can also process images, fonts, and other static assets. When you reference an asset within your JavaScript or CSS, Vite will automatically include it in the build process and update the URL.

Images in JavaScript

import logo from '../images/logo.png';

document.getElementById('logo').src = logo;

Images in CSS

.hero {
    /* Vite will resolve this path and hash the file */
    background-image: url('../images/hero-bg.jpg');
}

Images in Blade

To reference an asset inside a Blade template that has been processed by Vite, you can use the Vite::asset() method:

<img src="{{ Vite::asset('resources/images/logo.png') }}" alt="Logo">

Note

Hot Module Replacement (HMR)

When running npm run dev, Vite enables HMR. This means when you save a JavaScript file or a CSS file, only the modified module is updated in the browser. You don't lose the "state" of your page (like values in an input field), and the update happens in milliseconds.

Warning: Deployment Requirement

If you deploy your application to a production server without running the build command, your site will fail to load assets. You must execute npm run build as part of your CI/CD pipeline. This creates the public/build directory and the manifest.json file that Laravel's @vite directive requires.

Vite with CSS Frameworks

Laravel is often paired with Tailwind CSS. Because Vite uses PostCSS under the hood, integrating Tailwind is seamless. You simply need to ensure your tailwind.config.js is configured and that you import Tailwind in your main CSS file.

/* resources/css/app.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

URL Generation

Laravel provides several helpers to assist you in generating URLs for your application. While you could hard-code URLs in your templates and API responses, using Laravel's built-in generation tools ensures that your links remain valid even if you change your application's domain or restructure your route URIs. These helpers automatically account for the protocol (HTTP or HTTPS) and the base URL defined in your .env file.

Basic URL Generation

The most fundamental way to generate a URL is via the url helper. This function simply appends the path you provide to the application's base URL.

// If APP_URL is http://example.com
$url = url('/posts/1'); 

// Output: http://example.com/posts/1

If the current request is secure (HTTPS), the generated URL will also use HTTPS. You may also access the current URL or the previous URL using the following methods:

Method Output Example Purpose
url()->current() https://site.com/user The URL without the query string.
url()->full() https://site.com/user?id=1 The URL including the query string.
url()->previous() https://site.com/dashboard The URL of the previous request.

URLs for Named Routes

The route helper generates URLs for named routes. This is the preferred method for generating links because it decouples your UI from your URI structure. If you change a route's path from /user/profile to /account/settings, you only need to update the route definition; all calls to route('profile') will update automatically.

// Route Definition
Route::get('/post/{post}', function (Post $post) {
    // ...
})->name('post.show');

// URL Generation
$url = route('post.show', ['post' => 1]);

// Output: http://example.com/post/1

If the route requires multiple parameters, you can pass them as an associative array. Any extra array keys that do not match route parameters will be appended to the URL as a query string.

URLs for Controller Actions

The action helper generates a URL for a given controller action. You do not need to provide the full namespace of the controller if it is registered within the standard App\Http\Controllers namespace.

use App\Http\Controllers\UserController;

$url = action([UserController::class, 'index']);

// With parameters
$url = action([UserController::class, 'show'], ['id' => 1]);

Signed URLs

Laravel allows you to easily create "signed" URLs to named routes. These URLs have a "signature" hash appended to the query string which allows Laravel to verify that the URL has not been modified since it was created. This is exceptionally useful for "Unsubscribe" links or "Email Verification" links that should not be guessable or tamperable.

use Illuminate\Support\Facades\URL;

// Create a standard signed URL
$url = URL::signedRoute('unsubscribe', ['user' => 1]);

// Create a temporary signed URL (expires in 30 minutes)
$url = URL::temporarySignedRoute(
    'unsubscribe', now()->addMinutes(30), ['user' => 1]
);

To verify the signature on the incoming request, you should use the signed middleware provided by Laravel:

Route::get('/unsubscribe/{user}', function (Request $request) {
    // ...
})->name('unsubscribe')->middleware('signed');

Note

Asset URL Generation

To generate a URL for an asset (like a static image or a plain CSS file not processed by Vite), use the asset helper. This will point to the public directory: echo asset('img/logo.png'); // http://example.com/img/logo.png

Warning: The APP_URL Constant

When running Laravel from the CLI (e.g., in a scheduled task or a queued job), the framework cannot "guess" the base URL because there is no incoming web request. In these cases, Laravel defaults to the APP_URL value defined in your .env file. Ensure this is set correctly to avoid broken links in sent emails.

Session Storage

Since HTTP-driven applications are stateless, sessions provide a way to store information about the user across multiple requests. That user information is typically placed in a persistent store that can be accessed from subsequent requests. Laravel ships with a variety of session backends that are accessed through an expressive, unified API. Support for popular backends such as Memcached, Redis, and databases is included out of the box.

Configuration

Your application's session configuration file is stored at config/session.php. By default, Laravel is configured to use the file session driver, which works well for many applications. In production, you may consider using the database or redis drivers for even faster performance.

Driver Description Best Use Case
file Sessions are stored in storage/framework/sessions. Local development and small sites.
cookie Sessions are stored in encrypted, signed cookies. Small payloads; avoids server-side storage.
database Sessions are stored in a relational database table. Scalable apps with existing DB infrastructure.
memcached / redis Sessions are stored in fast, cache-based stores. High-traffic, multi-server applications.
array Sessions are stored in a PHP array and will not be persisted. Testing environments.

Interacting with the Session

There are two primary ways to work with session data in Laravel: the HTTP Request instance and the global session helper.

Retrieving Data

You can access session data from a Request instance, which can be type-hinted on a controller method.

public function show(Request $request, string $id)
{
    // Retrieve a piece of data from the session...
    $value = $request->session()->get('key');

    // Retrieve with a default value...
    $value = $request->session()->get('key', 'default');

    // Retrieve all data in the session...
    $data = $request->session()->all();
}

Storing Data

To store data in the session, you will typically use the request instance's put method or the global session helper.

// Via the Request instance...
$request->session()->put('key', 'value');

// Via the global helper...
session(['key' => 'value']);

Flash Data

Sometimes you may wish to store items in the session only for the next request. You may do so using the flash method. Data stored in the session using this method will only be available during the subsequent HTTP request, and then will be deleted. Flash data is primarily useful for short-lived status messages.

$request->session()->flash('status', 'Task was successful!');

In your Blade view, you can then check for this status:

@if (session('status'))
    <div class="alert alert-success">
        {{ session('status') }}
    </div>
@endif

Deleting Data

The forget method will remove a piece of data from the session. If you would like to remove all data from the session, you may use the flush method.

Method Action
forget('key') Removes a specific key.
pull('key') Retrieves and then deletes the key (one-step).
flush() Removes all data from the session.
invalidate() Flushes the session and regenerates the ID (Security best practice).

Note

Session Blocking

By default, Laravel allows requests using the same session to execute concurrently. If you have a long-running request that modifies session data, it might "clobber" data from a faster request. You can use the block method in your route definition to prevent concurrent session access for specific routes.

Warning: Database Session Setup

When using the database session driver, you must create a table to contain the session items. Laravel provides an Artisan command to generate the migration for this table: php artisan session:table. Without this table, the application will throw an error as soon as it tries to write session data.

Validation (Form Requests, Rules)

Validation is a critical aspect of any web application, ensuring that the data provided by users meets your business requirements and security standards before it is processed or stored. Laravel provides several approaches to validate incoming data, ranging from simple inline validation in controllers to dedicated "Form Request" classes that keep your controllers lean and readable.

Validation Approaches

Laravel offers three primary ways to handle validation. Choosing the right one depends on the complexity of the data and the design of your application.

Method Best Use Case Pros
Inline Validation Small, quick projects or single-field updates. Quick to implement; logic is right in the controller.
Form Requests Complex forms, reusable logic, or large applications. Extremely clean controllers; reusable across methods.
Manual Validator AJAX requests or non-HTTP data (e.g., CLI, Queues). Full control over the validator instance and redirects.

Inline Validation

The simplest way to validate a request is to use the validate method available on the Illuminate\Http\Request object. If the validation rules pass, your code will continue executing normally; if they fail, an exception is thrown, and the user is automatically redirected back to their previous location with the error messages and input data.

public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'publish_at' => 'nullable|date',
    ]);

    // The current request is valid...
    Post::create($validated);
}

Form Request Validation

For more complex validation scenarios, you may wish to create a "form request." Form requests are custom request classes that encapsulate their own validation and authorization logic. You can generate one using the Artisan command: php artisan make:request StorePostRequest.

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return $this->user()->can('create-posts');
    }

    /**
     * Get the validation rules that apply to the request.
     */
    public function rules(): array
    {
        return [
            'title' => ['required', 'unique:posts', 'max:255'],
            'body' => ['required'],
        ];
    }
}

In your controller, you simply type-hint the request:

public function store(StorePostRequest $request)
{
    // The request is already validated and authorized here!
    $validated = $request->validated();
}

Available Validation Rules

Laravel includes over 90 built-in validation rules. Here are some of the most commonly used:

  • required: The field must be present and not empty.
  • email: The field must be formatted as an e-mail address.
  • unique:table,column: The value must not exist in the specified database table.
  • confirmed: The field must have a matching field of foo_confirmation (common for passwords).
  • exists:table,column The value must exist in the specified database table.
  • mimes:jpg,png: The file must match one of the given extensions.

Displaying Validation Errors

When validation fails, Laravel automatically flashes the errors to the session. In your Blade templates, you can use the @error directive or the $errors variable to display these messages to the user.

<label for="title">Post Title</label>
<input id="title" type="text" name="title" class="@error('title') is-invalid @enderror">

@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

Note

Custom Validation Rules

If the built-in rules aren't enough, you can create custom rule objects using php artisan make:rule Uppercase. This allows you to define complex, reusable logic in a dedicated class's validate method.

Warning: The validated() Method

When using $request->validate() or Form Requests, always use the $request->validated() method to retrieve your data instead of $request->all(). This ensures you are only dealing with data that has actually been checked against your rules, preventing malicious users from injecting unvalidated fields into your database.

Error Handling & Logging

In any production application, things will eventually go wrong—a database connection might time out, an external API might be down, or a user might trigger an edge case. Laravel provides a robust, built-in error handling and logging system that makes it easy to catch, report, and debug these issues. By default, Laravel uses the Monolog library, which provides support for a variety of powerful log handlers.

Error Handling Configuration

In Laravel 11, the primary configuration for exception handling is found in bootstrap/app.php. This file allows you to customize how the application handles specific exceptions, ignores certain errors, or renders custom error pages.

Feature Description
APP_DEBUG In .env, set to true for detailed stack traces (Dev) or false for generic error pages (Prod).
Custom Rendering Allows you to return a specific response (like JSON) when a specific exception occurs.
Ignoring Exceptions Prevents specific exceptions from being logged to the disk to save space.

Example of Custom Exception Handling:

// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
    $exceptions->render(function (InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', [], 500);
    });
})

The Logging System

Laravel’s logging system is "channel" based. A channel defines where your log messages are sent. You might want to log simple errors to a single file, while critical system failures are sent to Slack or an external service like Sentry or Bugsnag.

Available Log Channels

Configuration is located in config/logging.php.

Channel Description
single A single laravel.log file in storage/logs.
daily A new log file is created for each day; old logs are pruned automatically.
slack Sends log messages to a Slack webhook.
stack A "wrapper" that allows you to send a single log message to multiple channels at once.
syslog Writes to the system's logging facility.

Writing Log Messages

You may write information to your logs using the Log facade. The logger provides the eight logging levels defined in the RFC 5424 specification: emergency, alert, critical, error, warning, notice, info, and debug.

use Illuminate\Support\Facades\Log;

Log::info('User failed to login.', ['id' => $user->id]);

try {
    // Dangerous operation...
} catch (Exception $e) {
    Log::error('Critical System Failure', [
        'exception' => $e->getMessage(),
        'trace' => $e->getTraceAsString(),
    ]);
}

HTTP Exceptions (Error Pages)

Some exceptions describe HTTP error codes from the server. For example, this may be a "page not found" error (404), an "unauthorized error" (401), or even a developer-generated 500 error. To return such a response from anywhere in your application, use the abort helper:

// Triggers a 404 Not Found
abort(404);

// Triggers a 403 Forbidden with a custom message
abort(403, 'You do not have permission to access this resource.');

Customizing Error Pages

If you want to create a custom error page for a specific HTTP status code, create a view file in resources/views/errors/. For example, 404.blade.php will be used for all 404 errors.

Note

Contextual Information

You can define "global" context that is added to every log entry for the duration of a request. This is useful for tracking a specific user's journey through the logs.

Log::withContext(['request_id' => $id]);

Warning: Sensitive Data in Logs

Never log sensitive information such as user passwords, credit card numbers, or API secrets. Laravel's default exception handler automatically masks some of these fields, but when writing manual Log::info() calls, always sanitize the data array you pass.

Database Core Last updated: Feb. 28, 2026, 10:08 p.m.

Laravel treats the database as a first-class citizen, providing a fluent Query Builder that serves as a bridge between your code and various SQL dialects. This section focuses on the concept of Migrations, which is essentially "version control for your database." Instead of sharing SQL dumps, developers share migration files that programmatically define the table structures, ensuring the entire team stays in sync.

Beyond structure, the core database layer handles Schema Building and Seeding. Seeding allows you to populate your database with dummy data for testing purposes using Faker. This section ensures that interacting with the database is safe and consistent, protecting you from SQL injection by default and providing a clean syntax that makes complex joins and aggregates feel intuitive.

Getting Started (Connections & Configuration)

Laravel makes interacting with databases extremely simple across a variety of database backends using either raw SQL, the Fluent Query Builder, or the Eloquent ORM. By default, Laravel provides first-class support for five primary database systems. Configuration for your database services is located in the config/database.php file, where you may define all of your database connections and specify which connection should be used by default.

Supported Database Systems

Laravel's database layer is built on top of PHP Data Objects (PDO), ensuring compatibility and security across different environments.

Database PHP Extension Required Best Use Case
SQLite pdo_sqlite Local development, testing, and small, low-traffic apps.
MySQL pdo_mysql The industry standard for web applications.
MariaDB pdo_mysql A popular, open-source community drop-in for MySQL.
PostgreSQL pdo_pgsql Advanced features, complex queries, and data integrity.
SQL Server pdo_sqlsrv Enterprise environments using the Microsoft stack.

Configuration via Environment Variables

While the config/database.php file contains the structural settings, the actual credentials (host, database name, username, password) should always be stored in your .env file. This keeps sensitive information out of your version control system (Git).

# .env Example
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_app
DB_USERNAME=root
DB_PASSWORD=secret

For SQLite, Laravel 11 simplifies things even further. If you don't provide a database name, Laravel will look for a database/database.sqlite file. If it doesn't exist, the framework can even create it for you automatically.

Read & Write Connections

For high-traffic applications, you may wish to use one database server for SELECT statements and another for INSERT, UPDATE, and DELETE statements. Laravel makes this easy to configure within your mysql or pgsql connection array.

// config/database.php
'mysql' => [
    'read' => [
        'host' => [
            '192.168.1.1',
        ],
    ],
    'write' => [
        'host' => [
            '192.168.1.2',
        ],
    ],
    'sticky' => true,
    'driver' => 'mysql',
    // ...
],

The sticky option is an optional value that can be used to allow the immediate reading of records that have been written to the database during the current request cycle.

Running Raw SQL Queries

Once you have configured your database connection, you may run queries using the DB facade. The DB facade provides methods for each type of query: select, update, insert, delete, and statement.

Method Returns Example
DB::select() An array of results. DB::select('select * from users where id = ?', [1])
DB::insert() bool (success/fail). DB::insert('insert into users (name) values (?)', ['Alice'])
DB::update() int (affected rows). DB::update('update users set votes = 100 where name = ?', ['Alice'])
DB::delete() int (affected rows). DB::delete('delete from users')

Note

Using Multiple Connections

If your application uses multiple database connections, you can access them via the connection method on the DB facade: $users = DB::connection('sqlite')->select(...);

Warning: SQL Injection Protection

When running raw queries, never manually concatenate user input into your SQL strings. Always use "parameter binding" (the ? or :name syntax) as shown in the examples above. Laravel uses PDO parameter binding to sanitize all input, protecting your application from SQL injection attacks.

Query Builder

Laravel's database query builder provides a fluent, convenient interface for creating and running database queries. It uses PDO parameter binding to protect your application against SQL injection attacks, so there is no need to manually clean strings passed to the query builder. It can be used to perform most database operations in your application and works across all supported database systems.

Retrieving Results

The DB facade's table method begins a query. From there, you can chain constraints onto the query and finally use a "termination" method to retrieve the results.

Method Result Type Description
get() Collection Retrieves all rows matching the query.
first() stdClass Retrieves a single row (the first match).
value('col') mixed Retrieves a single value from the first row.
find(id) stdClass Retrieves a single row by its id column.
pluck('col') Collection Retrieves a list of values from a single column.

Example of basic retrieval:

use Illuminate\Support\Facades\DB;

$users = DB::table('users')->where('active', true)->get();

foreach ($users as $user) {
    echo $user->name; // Results are objects
}

Where Clauses

The where method is the most basic way to filter your data. It requires three arguments: the column name, the operator, and the value. If you only provide two arguments, Laravel assumes the operator is =.

// Standard comparison
$users = DB::table('users')->where('votes', '>', 100)->get();

// Logical "OR"
$users = DB::table('users')
            ->where('votes', '>', 100)
            ->orWhere('name', 'John')
            ->get();

// Sub-queries (Where Between, Where In, Where Null)
$users = DB::table('users')->whereBetween('votes', [1, 100])->get();
$users = DB::table('users')->whereIn('id', [1, 2, 3])->get();

Joins

The query builder may also be used to add join clauses to your queries. For a basic "inner join," you can use the join method.

$users = DB::table('users')
            ->join('contacts', 'users.id', '=', 'contacts.user_id')
            ->join('orders', 'users.id', '=', 'orders.user_id')
            ->select('users.*', 'contacts.phone', 'orders.price')
            ->get();

Inserts, Updates, and Deletes

The query builder also provides methods for modifying data.

Operation Method Example
Insert insert() DB::table('users')->insert(['email' => 'kayla@example.com', 'votes' => 0]);
Update update() DB::table('users')->where('id', 1)->update(['votes' => 1]);
Increment increment() DB::table('users')->increment('votes', 5);
Delete delete() DB::table('users')->where('votes', '<', 50)->delete();
Truncate truncate() DB::table('users')->truncate(); // Clears all rows and resets IDs

Aggregates and Pagination

The query builder also provides a variety of methods for retrieving aggregate values like count, max, min, avg, and sum. Additionally, one of the most loved features is the built-in pagination.

// Aggregates
$price = DB::table('orders')->max('price');

// Pagination
$users = DB::table('users')->paginate(15);
// In Blade: {{ $users->links() }}

Note

Database Transactions

You may use the transaction method on the DB facade to run a set of operations within a database transaction. If an exception is thrown within the closure, the transaction will automatically be rolled back.

DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);
    DB::table('posts')->delete();
});

Warning: Object vs. Model

Unlike Eloquent ORM, the Query Builder returns results as generic PHP stdClass objects. This means you cannot call model methods (like relationships or custom attributes) on the results. If you need that functionality, use Eloquent models instead.

Pagination

In many web applications, showing every database record on a single page is impractical due to performance and user experience concerns. Laravel's paginator is integrated with the Query Builder and Eloquent ORM out of the box, providing a convenient, automated way to split large datasets into manageable chunks.

Basic Usage

The most common way to paginate data is using the paginate method. This method automatically handles setting the proper limit and offset based on the page query string parameter in the user's request.

With Query Builder

use Illuminate\Support\Facades\DB;

$users = DB::table('users')->paginate(15);

With Eloquent

use App\Models\User;

$users = User::where('active', 1)->paginate(15);
Method Description Best Use Case
paginate(n) Shows links for all pages; performs a COUNT query. Standard dashboards or search results.
simplePaginate(n) Shows only "Next" and "Previous" links; faster performance. Infinite scrolls or very large datasets.
cursorPaginate(n) Uses "pointers" instead of offsets; most performant. High-frequency data (social media feeds).

Displaying Pagination Results

When you call paginate, you receive an instance of Illuminate\Pagination\LengthAwarePaginator. When you call simplePaginate, you receive Illuminate\Pagination\Paginator. To render the navigation links in your Blade view, simply call the links method:

<div class="container">
    @foreach ($users as $user)
        {{ $user->name }}
    @endforeach
</div>

{{ $users->links() }}

Customizing Pagination

Laravel allows you to customize the URI, query string, and even the "fragment" (anchor) used by the paginator.

  • Appending to Links: If you have search filters, you must append them to the pagination links so they persist when the user changes pages.
  • On Each Side: Control how many links are displayed on each side of the current page link.
// Appending search filters
$users = User::paginate(15)->withQueryString();

// Manually appending specific parameters
$users = User::paginate(15)->appends(['sort' => 'votes']);

// Adjusting link count
$users = User::paginate(15)->onEachSide(1);

Paginator Instance Methods

The paginator instance provides additional information about the state of the data through various methods:

Method Description
$users->count() Number of items on the current page.
$users->currentPage() The current page number.
$users->hasMorePages() Boolean: Are there more pages after this?
$users->lastPage() The total number of pages (Not available in simplePaginate).
$users->total() Total number of items in the database (Not available in simplePaginate).

Note

Tailwind & Bootstrap

By default, Laravel uses Tailwind CSS for its pagination links. If you are using Bootstrap, you should inform Laravel in the boot method of your App\Providers\AppServiceProvider:

use Illuminate\Pagination\Paginator;
public function boot(): void {
Paginator::useBootstrapFive();
}

Warning: Performance and COUNT(*)

The standard paginate() method executes a COUNT(*) query to determine how many total pages exist. On tables with millions of rows, this can be slow. In these cases, prefer simplePaginate() or cursorPaginate(), which do not need to count the entire table.

Migrations

Migrations are like version control for your database, allowing your team to modify and share the application's database schema definition. Instead of manually creating tables and columns in a database management tool, you define your schema in PHP files. This ensures that every developer on your team and your production environment are running the exact same database structure.

Migrations also allow you to "roll back" changes to a previous state, making it much safer to experiment with schema changes during development.

Creating and Running Migrations

You can generate a migration using the make:migration Artisan command. Laravel uses the name you provide to guess if you are creating a new table or modifying an existing one.

Action Command Result
Create Table php artisan make:migration create_flights_table Creates a new migration with a Schema::create block.
Update Table php artisan make:migration add_votes_to_users_table Creates a migration with a Schema::table block.
Run Migrations php artisan migrate Executes all outstanding migrations.
Rollback php artisan migrate:rollback Reverts the last "batch" of migrations.

Migration Structure

A migration class contains two methods: up and down. The up method is used to add new tables, columns, or indexes to your database, while the down method should reverse the operations performed by the up method.

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('flights', function (Blueprint $blueprint) {
            $blueprint->id(); // Alias for bigIncremental ID
            $blueprint->string('name');
            $blueprint->string('airline')->nullable();
            $blueprint->integer('passengers')->default(0);
            $blueprint->timestamps(); // Adds created_at and updated_at
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('flights');
    }
};

Available Column Types

The schema builder contains a variety of column types that you may use when building your tables. Laravel maps these PHP methods to the appropriate SQL data types for your specific database driver (MySQL, PostgreSQL, etc.).

Method Database Type Use Case
$table->string('name') VARCHAR Short text (names, titles).
$table->text('bio') TEXT Long-form content.
$table->integer('votes') INTEGER Standard numbers.
$table->boolean('is_active') BOOLEAN/TINYINT True/False flags.
$table->decimal('price', 8, 2) DECIMAL Precise currency values.
$table->foreignId('user_id') BIGINT Foreign keys (unsigned).

Modifying and Deleting Columns

To modify an existing column (e.g., changing its length or making it nullable), you should use the change method. To drop a column, use the dropColumn method.

Note

Before modifying columns, you may need to install the doctrine/dbal library via Composer, though modern Laravel versions (10+) support most modifications natively in MySQL, PostgreSQL, and SQL Server.

// Modifying a column
Schema::table('users', function (Blueprint $table) {
    $table->string('name', 50)->nullable()->change();
});

// Dropping a column
Schema::table('users', function (Blueprint $table) {
    $table->dropColumn('votes');
});

Foreign Key Constraints

Laravel also provides support for creating foreign key constraints, which are used to force data integrity at the database level.

Schema::table('posts', function (Blueprint $table) {
    // Shorthand for an unsigned big integer user_id 
    // referencing the id on the users table
    $table->foreignId('user_id')
          ->constrained()
          ->onUpdate('cascade')
          ->onDelete('cascade');
});

Note

Squash Your Migrations

As your application grows, you may accumulate hundreds of migration files. You can "squash" these into a single SQL file using php artisan schema:dump. This keeps your migrations directory clean and speeds up your testing suite.

Warning: Data Loss on Refresh

The php artisan migrate:refresh and php artisan migrate:fresh commands will drop all tables and re-run all migrations. Use these with extreme caution; they should essentially never be run on a production server, as they will delete all of your data.

Seeding

Database seeding is the process of populating your database with test data. While Migrations define the structure of your database, Seeders provide the initial data needed for development, testing, or production defaults (like administrative accounts or country lists). Laravel includes a simple method of seeding your database with test data using seed classes, which are stored in the database/seeders directory.

Creating and Running Seeders

By default, Laravel provides a DatabaseSeeder class. You can create additional seeders using the make:seeder Artisan command. All seeder classes are executed using the php artisan db:seed command.

Action Command Result
Create Seeder php artisan make:seeder UserSeeder Creates a new class in database/seeders.
Run All Seeders php artisan db:seed Executes the run method in DatabaseSeeder.
Run Specific Seeder php artisan db:seed --class=UserSeeder Executes only the specified seeder class.
Fresh Seed php artisan migrate:fresh --seed Drops all tables, re-migrates, and then seeds.

Writing Seeders

A seeder class contains one default method: run. This method is called when the db:seed Artisan command is executed. Within the run method, you may insert data into your database using the Query Builder or Eloquent models.

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        DB::table('users')->insert([
            'name' => 'Administrator',
            'email' => 'admin@example.com',
            'password' => Hash::make('password'),
        ]);
    }
}

Using Model Factories

Manually specifying the attributes for each database seed is cumbersome. Instead, you can use Model Factories to generate large amounts of database records. Factories allow you to define a "blueprint" for your models and then generate as many instances as you need with fake data.

// database/seeders/DatabaseSeeder.php

public function run(): void
{
    // Generate 10 random users using the User Factory
    \App\Models\User::factory(10)->create();

    // Generate a user with a specific relationship
    \App\Models\User::factory(5)
        ->hasPosts(3) // Creates 3 posts for each of the 5 users
        ->create();
}

Calling Additional Seeders

Within the DatabaseSeeder class, you may use the call method to execute additional seed classes. Using the call method allows you to break up your database seeding into multiple files so that no single seeder class becomes too large.

public function run(): void
{
    $this->call([
        UserSeeder::class,
        PostSeeder::class,
        CommentSeeder::class,
    ]);
}

Database Seeding Summary

Feature Description Best Practice
Faker Library Generates fake names, emails, and text. Use inside Factories for realistic test data.
Environment Check Prevent seeding in production. Use if (app()->environment('local')) in your seeder.
Silent Seeding Run seeds without output. Use the --silent flag in the CLI.

Note

The run Method

You are not limited to database operations inside the run method. You can use it to create filesystem directories, fetch data from external APIs to populate your database, or even run other Artisan commands.

Warning: Mass Assignment

When using Eloquent models in your seeders, remember that Mass Assignment protections still apply. Ensure that the fields you are seeding are either in the $fillable array of your model or use the unguard method to temporarily disable protection during the seed process.

Redis Integration

Redis is an open-source, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets, and sorted sets. In Laravel, Redis is primarily used for caching, session storage, and as a high-performance queue worker.

Laravel interacts with Redis through the phpredis PHP extension or the predis/predis package.

Configuration

The Redis configuration for your application is located in the config/database.php file. Within this file, you will see a redis array containing the connections used by your application.

Setting Default Value Description
client phpredis The driver used to communicate with Redis.
host 127.0.0.1 The IP address of your Redis server.
port 6379 The port Redis is listening on.
database 0 The specific Redis logical database index.

To configure these values, update your .env file:

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

Interacting with Redis

You may interact with Redis by calling various methods on the Redis facade. The Redis facade supports dynamic methods, meaning you may call any Redis command directly, and it will be passed to the server.

use Illuminate\Support\Facades\Redis;

// Simple Key-Value storage
Redis::set('name', 'Taylor');
$name = Redis::get('name');

// Working with Lists
Redis::lpush('frameworks', 'Laravel');
Redis::lpush('frameworks', 'Symfony');

// Executing complex commands
$values = Redis::lrange('frameworks', 0, -1);

Primary Use Cases in Laravel

While you can use Redis for general data storage, Laravel integrates it deeply into three core systems for maximum performance.

System Benefit of Redis
Cache Extremely fast retrieval for frequently accessed data compared to the file driver.
Session Ideal for "sticky" sessions in load-balanced environments with multiple web servers.
Queue Low latency and high throughput for processing background jobs.
Pub/Sub Allows for real-time communication, such as broadcasting events to Laravel Echo.

Pipelining and Transactions

To reduce network latency, you can "pipeline" multiple commands to be sent to the server in one go. Alternatively, you can use transactions to ensure a block of commands is executed atomically.

Pipelining

Redis::pipeline(function ($pipe) {
    for ($i = 0; $i < 1000; $i++) {
        $pipe->set("key:$i", $i);
    }
});

Transactions (Multi/Exec)

Redis::transaction(function ($tx) {
    $tx->set('user:1:votes', 1);
    $tx->incr('user:1:votes');
});

Note

Connection Clusters

If your application uses a Redis cluster, you should define these connections within the clusters option in your Redis configuration. This allows Laravel to shard data across multiple Redis nodes automatically.

Warning: Redis Memory Limits

Redis stores all data in RAM. If your Redis server reaches its maximum memory limit, it may start evicting keys (deleting data) based on its maxmemory-policy. Always monitor your Redis memory usage, especially when using it for both Cache and Sessions simultaneously.

Eloquent ORM Last updated: Feb. 28, 2026, 10:08 p.m.

Eloquent is Laravel’s Active Record implementation, and for many, it is the framework’s standout feature. It allows you to interact with your database tables as if they were simple PHP objects. This section focuses on Relationships (One-to-One, One-to-Many, Many-to-Many), teaching you how to define the connections between your data models in a way that feels like natural language.

A significant portion of Eloquent's philosophy involves Collections and Accessors/Mutators. Collections provide a powerful suite of methods to filter and transform your data after it's retrieved, while accessors allow you to format data on the fly (e.g., combining first_name and last_name into a full_name attribute). Eloquent transforms the database from a storage bin into a rich, interactive layer of your application logic.

Eloquent Getting Started

The Eloquent ORM (Object-Relational Mapper) is Laravel's powerful implementation of the "Active Record" pattern. It allows you to interact with your database using PHP syntax instead of writing raw SQL. Each database table has a corresponding "Model" that is used to interact with that table.

When using Eloquent, every row in your database table is represented as an instance of your Model class, making data manipulation intuitive and object-oriented.

Defining Models

By default, Eloquent models live in the app/Models directory. You can create a new model using the make:model Artisan command. Typically, you will want to create a migration at the same time to define the table structure.

Command Result
php artisan make:model Post Creates only the Model file.
php artisan make:model Post -m Creates the Model and a database migration.
php artisan make:model Post -a Creates Model, Migration, Factory, Seeder, and Controller (Recommended).

Basic Model Example:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    // By default, this model maps to the "flights" table
}

Model Conventions

Eloquent makes several assumptions about your database structure to reduce configuration. However, if your database does not follow these conventions, you can manually override them within the model.

Feature Convention Manual Override Example
Table Name Lowercase, plural (e.g., flights). protected $table = 'my_flights';
Primary Key An integer named id. protected $primaryKey = 'flight_id';
Timestamps Requires created_at and updated_at. public $timestamps = false;
Connection The default database connection. protected $connection = 'sqlite';

Retrieving Models

Once you have a model and its associated table, you can start retrieving data. Eloquent models are also Query Builders, so you can use all the methods available in the Fluent Query Builder.

use App\Models\Flight;

// Retrieve all records
$flights = Flight::all();

// Filtered retrieval
$flights = Flight::where('active', 1)
               ->orderBy('name')
               ->take(10)
               ->get();

// Retrieve by primary key
$flight = Flight::find(1);

// Throw an exception if not found (useful for APIs/Controllers)
$flight = Flight::findOrFail(1);

Inserts & Updates

To create a new record, simply create a new model instance, set its attributes, and call the save method. To update, retrieve the model first, change the attributes, and save again.

Basic Insert

$flight = new Flight;
$flight->name = 'Tokyo 747';
$flight->save();

Basic Update

$flight = Flight::find(1);
$flight->name = 'London 808';
$flight->save();

Mass Assignment

Mass assignment occurs when you pass an array of data to a model method, such as create or update. For security reasons, Eloquent requires you to define which attributes are "fillable" to prevent users from injecting unexpected data (like is_admin = true).

class Flight extends Model
{
    /**
     * The attributes that are mass assignable.
     */
    protected $fillable = ['name', 'airline'];
}

// Now you can use the create method:
Flight::create(['name' => 'Paris 101', 'airline' => 'Air France']);

Note

When you retrieve multiple records via Eloquent (using all() or get()), they are returned as an Eloquent Collection. This is a powerful wrapper around PHP arrays that allows you to map, filter, or reduce your data easily without hitting the database again.

Warning: Database Logic in Models

While it's tempting to put all your logic in the Model, try to keep them focused on data access. For complex business logic involving multiple models, consider using Service Classes to keep your Eloquent models from becoming "God Objects."

Relationships (One-to-One, One-to-Many, Many-to-Ma

Database tables are often related to one another. For example, a blog post may have many comments, or an order could be linked to the user who placed it. Eloquent makes managing and working with these relationships easy, supporting a variety of common relationship types.

Relationships are defined as methods on your Eloquent model classes. Since relationships also serve as powerful query builders, defining them as methods provides powerful method chaining and querying capabilities.

One-to-One

A one-to-one relationship is a very basic type of database relationship. For example, a User model might be associated with one Phone model.

Direction Method Description
Parent to Child hasOne() Defined on the model that does not hold the foreign key.
Child to Parent belongsTo() Defined on the model that holds the foreign key (user_id).
// User.php
public function phone() {
    return $this->hasOne(Phone::class);
}

// Accessing it:
$phone = User::find(1)->phone;

One-to-Many

A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models. For example, a blog Post may have an infinite number of Comments.

Direction Method Example
One (Parent) hasMany() return $this->hasMany(Comment::class);
Many (Child) belongsTo() return $this->belongsTo(Post::class);
// Post.php
public function comments() {
    return $this->hasMany(Comment::class);
}

// Accessing it (returns a Collection):
$comments = Post::find(1)->comments;

Many-to-Many

Many-to-many relations are slightly more complicated than hasOne and hasMany. An example is a User with many Roles, where the roles are also shared by other users. This requires a pivot table (e.g., role_user).

// User.php
public function roles() {
    return $this->belongsToMany(Role::class);
}

// Attaching a role to a user:
$user->roles()->attach($roleId);

// Accessing pivot data:
foreach ($user->roles as $role) {
    echo $role->pivot->created_at;
}

Note

Touching Timestamps

When a child model is updated (like a Comment), you may want to update the updated_at timestamp of the parent (Post). You can do this by adding the relationship name to a $touches property on the child model.

Warning: Relationship Method vs Property

If you call $user->posts, you get a Collection of posts. If you call $user->posts(), you get a Query Builder instance, allowing you to filter further: $user->posts()->where('active', 1)->get();.

Collections

All multi-result sets returned by Eloquent are instances of the Illuminate\Database\Eloquent\Collection object. Eloquent collections extend the base Laravel Support Collection, which means they inherit dozens of powerful methods used to fluently wrap and manipulate arrays of data.

Unlike standard PHP arrays, collections allow you to chain operations like filtering, mapping, and reducing in a readable, functional style without needing manual foreach loops.

Common Collection Methods

Laravel collections offer over 100 methods. Below are the most frequently used operations for handling database results.

Method Description Example
filter() Filters the collection using a callback. $users->filter(fn($u) => $u->active);
map() Iterates and modifies each item. $users->map(fn($u) => strtoupper($u->name));
pluck() Retrieves all values for a given key. $users->pluck('email'); // Array of emails
contains() Checks if a specific item exists. $users->contains('role', 'admin');
first() Returns the first item passing a truth test. $users->first(fn($u) => $u->points > 100);
each() Iterates over items (useful for actions). $users->each(fn($u) => $u->sendEmail());
sortBy() Sorts the collection by a given key. $users->sortByDesc('created_at');

Eloquent vs. Support Collections

While Eloquent collections inherit from the base Support collection, they include specialized methods specifically for database models.

  • load(): Eager loads relationships for all models in the collection after they have already been retrieved.
  • find($id): Returns the model that has a primary key matching the given ID.
  • modelKeys(): Returns an array of all primary keys for the models in the collection.
  • toQuery(): Converts the collection back into a Query Builder instance, allowing you to perform database-level updates on the specific set of models.

Higher Order Messaging

Collections provide "shortcuts" for common actions. Instead of writing a full closure, you can call methods directly on the collection instance as if they were properties.

// Instead of this:
$users->each(function ($user) {
    $user->archive();
});

// Use Higher Order Messaging:
$users->each->archive();

This also works with filtering:

// Returns only the users where $user->is_active is true
$activeUsers = $users->filter->is_active;

Lazy Collections

When dealing with tens of thousands of Eloquent records, a standard collection might exhaust your PHP memory limit because it loads all records into RAM at once. Lazy Collections use PHP generators to keep memory usage low by loading only one model at a time as you iterate through them.

use App\Models\User;

// Only one user is kept in memory at a time
foreach (User::cursor() as $user) {
    // Process user...
}

Note

Most collection methods return a new collection instance. They do not modify the original collection. This allows you to "chain" multiple transformations together while keeping the original data intact if needed.

Warning: Collection vs. Database Query

Be careful not to perform "Collection Filtering" when you should be doing "Database Filtering."

  • Bad: User::all()->where('active', 1) (Loads all users into memory, then filters).
  • Good: User::where('active', 1)->get() (Filters at the database level, loading only what is needed).

Mutators & Casting

Mutators, accessors, and attribute casting allow you to transform Eloquent attribute values when you retrieve or set them on model instances. This is essential for ensuring data consistency—such as hashing passwords automatically, formatting dates, or converting JSON strings into PHP arrays.

Accessors & Mutators

In modern Laravel, accessors and mutators are defined by creating a single method on your model that returns an Illuminate\Database\Eloquent\Casts\Attribute instance.

Feature Method Purpose
Accessor get: Formats a value when it is retrieved from the database.
Mutator set: Formats a value before it is stored in the database.

Example: Formatting a User's Name

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Get the user's first name.
     */
    protected function firstName(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucfirst($value), // Accessor
            set: fn (string $value) => strtolower($value), // Mutator
        );
    }
}

Attribute Casting

Attribute casting provides functionality similar to accessors and mutators without requiring you to define extra methods. Instead, you define a casts method (or property) that maps a column to a specific data type.

Cast Type Description
integer / boolean Converts the database value to a primitive PHP type.
decimal:<digits> Converts to a string with fixed decimal places (safe for money).
datetime / date Converts the value to a Carbon instance.
json / array Automatically json_encode on save and json_decode on retrieval.
encrypted Encrypts the value in the database using Laravel's encrypter.
hashed Automatically hashes the value (perfect for passwords).

Example Usage:

protected function casts(): array
{
    return [
        'is_admin' => 'boolean',
        'options' => 'array',
        'published_at' => 'datetime',
        'password' => 'hashed',
    ];
}

Custom Casts

If Laravel’s built-in casts don't meet your needs, you can define your own cast class. This is useful for complex value objects, such as a Address object or a specific Currency class.

// Creating a custom cast
php artisan make:cast AddressCast

// Applying it in the Model
protected function casts(): array
{
    return [
        'address' => AddressCast::class,
    ];
}

Date Casting & Serialization

By default, Eloquent will cast the created_at and updated_at columns to Carbon instances. You can customize the format of these dates globally or per-attribute. When a model is converted to JSON (e.g., in an API response), the dates will be serialized to an ISO-8601 string.

protected function casts(): array
{
    return [
        'birthday' => 'date:Y-m-d', // Serialize specifically as 1990-01-01
    ];
}

Note

Appending Values

Sometimes you may want to add attributes that do not have a corresponding column in your database. You can do this by defining an Accessor and then adding the attribute name to the $appends property on your model. This ensures the value is included when the model is converted to an array or JSON.

Warning: Hashing Passwords

Never use a simple accessor to "check" a password. Always use the hashed cast for password fields. This ensures that even if you forget to manually call Hash::make() in your controller, the password will be safely encrypted before hitting the database.

API Resources

When building an API, you often need a transformation layer that sits between your Eloquent models and the JSON responses served to your application's users. Laravel's API Resources allow you to expressively and easily transform your models and model collections into JSON, ensuring your API output remains consistent even if your database schema changes.

Creating Resources

You can generate a resource class using the make:resource Artisan command. Typically, resources are placed in the app/Http/Resources directory.

Command Purpose
php artisan make:resource UserResource Creates a resource for a single model.
php artisan make:resource UserCollection Creates a resource for a collection of models (wraps pagination/metadata).

Data Transformation

Each resource class contains a toArray method which returns the array of attributes that should be converted to JSON when the resource is returned from a route or controller.

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at->format('Y-m-d'),
            // Conditional attributes
            'is_admin' => $this->when($request->user()?->isAdmin(), true),
        ];
    }
}

Handling Relationships

One of the most powerful features of API Resources is the ability to include relationships only if they have already been loaded by the model. This prevents the "N+1" query problem and ensures you don't accidentally leak data that wasn't requested.

public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'title' => $this->title,
        // Only include posts if they are eager-loaded
        'posts' => PostResource::collection($this->whenLoaded('posts')),
    ];
}

Resource Collections & Pagination

When returning a collection of resources or a paginated response, Laravel automatically wraps the data in a data key and includes relevant pagination information (like meta and links).

Method Usage Result
new UserResource($user) Single Model { "data": { ... } }
UserResource::collection($users) List/Collection { "data": [ {...}, {...} ] }
$users->paginate(10) Paginated List Adds links and meta keys to the JSON.

Top-Level Metadata

Sometimes you need to include extra information in your API response that isn't directly tied to the model, such as an API version or a success message.

// In the Controller
return (new UserResource($user))
    ->additional(['meta' => [
        'version' => '1.0.0',
    ]]);

Note

The $this Variable

Inside the toArray method of a resource, you can access the model's properties directly using $this. This is because a Resource class proxies property and method access down to the underlying model.

Warning: Avoiding toArray() on Models

While Eloquent models have a built-in toArray() method, using API Resources is highly recommended for production APIs. This prevents "Internal Schema Leakage"—where changing a database column name accidentally breaks your public API for third-party consumers.

Serialization (JSON & Arrays)

When building APIs or JavaScript-heavy frontends, you often need to convert your Eloquent models and collections into arrays or JSON. Serialization is the process of transforming a complex data structure (like an Eloquent Model object) into a format that can be easily stored or transmitted (like a JSON string).

Converting Models to Arrays & JSON

Eloquent provides two primary methods for basic serialization. These methods are recursive, meaning all nested relationships (that have been eager loaded) will also be serialized.

Method Output Use Case
toArray() PHP array Passing data to a Blade view or internal processing.
toJson() JSON string Manual API responses or storing data in a text field.

Example:

$user = User::with('posts')->first();

// Convert to array
$array = $user->toArray();

// Convert to JSON
$json = $user->toJson();
// Or simply cast to string
$jsonString = (string) $user;

Hiding Attributes from JSON

You often have sensitive data (like passwords, API tokens, or internal IDs) that should never be included in your serialized output. You can define these using the $hidden property on your model. Conversely, you can use $visible to act as an "allow-list."

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Attributes that should be hidden for serialization.
     */
    protected $hidden = ['password', 'remember_token'];

    /**
     * Attributes that should be visible (ignores everything else).
     * protected $visible = ['first_name', 'last_name'];
     */
}

Appending Values to JSON

Sometimes you need to include an attribute that doesn't exist as a column in your database but is calculated by an Accessor. To include these in your JSON output, add the attribute name to the $appends property.

class User extends Model
{
    protected $appends = ['is_admin'];

    protected function isAdmin(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->type === 'admin',
        );
    }
}

Date Serialization

Laravel serializes dates into an ISO-8601 string format (2026-02-26T20:19:38.000000Z) by default. You can customize this format per model by overriding the serializeDate method.

/**
 * Prepare a date for array / JSON serialization.
 */
protected function serializeDate(\DateTimeInterface $date): string
{
    return $date->format('Y-m-d H:i:s');
}

Dynamic Hiding/Showing

On specific occasions, you might want to hide or show attributes "on the fly" for a single request without changing the global model configuration.

Method Effect
makeVisible('attr') Temporarily shows a hidden attribute.
makeHidden('attr') Temporarily hides a visible attribute.
setVisible(['a', 'b']) Forces only these specific attributes to show.
return $user->makeVisible('email_verified_at')->toArray();

Note

Automatic Serialization

When you return an Eloquent model or collection directly from a Route or Controller, Laravel automatically calls toJson() and sets the Content-Type header to application/json. You don't need to manually encode your response.

Warning: Circular References

Be careful when eager loading reciprocal relationships (e.g., a User has many Posts, and a Post belongs to a User). If you serialize both directions simultaneously without hiding one of the foreign keys, you may create an infinite loop that crashes your PHP process.

Eloquent Factories

When testing your application or seeding your database, you often need to generate fake data that looks and behaves like real records. Eloquent Factories provide a convenient way to define a "blueprint" for your models. Instead of manually specifying every column, you can use factories to generate hundreds of realistic records with a single line of code.Laravel uses the Faker PHP library to generate random names, emails, addresses, and more.

Creating and Defining Factories

Factories are stored in the database/factories directory. You can generate a factory for a specific model using the make:factory Artisan command.

Command Purpose
php artisan make:factory PostFactory Creates a factory for the Post model.
php artisan make:model Post -f Creates a Model and its Factory simultaneously.
php artisan make:model Post -a Creates Model, Migration, Factory, Seeder, and Controller.

Example Factory Definition:

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
        ];
    }
}

Using Factories

Once a factory is defined, you can use the static factory() method provided by the HasFactory trait on your Eloquent models.

Creation Methods

Method Action Result
make() Creates instances in memory. Returns a Model object (not saved to DB).
create() Creates and persists to DB. Returns a Model object (saved to DB).
count(n) Specifies the number of items. Returns a Collection of models.

Example Usage:

// Create a single user in the database
$user = User::factory()->create();

// Create 50 users with a specific attribute override
$users = User::factory()->count(50)->create([
    'admin' => true,
]);

Factory States

States allow you to define discrete variations of your model. For example, a User might be an "admin" or "suspended." Instead of manually passing these attributes every time, you can define a state method.

// UserFactory.php
public function suspended(): static
{
    return $this->state(fn (array $attributes) => [
        'account_status' => 'suspended',
    ]);
}

// Usage
$suspendedUser = User::factory()->suspended()->create();

Factory Relationships

Factories make it easy to generate related models. You can define relationships using methods like has() or for() .

// Create a user with 3 related posts
$user = User::factory()
            ->has(Post::factory()->count(3))
            ->create();

// Create a post that "belongs to" a specific user
$post = Post::factory()
            ->for(User::factory()->state(['name' => 'John Doe']))
            ->create();

Sequences

If you want to toggle a value for each created model (e.g., alternating between "Male" and "Female" or "Active" and "Inactive"), you can use Sequences.

use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
            ->count(10)
            ->state(new Sequence(
                ['role' => 'admin'],
                ['role' => 'editor'],
            ))
            ->create();

Note

The fake() Helper

In Laravel 11, the fake() helper is the preferred way to access the Faker library. It is localized based on your app.faker_locale setting in config/app.php (e.g., en_US, fr_FR).

Warning: Database Speed in Tests

While create() is convenient, it hits the database. In unit tests where you only need to test model logic (and not database persistence), use make(). It is significantly faster because it avoids the overhead of SQL INSERT statements.

Security Last updated: Feb. 28, 2026, 10:09 p.m.

Security in Laravel is "opt-out" rather than "opt-in," meaning the framework protects you from common vulnerabilities like CSRF (Cross-Site Request Forgery) and XSS (Cross-Site Scripting) by default. This section covers the fundamental concepts of Authentication (identifying who a user is) and Authorization (determining what they are allowed to do) using Gates and Policies.

Laravel also provides robust Encryption and Hashing services. It ensures that sensitive data, like user passwords, are never stored in plain text and are hashed using modern, slow algorithms like Bcrypt or Argon2. By centralizing these security concerns, Laravel allows developers to focus on features without worrying about the underlying cryptographic implementation.

Authentication (Guards & Providers)

Laravel’s authentication system is designed to be "pluggable." At its core, the system is comprised of two main components: Guards and Providers. These components work together to define how users are authenticated and where their data is retrieved from for every request.

The Authentication Architecture

Understanding the relationship between Guards and Providers is essential for customizing how users log into your application (e.g., using a traditional session vs. an API token).

Component Responsibility Description
Guard "How" Defines how users are authenticated for each request (e.g., session guard for web, token guard for APIs).
Provider "Where" Defines how users are retrieved from your persistent storage (e.g., eloquent provider for DB models, database provider for raw tables).

Configuration

Authentication configuration is found in config/auth.php. By default, Laravel is configured with a web guard and an eloquent user provider.

// config/auth.php

'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],
],

Authenticating Users

While Laravel offers starter kits (like Breeze or Jetstream) to handle this automatically, you can interact with the authentication system manually using the Auth facade.

Attempting Authentication

The attempt method is typically used to handle authentication attempts from your application's "login" form.

use Illuminate\Support\Facades\Auth;

if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
    // Authentication was successful...
    return redirect()->intended('dashboard');
}

Checking if a User is Authenticated

To determine if the user is already logged into your application, you may use the check method:

if (Auth::check()) {
    // The user is logged in...
}

Accessing the Authenticated User

Once a user is authenticated, you can access the user model instance via the Auth facade or the Request instance.

Method Syntax
Via Facade Auth::user()
Via Request $request->user()
Get User ID Auth::id()

Protecting Routes

The auth middleware is used to allow only authenticated users to access a given route. If the user is not authenticated, they will be redirected to the login route.

// Protecting a single route
Route::get('/profile', function () {
    // Only authenticated users may access...
})->middleware('auth');

// Specifying a Guard
Route::get('/api/user', function () {
    // Use the 'api' guard instead of the default 'web'
})->middleware('auth:api');

Note

If you would like to provide "remember me" functionality in your application, you may pass a boolean value as the second argument to the attempt method. This will keep the user authenticated indefinitely, or until they manually logout.

Warning: Password Hashing

Laravel's Auth::attempt() method automatically hashes the password provided in the array and compares it against the hashed password in the database. You should never hash the password yourself before passing it to attempt(), or the comparison will fail.

Authorization (Gates & Policies)

While Authentication identifies who a user is, Authorization determines what they are allowed to do. Laravel provides two primary ways of authorizing actions: Gates and Policies.

Think of Gates as simple "closure-based" checks (like a bouncer at a door), while Policies are organized around specific Eloquent models (like a legal document defining ownership and permissions).

Gates

Gates are most useful for actions that are not related to a specific model, such as opening an administrative dashboard or viewing system logs. They are typically defined in the boot method of the App\ServiceProvider.

Defining a Gate:

use Illuminate\Support\Facades\Gate;
use App\Models\User;

public function boot(): void
{
    Gate::define('access-admin-panel', function (User $user) {
        return $user->is_admin;
    });
}

Using a Gate:

if (Gate::allows('access-admin-panel')) {
    // The user can access the admin panel...
}

if (Gate::denies('access-admin-panel')) {
    abort(403);
}

Policies

Policies are classes that organize authorization logic around a particular model or resource. For example, if your application has a Post model, you might have a PostPolicy to authorize actions such as creating or updating posts.

Action Policy Method Purpose
View viewAny, view Can the user see the list or a specific record?
Create create Can the user add a new record?
Update update Can the user edit an existing record?
Delete delete Can the user remove a record?

Creating a Policy:

php artisan make:policy PostPolicy --model=Post

Example Policy Logic:

public function update(User $user, Post $post): bool
{
    // Only the author can update the post
    return $user->id === $post->user_id;
}

Authorizing Actions

Laravel provides several ways to check permissions using policies.

Via the User Model

The User model includes two helpful methods for checking permissions: can and cannot.

if ($user->can('update', $post)) {
    // ...
}

Via Controller Helpers

In your controllers, you can use the authorize method. If the action is not authorized, a 403 Forbidden response is automatically sent.

public function update(Request $request, Post $post)
{
    $this->authorize('update', $post);
    // Continue update logic...
}

Via Blade Directives

To conditionally show parts of your UI, use the @can and @cannot directives.

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

The "Super Admin" Bypass

For administrative users, you might want to grant all permissions regardless of what the Gates or Policies say. You can use the Gate::before method to intercept all authorization checks.

Gate::before(function ($user, $ability) {
    if ($user->is_super_admin) {
        return true;
    }
});

Note

Policy Auto-Discovery

Laravel automatically discovers policies as long as they are in the app/Policies directory and the name matches the model (e.g., Post -> PostPolicy). You don't need to manually register them in most cases.

Warning

Middleware Placement

When using authorization in middleware (e.g., ->middleware('can:update,post')), ensure it comes after the auth middleware. You cannot authorize a user if the system hasn't identified who they are yet.

Email Verification

In many web applications, it is necessary to verify that a user actually owns the email address they used to register. Laravel provides a built-in, painless way to send and verify email verification requests. This system ensures that users have a valid email before they can access specific routes or features.

Model Preparation

To get started, your User model must implement the MustVerifyEmail contract. This interface tells Laravel that this model requires verification.

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;
    // ...
}

By default, the users table migration already includes an email_verified_at column. If yours does not, ensure it is added: $table->timestamp('email_verified_at')->nullable();

Routing & Controllers

To handle verification, you need to define three specific routes. While starter kits like Breeze handle this for you, the manual logic involves:

Route Purpose Description
Notice Displaying a "Verify Your Email" page. Shown to users who haven't verified yet.
Handler Processing the clicked link in the email. Marks the user as verified in the database.
Resend Allowing the user to request a new link. Throttled to prevent spam.

Protecting Routes

To ensure only verified users can access a specific route, Laravel provides the verified middleware. This middleware is typically applied to dashboard or profile routes.

Route::get('/profile', function () {
    // Only verified users may access this...
})->middleware(['auth', 'verified']);

If an unverified user attempts to access these routes, they will automatically be redirected to the email verification notice page.

Event Handling

When a user is successfully verified, Laravel dispatches an Illuminate\Auth\Events\Verified event. You can listen for this event to perform actions like:

  • Sending a "Welcome" email.
  • Assigning a default role.
  • Updating a CRM.

Customizing the Verification Email

The verification email is sent via a Notification. If you want to change the text, the "Verify" button color, or the overall layout, you can override the toMail method by calling VerifyEmail::toMailUsing() in your AppServiceProvider.

use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Notifications\Messages\MailMessage;

public function boot(): void
{
    VerifyEmail::toMailUsing(function (object $notifiable, string $url) {
        return (new MailMessage)
            ->subject('Verify Your Account')
            ->line('Click the button below to join our community.')
            ->action('Verify Email Address', $url);
    });
}

Note

Manual Verification

If you need to verify a user manually (e.g., via an admin panel), you can call the markEmailAsVerified() method on the user model instance. This will update the email_verified_at timestamp automatically.

Warning

Security of Links

Laravel verification links are "signed" URLs. They contain a cryptographic hash that prevents users from tampering with the user ID or expiration time in the URL. Never modify the signature logic unless you are an advanced user, as it protects your app from unauthorized account activations.

Encryption

Laravel provides a simple, convenient interface for encrypting and decrypting data via the OpenSSL library using AES-256 and AES-128 encryption. All of Laravel's encrypted values are signed using a Message Authentication Code (MAC) so that their underlying value cannot be modified or tampered with once encrypted.

The Application Key

Before you can use Laravel's encrypter, you must set the APP_KEY in your .env file. This key is used to "salt" your encrypted data. If this key is not set, your encrypted data will be insecure.

Component Setting in .env Requirement
App Key APP_KEY Must be 32 characters (random string).
Cipher APP_CIPHER Default is AES-256-CBC.

Warning

If you change your APP_KEY, all data currently encrypted by Laravel will become unreadable. Never change this key in a production environment if you have encrypted data in your database.

Encrypting & Decrypting Values

You may encrypt values using the Crypt facade. These methods are designed to handle strings, but they can also handle serialized objects and arrays.

Basic Usage

use Illuminate\Support\Facades\Crypt;

// Encrypting a value
$encrypted = Crypt::encryptString('Hello world.');

// Decrypting a value
try {
    $decrypted = Crypt::decryptString($encrypted);
} catch (DecryptException $e) {
    // The payload was invalid or the MAC was tampered with
}

Comparison: Encryption vs. Hashing

It is common to confuse encryption with hashing (which we covered in previous sections). They serve very different security purposes.

Feature Encryption ( Crypt ) Hashing ( Hash )
Type Two-way (Reversible). One-way (Irreversible).
Purpose To store data you need to read later (e.g., API keys). To verify data (e.g., passwords).
Recovery Can be decrypted with the APP_KEY. Cannot be "decrypted."

Eloquent Attribute Encryption

As discussed in the Mutators & Casting section, Laravel makes it incredibly easy to encrypt specific database columns automatically. This ensures that even if your database is compromised, sensitive fields remain unreadable without the APP_KEY.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The attributes that should be cast.
     */
    protected function casts(): array
    {
        return [
            'secret_token' => 'encrypted',
            'bank_account_data' => 'encrypted:json', // Encrypts an array as JSON
        ];
    }
}

Encryption for Files and Streams

While Crypt::encryptString is for short strings, you should not use it for massive files, as it loads the entire content into memory. For large files, it is better to use dedicated streaming encryption libraries or handle the encryption in chunks.

Note

Handling Decryption Failures

If the decryptString method fails (due to a changed APP_KEY or a tampered payload), it throws an Illuminate\Contracts\Encryption\DecryptException. Always wrap your decryption logic in a try-catch block when dealing with user-provided or external data.

Hashing

Laravel provides a secure, built-in interface for hashing data, primarily used for storing user passwords. Unlike Encryption, hashing is a one-way cryptographic operation. Once a value is hashed, it cannot be "decrypted" back to its original form. Instead, you verify the original value by hashing it again and comparing the results.

Laravel uses the Bcrypt and Argon2 hashing algorithms by default, ensuring your application stays compliant with modern security standards.

Configuration

The hashing configuration is managed in config/hashing.php. Here, you can specify which driver to use and customize the "work factor" (the amount of processing power required to generate a hash).

Algorithm Driver Recommendation
Bcrypt bcrypt The default and most common choice for web apps.
Argon2i argon Secure, but requires the PHP zodium extension.
Argon2id argon2id Most secure; resistant to GPU-based side-channel attacks.

Basic Usage

You may hash a string by calling the make method on the Hash facade.

Hashing a Password

use Illuminate\Support\Facades\Hash;

$hashedPassword = Hash::make($request->newPassword);

Verifying a Password

The check method allows you to verify that a given plain-text string matches a given hash. This is exactly what Laravel does internally during the authentication process.

if (Hash::check('plain-text-password', $hashedPassword)) {
    // The passwords match...
}

Checking If a Password Needs Re-hashing

As hardware improves, you may want to increase the "work factor" of your hashes. The needsRehash method allows you to determine if the hash was created using a work factor that is no longer considered secure.

if (Hash::needsRehash($hashedPassword)) {
    $hashedPassword = Hash::make('plain-text-password');
}

Automatic Hashing (Eloquent)

As of Laravel 10+, you can use the hashed cast in your Eloquent models. This ensures that any value assigned to that attribute is automatically hashed before being saved to the database.

protected function casts(): array
{
    return [
        'password' => 'hashed',
    ];
}

// In your controller:
User::create([
    'name' => 'John',
    'email' => 'john@example.com',
    'password' => 'secret123', // Automatically hashed by Eloquent
]);

Encryption vs. Hashing Summary

Feature Encryption ( Crypt ) Hashing ( Hash )
Nature Two-way (Reversible). One-way (Irreversible).
Best For API Keys, sensitive PII. Passwords, PINs.
Database Storage Ciphertext (readable with key). Hash digest (never readable).

Note

Secure by Default

Laravel uses a "salt" for every hash automatically. This means that if two users have the exact same password, their hashes stored in the database will still be completely different, protecting your app against "Rainbow Table" attacks.

Warning

Never Store Plain Text

Storing passwords in plain text is a critical security failure. Even if you use a "custom" reversible encryption, a database breach would expose all user passwords. Always use the Hash facade or the hashed model cast.

Password Reset

Laravel provides a robust, built-in system for handling user password resets. This includes generating secure tokens, sending reset links via email, and updating the password in the database. Like authentication, this system is designed to work out of the box with minimal configuration, especially when using Laravel starter kits.

Core Components

The password reset process relies on several moving parts to ensure security and a smooth user experience.

Component Responsibility
Password Facade The primary interface for sending reset links and resetting passwords.
Broker Manages the logic of creating tokens and retrieving users (configured in config/auth.php).
Tokens Table A database table (password_reset_tokens) that stores hashes of reset tokens.
Notification The email sent to the user containing the unique reset link.

Database Configuration

Before using the password reset system, ensure the password_reset_tokens table exists. This is included in a default Laravel installation.

Schema::create('password_reset_tokens', function (Blueprint $table) {
    $table->string('email')->primary();
    $table->string('token');
    $table->timestamp('created_at')->nullable();
});

Sending the Reset Link

To send a password reset link, use the sendResetLink method on the Password facade. This method expects an array of data that includes the user's email address.

use Illuminate\Support\Facades\Password;

$status = Password::sendResetLink(
    $request->only('email')
);

return $status === Password::RESET_LINK_SENT
    ? back()->with(['status' => __($status)])
    : back()->withErrors(['email' => __($status)]);

Resetting the Password

When the user clicks the link in their email, they are directed to a form where they enter their new password. You then use the reset method to validate the token and update the user.

use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;

$status = Password::reset(
    $request->only('email', 'password', 'password_confirmation', 'token'),
    function ($user, $password) {
        $user->forceFill([
            'password' => Hash::make($password)
        ])->setRememberToken(Str::random(60));

        $user->save();
    }
);

Password Reset Security

Laravel implements several security layers to prevent abuse of the reset system:

  • Token Expiration: By default, tokens expire after 60 minutes (configurable in config/auth.php).
  • Token Hashing: Tokens are never stored in plain text in your database; only their hashes are stored.
  • Throttling: Sending reset emails is automatically throttled to prevent spamming a user's inbox.
  • Single-Use: Once a password is successfully reset, the token is immediately deleted from the database.

Note

Customizing the Email

To customize the reset email, you can override the sendPasswordResetNotification method on your User model. This allows you to use a custom Notification class with your own branding and text.

Warning

The Token Payload

The token sent in the email is a plain-text string, while the version in the password_reset_tokens table is hashed. If you try to manually query the database for a token using the plain-text string from the URL, the match will fail.

Advanced Features Last updated: Feb. 28, 2026, 10:09 p.m.

As applications grow, they require more than just simple CRUD operations. "Digging Deeper" covers the Asynchronous Ecosystem, including Queues for background processing and the Task Scheduler for cron jobs. This allows you to offload time-consuming tasks (like sending emails or generating reports) so that the user doesn't experience any lag in the browser.

This section also introduces Events and Listeners, which allow for a decoupled architecture. For example, when an Order is placed, multiple independent listeners can trigger (e.g., one to send an email, one to update inventory, and one to notify a Slack channel) without the main controller ever knowing they exist. This "observer" pattern is key to maintaining clean, manageable codebases as they scale.

Artisan Console Custom Commands

Artisan is the command-line interface included with Laravel. While you use it daily to migrate databases and make controllers, its true power lies in the ability to create custom commands. These are perfect for automating repetitive tasks, such as clearing old logs, syncing external APIs, or generating complex reports.

Creating a Command

To create a new command, use the make:command Artisan command. This will generate a new class in the app/Console/Commands directory.

Command Action
php artisan make:command SendEmails Generates a basic command class.
php artisan list Shows all available commands, including your new one.
php artisan help [command] Displays the description and arguments for a specific command.

Command Structure

A command class consists of two main parts: the signature (how you call the command) and the handle method (what the command actually does).

namespace App\Console\Commands;

use Illuminate\Console\Command;

class SendEmails extends Command
{
    // The name and signature of the console command
    protected $signature = 'mail:send {user} {--queue}';

    // The console command description
    protected $description = 'Send an email to a specific user';

    // Execute the console command
    public function handle()
    {
        $userId = $this->argument('user');
        $useQueue = $this->option('queue');

        $this->info("Sending mail to user: {$userId}...");
        
        // Logic here...

        $this->success("Mail sent successfully!");
    }
}

Arguments & Options

Signatures allow you to define what data your command needs.

Type Syntax Description Example
Argument {user} Required input data. php artisan mail:send 1
Optional Arg {user?} Input that can be omitted. php artisan mail:send
Option {--queue} A flag (true/false). php artisan mail:send 1 --queue
Value Option {--id=} An option that requires a value. php artisan mail:send --id=5

I/O: Interacting with the User

Artisan provides several methods to make your command-line output look professional and interactive.

  • Outputting Text: Use $this->info(), $this->error(), $this->warn(), or $this->line().
  • Progress Bars: Perfect for long-running loops.
  • $bar = $this->output->createProgressBar(count($users));
    foreach ($users as $user) {
        // process...
        $bar->advance();
    }
    $bar->finish();
  • User Input: Ask for confirmation or secrets.
  • if ($this->confirm('Do you wish to continue?')) {
        $password = $this->secret('Enter the API password');
    }

Running Commands Programmatically

You don't always have to run Artisan commands from the terminal. You can trigger them from within your controllers or jobs using the Artisan facade.

use Illuminate\Support\Facades\Artisan;

Route::post('/clear-cache', function () {
    Artisan::call('cache:clear');
    return "Cache Cleared!";
});

Note

Automatic Registration

In modern Laravel versions, any command stored in app/Console/Commands is automatically registered by the framework. You do not need to manually add them to a list.

Warning

Long-Running Processes

If a command runs for a very long time, it may hit the PHP max_execution_time. However, when running via CLI, this is typically set to 0 (infinite). Be mindful of memory usage; use chunk() when processing thousands of database records to keep the footprint small.

Broadcasting (WebSockets & Reverb)

Laravel Broadcasting allows you to share the same event names between your server-side Laravel application and your client-side JavaScript application. This enables real-time features like live notifications, chat messages, or updating dashboards without the user needing to refresh the page.

The Broadcasting Flow

When an action occurs on the server, an Event is dispatched. If that event implements the ShouldBroadcast interface, Laravel sends it to a "Broadcasting Driver" (like Reverb, Pusher, or Ably), which then pushes the data to the client via WebSockets.

Driver Description
Laravel Reverb A first-party, high-performance WebSocket server built specifically for Laravel.
Pusher A hosted third-party service; easy to set up but has usage limits.
Ably A hosted alternative to Pusher with advanced scaling features.
Redis / Socket.io A self-hosted option using a Node.js companion server.

Channel Types

Not all data should be public. Laravel uses Channels to control who receives which broadcasts.

Channel Type Access Level Use Case
Public Anyone can listen. Stock tickers, public news feeds.
Private Requires authentication. User notifications, order status updates.
Presence Private + User data. "Who is online" lists, "User is typing..." indicators.

Defining Broadcast Events

To broadcast an event, add the ShouldBroadcast interface to your event class. You must define a broadcastOn method to specify which channel(s) the event should be sent to.

namespace App\Events;

use App\Models\Order;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Broadcasting\PrivateChannel;

class OrderShipmentStatusUpdated implements ShouldBroadcast
{
    public function __construct(public Order $order) {}

    public function broadcastOn(): array
    {
        return [
            new PrivateChannel('orders.' . $this->order->user_id),
        ];
    }
}

Authorizing Channels

For Private and Presence channels, you must define authorization logic in routes/channels.php. This acts like a "Gate" for your WebSockets.

// routes/channels.php

Broadcast::channel('orders.{userId}', function ($user, $userId) {
    return (int) $user->id === (int) $userId;
});

Listening on the Frontend (Laravel Echo)

Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by Laravel.

import Echo from 'laravel-echo';

window.Echo.private(`orders.${userId}`)
    .listen('OrderShipmentStatusUpdated', (e) => {
        console.log('Order status changed to:', e.order.status);
    });

Laravel Reverb

Introduced in 2024, Reverb is the modern standard for Laravel WebSockets. It is written in PHP using the Revolt event loop, making it incredibly fast and capable of handling thousands of simultaneous connections without needing Node.js or a third-party service.

Why use Reverb?

  • First-party: Built by the Laravel team specifically for the framework.
  • Horizontal Scaling: Supports scaling across multiple servers using Redis.
  • No Extra Cost: Self-hosted on your own server, so there are no monthly fees for high usage.

Note

Data Payloads

By default, Laravel will include all public properties of your Event class in the broadcast payload. If you want more control over the data sent to the client, you can define a broadcastWith method in your event class.

Warning

CSRF and WebSockets

Private channels require an AJAX request to authorize the user. Ensure your frontend is correctly sending the X-CSRF-TOKEN or using withCredentials (for Sanctum) so that Laravel can verify the user's session.

Cache

In high-traffic applications, querying a database or performing complex calculations repeatedly can slow down your server. Caching allows you to store the results of these expensive operations in a fast storage system (like RAM) so that subsequent requests can retrieve the data almost instantaneously.

Cache Drivers

Laravel provides a unified API for various caching backends. You can configure your default driver in the config/cache.php file or via the CACHE_STORE environment variable.

Driver Description Best Use Case
file Stores cached items in the filesystem (storage/framework/cache). Local development; simple apps with single servers.
database Stores items in a database table. When you don't want to manage a separate cache server.
redis An in-memory key-value store. Production standard; extremely fast and supports tagging.
memcached A high-performance memory object caching system. Legacy systems or specific high-concurrency needs.
array Stores items in a simple PHP array. Testing; data persists only for the current request.

Basic Usage

You interact with the cache using the Cache facade. It follows a simple key-value store logic.

Storing & Retrieving

use Illuminate\Support\Facades\Cache;

// Store for 60 seconds
Cache::put('key', 'value', $seconds = 60);

// Retrieve (returns null if not found)
$value = Cache::get('key');

// Retrieve with a default value
$value = Cache::get('key', 'default');

The "Remember" Pattern

This is the most common use case: "If it's in the cache, give it to me; otherwise, get it from the DB and save it for next time."

$users = Cache::remember('users.active', $seconds = 3600, function () {
    return User::where('active', 1)->get();
});

Atomic Locks

Atomic locks allow for the manipulation of distributed locks without worrying about "race conditions." For example, if you want to ensure only one server processes a specific task at a time.

$lock = Cache::lock('processing-invoice-1', 10);

if ($lock->get()) {
    // Critical section logic...

    $lock->release();
}

Cache Tags

Note

Cache tags are not supported when using the file, dynamodb, or database cache drivers. They require redis or memcached.

Tags allow you to group related cached items. This makes it easy to clear an entire category of data without affecting the rest of the cache.

// Storing tagged data
Cache::tags(['people', 'artists'])->put('John', $john, $seconds);

// Clearing all "people" tags
Cache::tags('people')->flush();

Cache Best Practices

Practice Description
Granular Keys Use specific keys like user:1:profile rather than one giant users key.
Avoid Over-Caching Don't cache data that changes every few seconds; the overhead of writing to cache might outweigh the benefit.
Fallbacks Always provide a default or a closure in Cache::get to handle cache misses gracefully.
Production Driver Always use Redis or Memcached in production for true performance gains.

Warning

Cache Invalidation

The hardest part of caching is knowing when to delete it. If you cache a user's profile, ensure you use an Eloquent Observer or Listener to clear that specific cache key whenever the user updates their information.

Collections (Laravel Base Collections)

While we previously covered Eloquent Collections, Laravel also provides Base Collections for handling standard PHP arrays. A Collection is a sophisticated wrapper around arrays, providing a fluent, chainable interface for data manipulation. Instead of using messy nested foreach loops and array_ functions, you can transform data using a functional approach.

Creating Collections

You can create a collection using the collect helper or by instantiating the Illuminate\Support\Collection class.

$collection = collect([1, 2, 3, 4, 5]);

// Or from an associative array
$prices = collect(['apple' => 10, 'orange' => 20]);

Essential Transformation Methods

Base collections offer over 100 methods to filter, sort, and transform data. Here are the "Power Players" used in daily development:

Method Action Example
map() Iterates and modifies every item. collect([1, 2])->map(fn($n) => $n * 10); // [10, 20]
filter() Removes items that don't pass a truth test. $col->filter(fn($n) => $n > 5);
reduce() Reduces collection to a single value. $col->reduce(fn($carry, $item) => $carry + $item);
pluck() Retrieves values from a specific key. $users->pluck('email');
flatten() Flattens a multi-dimensional array. collect([[1, 2], [3, 4]])->flatten(); // [1, 2, 3, 4]
chunk() Breaks collection into smaller sizes. $col->chunk(3); // Useful for grid layouts

Chainability & Readability

The true power of collections is method chaining. Each method returns a new collection instance, allowing you to perform complex data surgery in a single, readable block.

$names = collect([' taylor', 'abigail', null, ' jeffrey'])
    ->map(fn($name) => trim($name)) // Remove whitespace
    ->filter()                     // Remove nulls
    ->map(fn($name) => ucfirst($name)) // Capitalize
    ->sort();                      // Alphabetize

// Result: ['Abigail', 'Jeffrey', 'Taylor']

Advanced Comparison & Mathematical Methods

Collections aren't just for loops; they include powerful logic for comparing sets of data and performing math.

  • diff(): Returns items in the original collection that are not present in the given collection.
  • intersect(): Returns items present in both collections.
  • sum(), avg(), min(), max(): Instantly calculate totals or averages.
  • unique(): Removes duplicate items from the collection.

Lazy Collections

Standard collections load the entire array into memory. If you are reading a 500MB log file or thousands of records, your script might crash. Lazy Collections use PHP generators to process one item at a time, keeping memory usage near zero.

use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen('huge_file.csv', 'r');
    while (($line = fgetcsv($handle)) !== false) {
        yield $line;
    }
})->map(fn($row) => $row[0])->each(function ($data) {
    // Process one row at a time
});

Note

Immutability

Collections are immutable. Calling a method like map() or filter() does not change the original collection; it returns a new one. If you need to keep the results, make sure to assign the result to a variable.

Warning

Collection vs. Array

While collections are powerful, they carry a slight performance overhead compared to raw PHP arrays. For small arrays or extremely performance-critical loops inside a high-frequency function, a standard foreach on a native array will be faster.

Context (Request Context)

In a typical web application, "Context" refers to the specific environment and data associated with a single lifecycle of a request. Laravel provides several mechanisms to access and store this data, ensuring that information—like the currently authenticated user, the language preference, or a unique tracking ID—is available throughout the entire execution of your code without having to pass variables manually between every function.

The Request Object

The most immediate form of context is the Illuminate\Http\Request instance. It contains everything about the incoming HTTP call.

Category Method Description
Input $request->input('key') Accesses data from GET, POST, or JSON body.
Authentication $request->user() Retrieves the currently logged-in user instance.
Environment $request->ip() / $request->userAgent() Details about the client's machine and browser.
Route Info $request->route()->getName() Identifies which specific route was triggered.

Context Class (The Context Facade)

Introduced in recent Laravel versions, the Context facade allows you to store and retrieve data that is local to the current request or job. This is particularly useful for logging, as any data added to the Context is automatically appended to every log entry generated during that request.

Storing Contextual Data:

use Illuminate\Support\Facades\Context;

// In a Middleware
Context::add('request_id', (string) Str::uuid());
Context::add('tenant_id', $request->user()->tenant_id);

Retrieving Contextual Data:

$tenantId = Context::get('tenant_id');

Context vs. Sessions

It is important to distinguish between "Request Context" and "User Sessions".

Feature Request Context (Context) Sessions (Session)
Lifespan Single Request. Disappears after response. Persistent. Lasts across many requests.
Storage Memory (RAM). File, Database, or Redis.
Best For Trace IDs, logging metadata, current tenant. Login status, shopping carts, flash messages.

Sharing Context with Background Jobs

One of the most powerful features of Laravel's Context system is its ability to "follow" the execution into the queue. If you dispatch a background job while certain data is in the Context, Laravel can automatically hydrate that context inside the queue worker when the job starts processing.

// In config/context.php, you can define which keys should be hidden or shared
'stack' => [
    'request_id',
    'source_ip',
],

The Service Container as Context

The Service Container itself acts as a context manager. When you bind a class as a "Singleton," it exists for the duration of that request. This is often used for "Repository" or "Service" classes that need to maintain state while the request is being processed.

// Binding a singleton to hold "Request-Specific" settings
$this->app->singleton(Settings::class, function ($app) {
    return new Settings($app['request'])->user();
});

Note

Global Helpers

While the Context facade is the modern way to handle this, many developers still use the request() helper to access context globally without dependency injection. However, using the Context facade is cleaner for data that isn't strictly part of the HTTP specification (like a transaction ID).

Warning

Data Leakage

Because Context is stored in memory, be careful not to store massive objects or sensitive credentials that might accidentally end up in your log files. Always use Context::addHidden() for data that you want to access programmatically but do not want to see in your logs.

Contracts

Laravel Contracts are a set of interfaces that define the core services provided by the framework. For example, the Illuminate\Contracts\Queue\Queue contract defines the methods needed for queueing jobs, while the Illuminate\Contracts\Mail\Mailer contract defines the methods needed for sending emails.

If you type-hint a contract in your class constructor, Laravel's service container will automatically resolve the concrete implementation for you.

Contracts vs. Facades

While Facades provide a "static" way to access services, Contracts provide an explicit way to define your class's dependencies.

Feature Facades Contracts
Syntax Cache::get('key') $this->cache->get('key')
Dependency Implicit (hidden) Explicit (visible in constructor)
Testing Uses "Mock" methods on facade Standard PHPUnit interface mocking
Coupling High (tied to Laravel Facades) Low (tied to a PHP Interface)

Why Use Contracts?

The primary benefits of using Contracts are Loose Coupling and Simplicity.

  • Loose Coupling: Your code is not tied to a specific library. If you want to swap the cache implementation from Redis to Memcached, your application code remains untouched because it only cares about the Cache interface.
  • Simplicity: When all of Laravel's services are defined within tidy interfaces, it is very easy to determine what a particular service does.

How to Use Contracts

To use a contract, you simply type-hint the interface in the constructor of your class. Laravel's Service Container will look at the interface and inject the correct underlying class.

namespace App\Orders;

use App\Models\Order;
use Illuminate\Contracts\Cache\Repository as Cache;

class OrderProcessor
{
    /**
     * Create a new processor instance.
     */
    public function __construct(
        protected Cache $cache, // The Contract
    ) {}

    public function process(Order $order): void
    {
        if ($this->cache->has('order.'.$order->id)) {
            return;
        }

        // Processing logic...
    }
}

Commonly Used Contracts

Laravel provides dozens of contracts. Below are the ones you will encounter most frequently:

Contract Purpose
Illuminate\Contracts\Auth\Authenticatable Defines what a "User" model must look like.
Illuminate\Contracts\Cache\Repository Provides methods for interacting with the cache.
Illuminate\Contracts\Config\Repository Provides methods for accessing configuration values.
Illuminate\Contracts\Filesystem\Filesystem Defines methods for reading/writing files (Local, S3).
Illuminate\Contracts\Queue\ShouldQueue An interface that tells Laravel a job should be backgrounded.

Note

When to use Contracts

Most developers use Facades for speed and simplicity in controllers. However, if you are building a Package or a highly complex enterprise application where you might want to swap out core components, Contracts are the superior choice.

Warning

Choosing the Right Interface

Always ensure you are importing the interface from the Illuminate\Contracts namespace. Many services have a "concrete" class with a similar name in a different namespace; type-hinting the concrete class instead of the interface defeats the purpose of using Contracts.

Events & Listeners

Laravel's Events and Listeners provide a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application. This serves as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other.

For example, when an order is shipped, you may want to send a Slack notification, update the inventory, and email the customer. Instead of bloating your OrderController, you can simply dispatch an OrderShipped event.

The Workflow

The event system follows a straightforward "Announcement and Reaction" pattern.

Component Responsibility
Event A "Plain Old PHP Object" (POPO) that contains the data related to the event (e.g., the Order model).
Listener A class that performs the actual logic when the event is triggered (e.g., SendShipmentNotification).
Dispatcher The mechanism that notifies all listeners that an event has occurred.

Creating Events & Listeners

You can generate these classes quickly using Artisan:

  • php artisan make:event OrderShipped
  • php artisan make:listener SendShipmentNotification --event=OrderShipped

Defining the Event

An event class typically just accepts a model instance via its constructor:

namespace App\Events;

use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderShipped
{
    use Dispatchable, SerializesModels;

    public function __construct(public Order $order) {}
}

Registering Events

In modern Laravel versions, you don't always need to manually register events in a Service Provider; the framework can auto-discover them. However, you can still manually map them in the App\Providers\AppServiceProvider:

use App\Events\OrderShipped;
use App\Listeners\SendShipmentNotification;
use Illuminate\Support\Facades\Event;

/**
 * Register any events for your application.
 */
public function boot(): void
{
    Event::listen(
        OrderShipped::class,
        SendShipmentNotification::class,
    );
}

Queued Listeners

If a listener is going to perform a slow task (like sending an email or calling a 3rd-party API), you can tell Laravel to handle it in the background by implementing the ShouldQueue interface.

namespace App\Listeners;

use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendShipmentNotification implements ShouldQueue
{
    public function handle(OrderShipped $event): void
    {
        // Access the order via $event->order...
        // This runs in the background!
    }
}

Dispatching Events

To trigger an event, use the dispatch static method on the event class or the event() helper.

use App\Events\OrderShipped;

public function shipOrder($orderId)
{
    $order = Order::findOrFail($orderId);

    // Logic to ship the order...

    OrderShipped::dispatch($order);
}

Summary of Benefits

Benefit Description
Clean Controllers Keeps business logic out of your HTTP layer.
Extensibility Easy to add new features (e.g., a new "Rewards" listener) without touching existing code.
Asynchronous Queued listeners ensure the user doesn't wait for slow background tasks.

Note

Event Discovery

If you prefer not to register events manually, ensure your listener methods are type-hinted with the event they handle. Laravel will scan your Listeners directory and map them automatically.

Warning

SerializesModels

Always use the SerializesModels trait on your Event classes. If the event is queued, Laravel will only store the Model's ID in the queue. When the listener runs, it will automatically re-fetch the fresh model from the database, preventing "stale data" issues.

File Storage & Filesystem

Laravel provides a powerful filesystem abstraction thanks to the Flysystem PHP package. This allows you to interact with local files, Amazon S3, and SFTP using the same simple API. You can switch your storage backend in your configuration without changing a single line of application code.

Configuration & Disks

The filesystem configuration is located at config/filesystems.php. Inside this file, you define "disks." A disk represents a particular storage driver and location.

Disk Driver Description
local local Private files stored in storage/app. Not accessible via URL.
public local Publicly accessible files (after running storage:link).
s3 s3 Amazon S3 cloud storage. Requires the S3 SDK package.

Storing Files

You can use the Storage facade to store files. Laravel automatically handles generating unique file names if you don't provide one.

Basic Uploads

use Illuminate\Support\Facades\Storage;

// Store a file with an automatically generated name
$path = Storage::putFile('avatars', $request->file('avatar'));

// Store a file with a specific name
Storage::put('photos/1.jpg', $contents);

Automatic Stream Uploads

When working with large files, use putFile or putFileAs. Laravel will automatically stream the file to your storage (S3 or Local) to keep memory usage low.

Retrieving Files

Task Method
Get Content $content = Storage::get('file.jpg');
Download return Storage::download('file.jpg');
File URL $url = Storage::url('file.jpg');
Check Existence if (Storage::exists('file.jpg')) { ... }

The Public Disk & Symbolic Links

Files stored in storage/app/public are not accessible to the web by default. To make them accessible, you must create a symbolic link from public/storage to storage/app/public.

Command:

php artisan storage:link

Once linked, you can generate a URL to the file in your Blade templates:

<img src="{{ asset('storage/avatars/user.jpg') }}">

File Metadata & Deletion

Laravel makes it easy to manage file properties and cleanup.

// Get file size in bytes
$size = Storage::size('file.jpg');

// Get last modified timestamp
$time = Storage::lastModified('file.jpg');

// Delete a single file
Storage::delete('file.jpg');

// Delete multiple files
Storage::delete(['file1.jpg', 'file2.jpg']);

Note

S3 Visibility

When uploading to S3, you can specify if the file should be "public" or "private" directly in the put method: Storage::put('file.jpg', $contents, 'public');

Warning

Cloud Storage Latency

While the API is the same for Local and S3, remember that S3 involves a network request. If you are checking Storage::exists() or getting file sizes inside a loop for 100 files, your application will slow down significantly. In those cases, consider caching file metadata in your database.

Helpers (Array, String, Path)

Laravel includes a variety of global PHP "helper" functions. These functions are designed to make your life easier by providing shortcuts for common tasks like manipulating arrays, transforming strings, and generating paths to files within your application.

String Helpers ( Str )

While PHP has native string functions, Laravel's Str class (and the str() helper) provides a more fluent and expressive way to handle complex string manipulations.

Method Result Description
str()->slug('Laravel Framework') laravel-framework Ideal for SEO-friendly URLs.
str()->limit($text, 20) The quick brown... Truncates a string with a suffix.
str()->contains($str, 'word') true / false Checks if a string exists within another.
str()->snake('FooBar') foo_bar Converts to snake_case.
str()->isUuid($string) true / false Validates if a string is a UUID.

Fluent Strings:

You can chain multiple string operations together using the str() helper:

$result = str(' Laravel 11 ')->trim()->replace('11', '12')->upper();
// "LARAVEL 12"

Array Helpers ( Arr )

The Arr class (and collect() for more complex logic) provides tools for working with PHP arrays.

Method Purpose
Arr::get($array, 'user.name') Uses "dot notation" to get a nested value safely.
Arr::has($array, 'products.0.price') Checks if a nested key exists.
Arr::forget($array, 'key') Removes a key/value pair using dot notation.
Arr::pluck($array, 'email') Pulls an array of values from a specific key.
Arr::wrap($value) Ensures the value is an array (wraps it if it's not).

Path Helpers

Path helpers allow you to generate absolute paths to various directories in your Laravel application. This is much safer than hardcoding paths, as directory structures can change between local and production environments.

Helper Path Returned
base_path() The root directory of your project.
app_path() The app directory.
config_path() The config directory.
public_path() The public directory.
resource_path() The resources directory (views, CSS, JS).
storage_path() The storage directory.

Example:

$path = storage_path('app/photos/user.jpg');

Miscellaneous Global Helpers

Beyond strings and arrays, several "power" helpers are used throughout the framework:

  • blank() / filled(): Better than empty(). blank() returns true if the value is null, an empty string, or only whitespace.
  • retry(): Executes a callback a certain number of times until it succeeds or hits a limit.
  • throw_if(): Throws an exception if a given condition is true.
  • optional(): Allows you to access properties or call methods on an object that might be null without crashing.

Note

Facade vs. Helper

Most helpers have a corresponding Facade (e.g., str() vs Str::). Using the str() helper is often preferred for "fluent" chaining, while the Str:: facade is used for quick static calls.

Warning

Custom Helpers

If you create your own global helper functions, it is best practice to wrap them in a function_exists check to avoid collisions with future Laravel updates or third-party packages.

HTTP Client (Guzzle Wrapper)

Laravel provides an expressive, minimal API around the Guzzle HTTP client, allowing you to quickly make outgoing HTTP requests to communicate with other web applications or third-party APIs. It focuses on the most common use cases and provides a wonderful testing experience.

Making Requests

You can use the Http facade to send GET, POST, PUT, PATCH, and DELETE requests. The client automatically handles JSON data and returns a fluent response object.

Method Usage Description
get() Http::get($url, $params) Retrieves data from an endpoint.
post() Http::post($url, $data) Sends data (defaults to JSON) to an endpoint.
withToken() Http::withToken($token) Quickly adds a Bearer token to the Authorization header.
timeout() Http::timeout(5) Specifies the maximum number of seconds to wait.

Example:

use Illuminate\Support\Facades\Http;
$response = Http::withToken('your-api-key')
->post('https://api.example.com/orders', [
'product_id' => 1,
'quantity' => 5,
]);

Handling Responses

The response object returned by the client provides a variety of methods that can be used to inspect the results of the request.

Method Result
$response->body() Returns the raw body content as a string.
$response->json() Decodes the JSON response into an array.
$response->status() Returns the HTTP status code (e.g., 200, 404).
$response->ok() Boolean: check if the status code is 200.
$response->failed() Boolean: check if the status code is >= 400.
if ($response->successful()) {
return $response->json()['data'];
}

Retries and Error Handling

External APIs can be flaky. Laravel makes it easy to retry requests that fail due to temporary network issues.

// Retry 3 times, waiting 100ms between attempts
$response = Http::retry(3, 100)->get('https://api.example.com');

// Throw an exception if the request fails (4xx or 5xx)
$response = Http::get('https://api.example.com')->throw();

Concurrent Requests

Sometimes you need to make multiple HTTP requests simultaneously to improve performance. You can use the pool method to dispatch several requests at once.

use Illuminate\Http\Client\Pool;

$responses = Http::pool(fn (Pool $pool) => [
    $pool->get('https://api.test/users'),
    $pool->get('https://api.test/posts'),
]);

return $responses[0]->ok() && $responses[1]->ok();

Testing & Faking

One of the best features of the Laravel HTTP client is the ability to "fake" responses. This allows you to test your application logic without actually hitting an external API.

Http::fake([
    // Stub a JSON response for GitHub endpoints...
    'github.com/*' => Http::response(['foo' => 'bar'], 200),

    // Stub a string response for Google endpoints...
    'google.com/*' => Http::response('Hello World', 200),
]);

$response = Http::get('https://github.com/laravel');
// Returns ['foo' => 'bar']

Note

Guzzle Options

Since Laravel's client is a wrapper for Guzzle, you can pass any Guzzle request options to the withOptions method if you need advanced configuration not covered by the standard facade.

Warning

Sensitive Data in Logs

If you are logging your outgoing HTTP requests, be careful with the withToken() or withHeaders() methods. Ensure your logging middleware masks sensitive keys like Authorization or X-Api-Key to prevent them from appearing in plain text in your log files.

Localization (i18n)

Laravel's localization features provide a convenient way to retrieve strings in various languages, allowing you to easily support multiple languages within your application. Language strings are stored in files within the lang directory.

Language Files & Structure

Laravel supports two ways of managing translation strings: using PHP files (for short keys) and JSON files (for full-sentence translations).

Storage Method Directory Best Use Case
PHP Files lang/{locale}/{file}.php Small, repetitive keys (e.g., auth.failed).
JSON Files lang/{locale}.json Large applications or translating full sentences.

Example PHP File (lang/es/messages.php):

return [
    'welcome' => '¡Bienvenido a nuestra aplicación!',
];

Example JSON File (lang/es.json):

{
    "I love Laravel": "Me encanta Laravel"
}

Retrieving Translation Strings

You may retrieve strings from your language files using the __ helper function or the @lang Blade directive.

Basic Retrieval

// From PHP file (filename.key)
echo __('messages.welcome');

// From JSON file (exact string)
echo __('I love Laravel');

Replacing Parameters

You can define "placeholders" in your translation strings by prefixing them with a :.

// lang/en/messages.php
'welcome' => 'Welcome, :name',

// Usage
echo __('messages.welcome', ['name' => 'Dayle']);

Pluralization

Laravel uses the ICU Message Format to handle pluralization rules gracefully using the trans_choice function or the | character.

// lang/en/messages.php
'apples' => '{0} There are none|[1,19] There are some|[20,*] There are many',

// Usage
echo trans_choice('messages.apples', 10);

Configuring & Switching Locales

The default language for your application is stored in config/app.php. You can change the locale at runtime using the App facade.

Action Code
Get Current Locale App::currentLocale();
Set Locale App::setLocale('es');
Check Locale App::isLocale('fr');

Middleware for Language Switching:

Usually, you would create a middleware to set the locale based on a session variable or a URL parameter:

public function handle(Request $request, Closure $next)
{
    if (Session::has('locale')) {
        App::setLocale(Session::get('locale'));
    }
    return $next($request);
}

Overriding Package Translations

If you are using a package that comes with its own translations, you can override them by placing files in lang/vendor/{package}/{locale}. This allows you to customize third-party text without modifying the vendor files.

Note

The trans() Helper

The trans() helper is an alias for __(). While both work the same way, __() is more common in modern Laravel applications, especially when using JSON translation files.

Warning

Fallback Locales

If a translation string does not exist for the current locale, Laravel will try to find it in the fallback_locale defined in config/app.php (usually en). If it still can't find it, it will return the key itself. Always ensure your fallback language files are complete.

Mail

Laravel provides a clean, simple API over the popular Symfony Mailer library. It includes drivers for SMTP, Mailgun, Postmark, and Amazon SES, allowing you to send mail through a local or cloud-based service of your choice.

Configuration

The email services are configured in config/mail.php. Each "mailer" defined here can have its own options and even its own "transport."

Driver Best Use Case
SMTP Standard email servers (Gmail, Outlook, Mailtrap).
SES High-volume, low-cost sending via Amazon Web Services.
Mailgun / Postmark Managed services with high deliverability and API features.
Log Writes emails to storage/logs/laravel.log (Great for local dev).

Generating Mailables

In Laravel, each email sent by your application is represented as a Mailable class. These classes are stored in the app/Mail directory.

Command:

php artisan make:mail OrderShipped

The Mailable Structure

A Mailable class uses three main methods to define the email's properties:

namespace App\Mail;

use App\Models\Order;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;

class OrderShipped extends Mailable
{
    public function __construct(protected Order $order) {}

    // Define Subject and "From"
    public function envelope(): Envelope
    {
        return new Envelope(subject: 'Order Shipped');
    }

    // Define the Blade view and data
    public function content(): Content
    {
        return new Content(
            view: 'emails.orders.shipped',
            with: ['orderName' => $this->order->name]
        );
    }
}

Sending Mail

To send an email, use the to method on the Mail facade. The to method accepts an email address, a user instance, or a collection of users.

use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;

Mail::to($request->user())->send(new OrderShipped($order));

Markdown Mailables

Markdown mailables allow you to take advantage of pre-built, beautiful HTML email templates provided by Laravel. You can write your email using Markdown, and Laravel will convert it into a responsive HTML email.

Command:

php artisan make:mail OrderShipped --markdown=emails.orders.shipped

Example Blade (emails/orders/shipped.blade.php):

<x-mail::message>
# Order Shipped

Your order has been dispatched!

<x-mail::button :url="$url">
View Order
</x-mail::button>

Thanks,<br>
{{ config('app.name') }}
</x-mail::message>

Queuing Mail

Since sending email can be a slow process that impacts application response time, most developers queue emails for background sending. To do this, simply have your Mailable implement the ShouldQueue interface.

class OrderShipped extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;
    // ...
}

When you call Mail::to(...)->send(), Laravel will automatically push the email onto the queue.

Previewing Mail in the Browser

Instead of sending emails to a real address during development, you can return a Mailable directly from a route to see how it looks in the browser.

Route::get('/mail-preview', function () {
    $order = App\Models\Order::find(1);
    return new App\Mail\OrderShipped($order);
});

Note

Mailtrap

For local testing, Mailtrap is a popular service that acts as a "fake" SMTP server. It catches all outgoing mail and displays it in a web interface, ensuring you never accidentally send a test email to a real customer.

Warning

Sensitive Data in Views

When passing data to an email view, remember that email content is often stored in plain text on mail servers. Avoid sending highly sensitive information (like passwords or full credit card numbers) directly in the body of an email.

Notifications (Email, SMS, Slack)

While Mailables are great for sending emails, Laravel Notifications are designed to send short, informational messages across a variety of delivery channels. A single notification can be delivered via Email, SMS, Slack, Database, or even custom channels like Telegram or Discord.

The Notification Class

Notifications are represented by a single class (typically stored in app/Notifications). Each class defines which channels the notification should be sent on and how the message should be formatted for each specific channel.

Component Purpose
via() Determines which channels the notification will be sent on (e.g., mail, database, slack).
toMail() Defines the email representation of the notification.
toDatabase() Defines the data that should be stored in the notifications table.
toSlack() Defines the message format for a Slack workspace.

Command:

php artisan make:notification InvoicePaid

Sending Notifications

You can send notifications in two ways: using the Notifiable trait on your models or using the Notification facade.

Using the Notifiable Trait

Your User model includes the Notifiable trait by default. This allows you to call a notify method directly on the user.

use App\Notifications\InvoicePaid;

$user->notify(new InvoicePaid($invoice));

Using the Notification Facade

This is useful when you need to send a notification to a collection of users or an "on-demand" recipient (someone not in your database).

use Illuminate\Support\Facades\Notification;

Notification::send($users, new InvoicePaid($invoice));

// On-demand (Anonymous) Notification
Notification::route('mail', 'guest@example.com')
    ->notify(new InvoicePaid($invoice));

Delivery Channels

Channel Requirement Best For
mail Mailer configured in mail.php. Receipts, password resets, official alerts.
database A notifications database table. In-app notification centers/bells.
broadcast WebSockets (Reverb/Pusher). Real-time browser alerts.
slack A Slack Webhook URL. Internal team alerts (e.g., "New Sale!").
vonage / twilio External SMS API credentials. Urgent security alerts, MFA codes.

Database Notifications

Storing notifications in a database allows you to display them in your application's UI (the "bell" icon). Laravel provides a migration for the necessary table:

php artisan notifications:table

Retrieving Notifications:

// Get all unread notifications
foreach ($user->unreadNotifications as $notification) {
    echo $notification->data['message'];

    // Mark as read
    $notification->markAsRead();
}

Formatting Slack Notifications

To send Slack notifications, you must install the Slack notification channel package. You can then define how the message looks using a "block" style.

public function toSlack(object $notifiable): SlackMessage
{
    return (new SlackMessage)
        ->content('A new invoice has been paid!')
        ->attachment(function ($attachment) {
            $attachment->title('Invoice #1234')
                       ->fields(['Amount' => '$99.00']);
        });
}

Note

Queuing Notifications

Just like Mailables, Notifications can take time to send. To make them asynchronous, simply implement the ShouldQueue interface in your Notification class. Laravel will then push the delivery of each channel onto your queue.

Warning

Channel Overload

Be careful not to overwhelm users by sending the same notification through too many channels at once. Use the via() method to implement logic (e.g., check user preferences) to determine if they should receive an SMS and an email, or just one of the two.

Package Development

Package development is the process of extracting reusable code into a standalone library that can be installed via Composer. This is the "pro level" of Laravel development, allowing you to share functionality across multiple projects or contribute to the open-source community.

Package Structure

A Laravel package typically follows a standard directory structure that mirrors a mini-Laravel application.

Directory/File Purpose
src/ Contains the actual logic (Controllers, Models, Services).
resources/ Contains views, lang files, and assets.
database/ Contains migrations and factories.
composer.json Defines dependencies and autoloading rules.
tests/ Contains the package's test suite (usually using Orchestra Testbench).

The Service Provider

The Service Provider is the connection point between your package and Laravel. It tells Laravel how to load your package's resources.

namespace YourName\PackageName;

use Illuminate\Support\ServiceProvider;

class PackageServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // Load routes, views, and migrations
        $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
        $this->loadViewsFrom(__DIR__.'/../resources/views', 'packagename');
        $this->loadMigrationsFrom(__DIR__.'/../database/migrations');

        // Publishing files for the user to customize
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../config/config.php' => config_path('packagename.php'),
            ], 'config');
        }
    }

    public function register()
    {
        // Merge configuration or bind classes to the container
        $this->mergeConfigFrom(__DIR__.'/../config/config.php', 'packagename');
    }
}

Developing Locally

To develop a package alongside an existing Laravel app without publishing it to GitHub first, you use the "path" repository feature in your main app's composer.json.

"repositories": [
    {
        "type": "path",
        "url": "../packages/my-package"
    }
],
"require": {
    "yourname/package-name": "dev-main"
}

Key Considerations

Feature Description
Namespacing Use a unique vendor name (e.g., Spatie, TaylorOtwell) to avoid collisions.
Config Publishing Always allow users to publish your config file so they can modify settings without touching your package code.
View Namespacing Use view('packagename::view-name') to ensure Laravel doesn't confuse your views with the app's views.
Testing Use Orchestra Testbench, which provides a "fake" Laravel environment to run your package tests.

Publishing to Packagist

Once your package is ready:

  1. Push to GitHub: Create a public repository.
  2. Tag a Version: Use Semantic Versioning (e.g., v1.0.0).
  3. Submit to Packagist: Go to Packagist.org and submit your GitHub URL.
  4. Install: Now anyone can run composer require yourname/packagename.

Note

Discovery

In your package's composer.json, you can add an extra section so that Laravel automatically detects and registers your Service Provider and Facades. This is called Package Discovery.

Warning

Dependency Versioning

Be careful when defining dependencies in your package. If you require laravel/framework: ^11.0 but a user is on Laravel 10, they won't be able to install your package. Try to support a range of versions if possible.

Processes (Invoking CLI commands)

While Laravel's Artisan allows you to run internal commands, you often need to execute external system commands—like optimized image compression (OptiPNG), video processing (FFmpeg), or custom Python scripts. Laravel provides a beautiful, fluent wrapper around the Symfony Process component to make this secure and easy.

Invoking a Process

You can use the Process facade to run a command. The result is a process object that allows you to inspect the output, exit code, and error messages.

use Illuminate\Support\Facades\Process;

$result = Process::run('ls -la');

return $result->output();

Process Results

The result instance returned by the run method provides several helpful methods for inspecting the output and status of the command.

Method Description
output() Returns the standard output (stdout) of the process.
errorOutput() Returns the error output (stderr) of the process.
exitCode() Returns the integer exit code (typically 0 for success).
successful() Returns a boolean indicating if the exit code was 0.
failed() Returns a boolean indicating if the exit code was not 0.

Asynchronous Processes

If you need to start a process in the background without waiting for it to finish, you may use the start method. This is useful for long-running tasks that shouldn't block the current request.

$process = Process::start('php artisan backup:run');

while ($process->running()) {
    // Do something else while the process is working...
}

$result = $process->wait();

Concurrent Processes

Laravel allows you to run multiple external commands at the same time and wait for all of them to finish. This is significantly faster than running them one after another.

use Illuminate\Process\Pool;

$results = Process::pool(function (Pool $pool) {
    $pool->path(public_path())->command('npm run build');
    $pool->path(base_path())->command('php artisan migrate');
})->start()->wait();

// Check if all were successful
$results->every(fn ($result) => $result->successful());

Testing & Faking

Just like the HTTP client, you should never run real system commands during your unit tests. Laravel allows you to "fake" the results of any process.

Process::fake([
    'ffmpeg *' => Process::result('Fake Video Content', exitCode: 0),
    'ls *' => Process::result('file1.txt', exitCode: 0),
]);

// This will now return "Fake Video Content" without actually running FFmpeg
$result = Process::run('ffmpeg -i input.mp4 output.avi');

Note

Security (Command Injection)

Laravel automatically escapes arguments when you pass them as an array. Always prefer the array syntax over a single string if you are including user input to prevent malicious "command injection" attacks.

  • Safe: Process::run(['ls', $userPath])
  • Unsafe: Process::run("ls $userPath")

Warning

TTY and Interactive Commands

Some CLI commands require a "TTY" (Terminal) to run (like those that ask for input). While Laravel supports tty(), these can be difficult to manage in a web environment. Whenever possible, use flags like -y or --no-interaction to ensure commands run non-interactively.

Queues & Background Jobs

Laravel Queues allow you to defer the processing of a time-consuming task, such as sending an email or generating a PDF, until a later time. By moving these tasks to the background, your application can respond to user requests much faster, providing a smoother user experience.

The Queue Workflow

When a task is "queued," it is serialized and stored in a database or a specialized "broker." A background process, known as a worker, constantly polls this storage and executes the jobs as they arrive.

Component Responsibility
Job A simple class containing the logic to be executed (e.g., ProcessVideo).
Dispatcher The part of your code that pushes the job into the queue.
Driver/Broker Where the jobs are stored (Database, Redis, Amazon SQS, etc.).
Worker A persistent CLI process that pulls jobs and runs them.

Configuration

You can configure your queue connections in config/queue.php.

Driver Description Best For
sync Executes jobs immediately (no background). Local development and testing.
database Stores jobs in a database table. Simple apps without external dependencies.
redis Fast, in-memory storage. Production standard for high-performance apps.
sqs Amazon Simple Queue Service. High-scale, cloud-native applications.

Creating and Dispatching Jobs

Command:

php artisan make:job ProcessVideo

This creates a class in app/Jobs implementing the ShouldQueue interface.

Defining a Job

namespace App\Jobs;

use App\Models\Video;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class ProcessVideo implements ShouldQueue
{
    use Queueable;

    public function __construct(public Video $video) {}

    public function handle(): void
    {
        // Expensive logic: transcode video, generate thumbnails...
    }
}

Dispatching a Job

use App\Jobs\ProcessVideo;

ProcessVideo::dispatch($video);

// Or with a delay
ProcessVideo::dispatch($video)->delay(now()->addMinutes(10));

Managing Workers

To start processing jobs, you must run the queue:work command. In production, this should be kept running by a process monitor like Supervisor.

  • php artisan queue:work: Starts a worker process.
  • php artisan queue:restart: Gracefully kills workers after they finish their current job (used during deployment).
  • php artisan queue:failed: Lists all jobs that failed.
  • php artisan queue:retry {id}: Attempts to run a failed job again.

Advanced Features

  • Job Chaining: Run a sequence of jobs in order. If one fails, the rest are not executed. Bus::chain([new ProcessVideo, new NotifyUser])->dispatch();
  • Job Batches: Dispatch a set of jobs and perform an action when the whole batch is finished.
  • Rate Limiting: Ensure a job (like calling an external API) doesn't run too many times per minute.

Note

Horizon

If you are using Redis for your queue, you should use Laravel Horizon. It provides a beautiful dashboard and code-driven configuration for your workers, allowing you to monitor job throughput and runtime in real-time.

Warning

Serializing Models

When a model is passed to a job, only its ID is stored in the queue. When the job is processed, the worker re-fetches the model from the DB. Ensure the data the worker needs still exists and hasn't changed in a way that would break the job logic.

Rate Limiting

Rate limiting is a security and performance strategy used to control the amount of incoming traffic to your application or specific features. It prevents "brute-force" attacks on login forms, protects your APIs from being overwhelmed by too many requests, and ensures fair usage for all users.

How it Works

Laravel uses a "Token Bucket" or "Fixed Window" approach to track requests. Every time a user hits a route, Laravel increments a counter associated with their IP address or User ID. If the counter exceeds a defined threshold within a specific timeframe, Laravel returns a 429 Too Many Requests response.

Defining Rate Limiters

Rate limiters are typically defined in the boot method of your App\Providers\AppServiceProvider (or RouteServiceProvider in older versions). You use the RateLimiter facade to define named configurations.

Configuration Example Use Case
Fixed Allow 60 requests per minute for everyone.
Dynamic Allow 100 requests for VIP users and 10 for guests.
Segmented Limit requests per IP address or per User ID.

Example Definition:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

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

Applying Rate Limiters to Routes

Once defined, you apply the rate limiter to routes or route groups using the throttle middleware followed by the name of the limiter.

Route::middleware(['throttle:api'])->group(function () {
    Route::get('/user', function () {
        // ...
    });
});

Manual Rate Limiting

Sometimes you need to rate limit logic that isn't tied to a specific route, such as a custom command or a button click in an internal dashboard. You can interact with the RateLimiter facade manually.

use Illuminate\Support\Facades\RateLimiter;

if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
    return 'Too many messages sent. Please wait.';
}

RateLimiter::hit('send-message:'.$user->id);

// Perform the action...

Rate Limiter Response Headers

When a rate limiter is active, Laravel automatically attaches headers to the outgoing response to inform the client of their current status:

Header Description
X-RateLimit-Limit The maximum number of requests allowed in the window.
X-RateLimit-Remaining The number of requests left in the current window.
Retry-After (Only on 429) The number of seconds to wait before trying again.

Note

Cache Dependency

Rate limiting relies on your application's Cache. If your cache driver is set to array, the rate limit will reset after every request. For production, ensure you are using redis or database to keep the counters persistent across requests.

Warning

IP Spoofing

If your application is behind a load balancer or a service like Cloudflare, request()->ip() might return the IP of the balancer rather than the user. Ensure your "Trusted Proxies" are configured correctly in Laravel so the rate limiter targets the correct individual.

String Manipulation

While we touched on string helpers in 7.9, Laravel's Fluent String Manipulation deserves its own deep dive. It allows you to perform a sequence of string operations using a readable, object-oriented syntax rather than nesting multiple PHP functions.

The Fluent Workflow

To begin a fluent string chain, use the Str::of() method or the str() helper. This wraps the raw string in an Illuminate\Support\Stringable object, giving you access to dozens of chainable methods.

Approach Syntax Example
Traditional (Helper) Str::slug(Str::limit($title, 20))
Fluent (Object) str($title)->limit(20)->slug()

Common Fluent Methods

Fluent strings allow you to transform text in a "pipeline" fashion.

Method Description Example Output
after() Returns everything after a given value. str('user@example.com')->after('@') // "example.com"
before() Returns everything before a given value. str('user@example.com')->before('@') // "user"
append() / prepend() Adds text to the end or start. str('log')->prepend('sys_')->append('.txt')
headline() Converts string to "Title Case" with spaces. str('user_first_name')->headline() // "User First Name"
markdown() Converts GitHub-flavored Markdown to HTML. str('# Hello')->markdown() // "<h1>Hello</h1>"
squish() Removes all extra whitespace. str(' John Doe ')->squish() // "John Doe"

Logical & Testing Methods

You can also perform logic checks within the chain. These methods return a boolean or allow for conditional transformations.

$isSecure = str('https://laravel.com')->startsWith('https://'); // true

$contains = str('The quick brown fox')->contains(['fox', 'dog']); // true

Conditional Transformations (when)

The when method allows you to apply a transformation only if a specific condition is met.

$string = str('laravel')
    ->when(true, function ($string) {
        return $string->upper();
    });
// "LARAVEL"

Advanced Manipulation

For complex text processing, Laravel provides methods that integrate with regular expressions or sub-string logic.

  • replaceMatches(): Replaces text based on a RegEx pattern.
  • scan(): Parses a string into a collection based on a format (like sscanf).
  • wordCount(): Returns the number of words in the string.
  • wrap(): Wraps the string with the given strings. str('Laravel')->wrap('**') becomes **Laravel**.

Converting Back to a String

Once you are finished with your transformations, you can treat the object like a string (it implements __toString()), or explicitly cast it.

$result = (string) str('  framework  ')->trim()->title();

// Or use it directly in a view
<h1>{{ str($page->title)->limit(50) }}</h1>

Note

Immutability

Like Collections, Fluent Strings are immutable. Each method in the chain returns a new Stringable instance. The original string remains unchanged unless you re-assign the variable.

Warning

Performance

While fluent strings are much more readable, creating a Stringable object is slightly more memory-intensive than using native PHP string functions. For a single explode() or trim(), native PHP is fine. For 3+ transformations, use the Fluent API for better code maintenance.

Task Scheduling (Cron Jobs)

In the past, developers had to generate a Cron configuration entry for every task they wanted to schedule on their server. This was a headache because your task schedule was no longer in source control, and you had to SSH into your server to view or edit them.

Laravel's Command Scheduler allows you to fluently and expressively define your command schedule within Laravel itself. Your schedule is defined in code, and only a single Cron entry is needed on your server to run everything.

The "Heartbeat" Cron Entry

To start the scheduler, you only need to add one single Cron item to your server. This entry calls the Laravel schedule runner every minute.

* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

Defining Schedules

In modern Laravel versions, you define your scheduled tasks in the withSchedule closure within your bootstrap/app.php file. In older versions, this was done in the app/Console/Kernel.php file.

Task Type Method Example
Artisan Command command() schedule->command('emails:send')->daily();
Queued Job job() schedule->job(new Heartbeat)->everyFiveMinutes();
Shell Command exec() schedule->exec('node script.js')->weekly();
Closure call() schedule->call(fn() => DB::table('logs')->delete())->monthly();

Schedule Frequency Options

Laravel provides dozens of frequency helpers to make your schedule readable.

Helper Frequency
->everyMinute() Run the task every minute.
->hourly() Run the task at the top of every hour.
->dailyAt('13:00') Run the task every day at 1:00 PM.
->twiceDaily(1, 13) Run at 1:00 AM and 1:00 PM.
->weekdays() Limit the task to Monday through Friday.
->cron('* * * * *') Use a custom Cron expression.

Task Hooks & Constraints

You can further refine when a task should run using truth tests or handle what happens after a task finishes.

  • ->when(Closure): Run the task only if the closure returns true.
  • ->onOneServer(): If you have multiple web servers, this ensures the task only runs on one of them (requires Redis/Memcached).
  • ->withoutOverlapping(): Prevents a task from running if the previous instance is still executing.
  • ->emailOutputTo('admin@example.com'): Automatically emails the console output if the task produces any.

Running the Scheduler Locally

During development, you don't need to mess with your local machine's Crontab. Laravel provides a convenient command that simulates the Cron heartbeat:

Command:

php artisan schedule:work

This command will stay running in your terminal and execute your scheduled tasks every minute as defined in your code.

Note

Maintenance Mode

By default, scheduled tasks will not run when Laravel is in maintenance mode. If you have a critical cleanup task that must run regardless, use the ->evenInMaintenanceMode() method when defining the schedule.

Warning

Task Timeouts

The scheduler assumes tasks finish quickly. If you have a task that takes 30 minutes to run, it might block other tasks or cause overlap issues. Always use ->withoutOverlapping() for long-running tasks or consider dispatching them to the Queue instead.

Testing Last updated: Feb. 28, 2026, 10:09 p.m.

Laravel is built with testing in mind from the ground up, integrating PHPUnit and Pest out of the box. This section emphasizes the difference between Unit Testing (testing a single function in isolation) and Feature Testing (testing an entire user flow, like a login or a checkout). The goal is to give developers "peace of mind" when refactoring code or adding new features.

The testing suite includes powerful helpers for Mocking and Faking. You can "fake" the entire cloud storage system, the mailer, or the event dispatcher to ensure your tests are fast and don't actually send real emails or charge real credit cards. By making testing easy and expressive, Laravel encourages a culture of high-quality, bug-free software.

Getting Started (Pest & PHPUnit)

Testing is the practice of writing code to verify that your application logic works as expected. In Laravel, testing is a first-class citizen. Every new Laravel installation comes pre-configured with two primary testing frameworks: PHPUnit (the industry standard) and Pest (the modern, elegant alternative).

Pest vs. PHPUnit

Laravel officially supports both. While PHPUnit uses a class-based approach, Pest offers a functional, "domain-specific language" (DSL) that makes tests much easier to read and write.

Feature PHPUnit Pest
Syntax Class-based ( class UserTest extends TestCase ) Functional ( it('does something', function() { ... }) )
Readability Verbose; requires boilerplate. Minimalist; reads like English sentences.
Features Mature, standard features. Includes built-in architecture testing and "Expectations."
Compatibility The foundation of PHP testing. Runs on top of PHPUnit (you can use both in one project).

Test Types: Unit vs. Feature

Laravel separates tests into two directories within the tests/ folder. Understanding the difference is crucial for a healthy test suite.

Test Type Location Scope Speed
Unit tests/Unit Tests a single function or class in isolation. Does not load the Laravel framework or database. Extremely Fast
Feature tests/Feature Tests a larger portion of code (e.g., a full HTTP request, database interaction, or event dispatch). Slower

Pro Tip: If your test needs to touch the database or hit a URL, it belongs in Feature. If it only tests a math calculation or string manipulation, it's a Unit test.

The Testing Environment

When running tests, Laravel automatically sets the configuration environment to testing (defined in your phpunit.xml file).

  • Environment Variables: You can override .env values specifically for tests (e.g., using an in-memory SQLite database).
  • Database Refresh: Feature tests often use the Illuminate\Foundation\Testing\RefreshDatabase trait. This wipes the database and runs migrations before every test, ensuring a clean state.

Writing Your First Test (Pest)

If you chose Pest during your Laravel installation, a basic feature test looks like this:

// tests/Feature/ExampleTest.php

it('returns a successful response', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});

Running Your Tests

Laravel provides a dedicated Artisan command to run your suite. It provides a beautiful summary of passed and failed tests.

  • Run all tests: php artisan test
  • Run with coverage: php artisan test --coverage (Requires Xdebug or PCOV)
  • Filter by name: php artisan test --filter NameOfTest

Common Assertions

Assertions are the "checks" at the end of your test. They verify that the outcome matches your expectations.

Assertion Description
assertStatus(200) Checks the HTTP response code.
assertSee('Welcome') Checks if the string exists in the response body.
assertDatabaseHas('users', [...]) Checks if a record exists in the database.
expect($value)->toBe(10) (Pest Syntax) Checks if a variable matches a value.

Note

Parallel Testing

If your test suite grows to hundreds of tests, it may become slow. You can run tests in parallel using php artisan test --parallel. This spins up multiple processes to execute tests simultaneously, significantly reducing wait times.

Warning

Production Databases

Never run tests using your production database. Laravel's RefreshDatabase trait will delete all your data. Always use a dedicated testing database or an in-memory :memory: SQLite instance.

HTTP Tests

HTTP tests (a subset of Feature Tests) allow you to simulate a full request-response lifecycle. Instead of manually clicking through your app, you write code that "visits" a URL, submits form data, and asserts that the resulting page or JSON response is correct.

The Request-Response Flow

Laravel provides a fluent API for making requests to your application's routes. These tests load the entire framework, allowing you to test Middleware, Controllers, and even View rendering.

Method Description
get($url) Simulates a GET request.
post($url, $data) Simulates a POST request with payload.
put($url, $data) Simulates a PUT/PATCH request for updates.
delete($url) Simulates a DELETE request.
withHeaders($headers) Attaches custom headers (e.g., Accept: application/json).

Authentication in Tests

Most of your routes will be protected by middleware. You don't need to manually fill out a login form in every test; you can use the actingAs method to specify which user is "logged in" for the duration of the request.

it('allows authenticated users to see the dashboard', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
         ->get('/dashboard')
         ->assertStatus(200);
});

Common HTTP Assertions

Once a request is made, you use assertions to verify the response. Laravel's response object is packed with helpful checks.

Response Status & Structure

Assertion Purpose
assertStatus(int) Asserts a specific HTTP code (200, 201, 403, 404, etc.).
assertRedirect($url) Asserts the response is a redirect to a specific path.
assertOk() Shortcut for assertStatus(200).
assertForbidden() Shortcut for assertStatus(403).
assertSee('Text') Checks if the string is present in the HTML/Body.
assertDontSee('Error') Verifies a string is not present.
assertViewIs('home') Ensures the correct Blade file was used.
assertViewHas('user') Checks if specific data was passed to the view.

Testing JSON APIs

If you are building an API, you will use specific assertions to check the JSON structure and data returned by your endpoints.

it('returns a list of products', function () {
    $response = $this->getJson('/api/products');

    $response->assertStatus(200)
             ->assertJsonStructure([
                 'data' => [
                     '*' => ['id', 'name', 'price']
                 ]
             ])
             ->assertJsonFragment(['name' => 'Laptop']);
});

Testing File Uploads

Testing file uploads is easy thanks to the Storage::fake and UploadedFile::fake methods. This ensures no real files are ever written to your disk during testing.

it('can upload an avatar', function () {
    Storage::fake('avatars');
    $file = UploadedFile::fake()->image('avatar.jpg');

    $this->post('/profile/avatar', ['avatar' => $file]);

    Storage::disk('avatars')->assertExists($file->hashName());
});

Session & Validation Errors

To test that your forms correctly validate data, you can assert that the session has errors for specific fields.

it('requires an email to register', function () {
    $this->post('/register', ['name' => 'John'])
         ->assertSessionHasErrors(['email']);
});

Note

Debugging Failed Tests

If a test fails and you aren't sure why, use $response->dump() or $response->dd() (Die and Dump). This will output the full HTML or JSON content of the response to your terminal so you can see exactly what went wrong.

Warning

CSRF Protection

Laravel automatically disables CSRF protection during tests. This allows you to make POST requests easily. However, keep in mind that other middleware (like custom auth or rate limiting) will still be active.

Console Tests

Console tests allow you to verify the behavior of your custom Artisan commands. Just as HTTP tests simulate web requests, console tests simulate terminal input and verify the output, exit codes, and side effects (like database changes) of your CLI tools.

The Console Testing Flow

Laravel provides the artisan method to initiate a console test. You can chain methods to provide "answers" to interactive prompts and assert that specific strings appear in the terminal.

Method Purpose
expectsQuestion() Provides a mock answer to an interactive prompt.
expectsOutput() Asserts that a specific string is printed to the console.
expectsConfirmation() Answers a "yes/no" (Boolean) prompt.
assertExitCode() Verifies the process return code (usually 0 for success).
doesntExpectOutput() Ensures specific text is not displayed.

Basic Command Test

Suppose you have a command mail:clean that asks for confirmation before deleting old logs.

Pest Example:

it('cleans logs after user confirmation', function () {
    $this->artisan('mail:clean')
         ->expectsConfirmation('Do you want to clean old logs?', 'yes')
         ->expectsOutput('Logs cleaned successfully!')
         ->assertExitCode(0);

    // Also assert side effects
    $this->assertDatabaseCount('logs', 0);
});

Handling Choices and Tables

If your command uses the choice or table methods, you can test those specifically.

Testing Choices

$this->artisan('user:create')
     ->expectsChoice('Which role?', 'Admin', ['Admin', 'Editor', 'User'])
     ->assertExitCode(0);

Testing Tables

While there isn't a direct expectsTable method, you can use expectsOutput to look for specific fragments of the table border or content.

$this->artisan('user:list')
     ->expectsOutput('Name')          // Part of the table header
     ->expectsOutput('Jane Doe')      // Part of the row data
     ->assertExitCode(0);

Testing Command Success/Failure

In CLI development, exit codes are the standard way to signal success or failure to other system processes.

Exit Code Meaning Laravel Assertion
0 Success assertExitCode(0) or assertSuccessful()
1 General Error assertExitCode(1) or assertFailed()
2+ Specific Error assertExitCode(n)

Mocking Console Components

In modern Laravel, you might use the components property for beautiful CLI output (e.g., $this->components->info('...')). Testing these follows the same expectsOutput logic, as Laravel captures all styled output into the same buffer.

Note

Testing Schedule Execution

You don't usually test the Scheduler itself, but rather the commands the scheduler runs. However, if you need to ensure a command is correctly scheduled, you can use Bus::fake() or specialized community packages to assert that a command was dispatched to the scheduler.

Warning

Blocking Commands

If your command contains an infinite loop or waits for external input that you haven't mocked with expectsQuestion, your test will hang indefinitely. Always ensure every prompt in your command has a corresponding expects call in your test.

Browser Tests (Laravel Dusk)

While HTTP tests simulate requests at the server level, Laravel Dusk provides an expressive, easy-to-use browser automation and testing API. It uses a real Chrome browser (via ChromeDriver) to test your application, allowing you to verify JavaScript, AJAX calls, and complex UI interactions that PHPUnit alone cannot see.

Why Use Dusk?

Feature HTTP Tests Laravel Dusk
JavaScript Cannot execute JS. Full JS/Vue/React support.
Execution Internal (fast). External browser (slower).
Visuals No UI rendered. Real rendering (can take screenshots).
Best For APIs, Back-end logic. Front-end UI, User Journeys.

Installation & Setup

Dusk is not included by default. You must install it via Composer:

1. composer require --dev laravel/dusk

2. php artisan dusk:install

This creates a tests/Browser directory and installs the latest ChromeDriver.

Writing a Browser Test

Dusk tests use a "Browser" instance to interact with your site. You can chain methods to click links, fill forms, and wait for elements to appear.

Example: User Login Journey

use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class LoginTest extends DuskTestCase
{
    public function test_user_can_login_successfully(): void
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/login')
                    ->type('email', 'taylor@laravel.com')
                    ->type('password', 'secret')
                    ->press('Login')
                    ->assertPathIs('/home')
                    ->assertSee('Welcome back!');
        });
    }
}

Key Interaction Methods

Dusk provides a massive library of helpers to simulate user behavior.

Method Action
visit($url) Navigates to a specific page.
click('#id') Clicks an element by selector or text.
type('name', 'val') Fills an input field.
check('id') Checks a checkbox.
select('id', 'val') Selects an option from a dropdown.
scrollIntoView('#id') Scrolls the browser to an element.

Waiting for JavaScript

Since JS is asynchronous, Dusk allows you to "wait" for the DOM to change before continuing the test.

$browser->click('#submit-btn')
        ->waitForText('Success!', 5) // Wait up to 5 seconds
        ->assertSee('Success!');

Debugging Tools

Testing in a browser can be tricky when things fail. Dusk includes built-in tools to help you see what went wrong:

  • Screenshots: When a test fails, Dusk automatically saves a screenshot in tests/Browser/screenshots.
  • Console Logs: It also captures the browser's JavaScript console output for inspection.
  • Manual Screenshots: You can trigger one manually: $browser->screenshot('filename');.
  • Headless Mode: By default, Dusk runs "headless" (no visible window). You can disable this in DuskTestCase.php to watch the browser work in real-time.

Note

Database Transactions

Unlike standard Feature tests, Dusk cannot use the RefreshDatabase trait with transactions because the browser runs in a separate process from the test. Instead, use the DatabaseMigrations trait to ensure your DB is fresh.

Warning

Environment Configuration

Dusk requires its own environment file. Create a .env.dusk.local file to ensure Dusk uses a dedicated test database, preventing it from overwriting your local development data during execution.

Database Testing

Database testing ensures that your application properly stores, retrieves, and manipulates data. Laravel provides a suite of tools to make database testing seamless, from resetting the database state between tests to asserting that specific records exist.

Resetting the Database

To ensure each test runs in a clean environment, you should reset your database before every test. Laravel provides traits to handle this automatically:

Trait Behavior Best Use Case
RefreshDatabase Migrates the database once, then uses transactions to reset data after each test. Recommended for most feature tests.
DatabaseMigrations Re-runs all migrations (Drop & Create) for every single test. Necessary when testing logic that involves ALTER TABLE or specific schema changes.
DatabaseTransactions Wraps the test in a transaction and rolls it back. Fast, but does not handle auto-increment resets or schema changes.

Using Model Factories

Instead of manually creating arrays for your database records, use Model Factories to generate "fake" data. This keeps your tests clean and resilient to schema changes.

it('calculates the total price of an order', function () {
    // Create a user with 3 related orders
    $user = User::factory()
        ->has(Order::factory()->count(3), 'orders')
        ->create();

    expect($user->orders)->toHaveCount(3);
});

Database Assertions

Once your logic has executed, you need to verify the state of the database. Laravel's TestCase includes several specialized assertions:

Assertion Description
assertDatabaseHas($table, $data) Asserts that a row matching the data exists.
assertDatabaseMissing($table, $data) Asserts that a row matching the data does not exist.
assertDatabaseCount($table, $count) Asserts the total number of rows in a table.
assertSoftDeleted($model) Asserts that the given model has been soft deleted.
assertModelExists($model) Asserts the model exists in the database.

Example: Deleting a Product

it('can delete a product', function () {
    $product = Product::factory()->create();

    $this->delete("/products/{$product->id}");

    $this->assertDatabaseMissing('products', [
        'id' => $product->id,
    ]);
});

Testing with SQLite (In-Memory)

For maximum speed, many developers use an in-memory SQLite database for testing. This lives entirely in RAM and is destroyed the moment the test finishes.

Configuration (phpunit.xml):

<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>

Seeders in Tests

Sometimes your tests require "static" data (like a list of countries or roles) to be present. You can run specific seeders within your test's setUp method or the test itself:

beforeEach(function () {
    $this->seed(RoleSeeder::class);
});

Note

Testing Relationships

When asserting relationships, remember that models are cached in memory. If you update a relationship in your controller and then check $user->posts in your test, it might show the old data. Use $user->refresh() to reload the model and its relationships from the database.

Warning

Database Truncation

If you are using a real database (like MySQL) for testing and you stop a test midway, the RefreshDatabase transaction might not roll back. This can leave "junk" data in your tables. Running php artisan migrate:fresh will fix this.

Mocking

Mocking is the practice of replacing a real component of your application with a "fake" version that simulates its behavior. This is essential for testing code that interacts with external services (like APIs or payment gateways) or slow processes (like file uploads), ensuring your tests remain fast, predictable, and isolated.

Why Mock?

Scenario Real Component Problem Mocking Solution
External APIs Network dependency, rate limits, costs. Return a hardcoded JSON response instantly.
Payment Gateways Risk of real transactions, slow. Simulate a "Success" or "Declined" token.
Time/Date Tests might fail depending on when they run. "Freeze" time at a specific second.
Email/SMS Sending real spam to test addresses. Assert that an email would have been sent.

Faking Laravel Services

Laravel provides built-in "fakes" for its most common services. These allow you to intercept actions and make assertions without actually executing the logic.

The Mail Fake

use Illuminate\Support\Facades\Mail;
use App\Mail\OrderShipped;

it('sends an order shipment email', function () {
    Mail::fake();

    // Perform action...
    OrderShipped::dispatch($order);

    // Assert a mailable was sent
    Mail::assertSent(OrderShipped::class);
    
    // Assert a mailable was NOT sent
    Mail::assertNotSent(AnotherMail::class);
});

The Event & Queue Fakes

Useful for ensuring that side effects are triggered without actually running the listeners or background jobs.

Fake Assertion Example
Event::fake() Event::assertDispatched(UserRegistered::class);
Queue::fake() Queue::assertPushed(ProcessPodcast::class);
Notification::fake() Notification::assertSentTo($user, InvoicePaid::class);

The HTTP Client Fake

As discussed in 7.10, the HTTP client can be faked to avoid making real network requests. You can even sequence responses to simulate a complex API conversation.

Http::fake([
    'github.com/*' => Http::response(['id' => 1], 200),
    'api.stripe.com/*' => Http::sequence()
                            ->push(['status' => 'pending'])
                            ->push(['status' => 'success']),
]);

Mocking Custom Objects (Mockery)

For your own internal classes or third-party libraries that don't have built-in Laravel fakes, Laravel integrates with Mockery.

it('calls the price calculator service', function () {
    $mock = $this->mock(PriceCalculator::class, function ($mock) {
        $mock->shouldReceive('calculate')
             ->once()
             ->with(100)
             ->andReturn(120);
    });

    // When the app pulls PriceCalculator from the container, 
    // it gets this mock instead of the real class.
    $response = $this->get('/calculate/100');
    
    $response->assertSee('120');
});

Time Travel

Testing logic that depends on time (e.g., "trial expires in 30 days") can be difficult. Laravel allows you to "travel" to the future or "freeze" the current time.

// Travel to the future
$this->travel(30)->days();

expect(now()->isFuture())->toBeTrue();

// Travel back to the present
$this->travelBack();

// Freeze time at a specific moment
$this->travelTo(now()->startOfYear());

Note

Mocking Facades

You can mock any Laravel Facade directly using the shouldReceive method. For example: Cache::shouldReceive('get')->once()->with('key')->andReturn('value');.

Warning

Over-Mocking

Avoid mocking everything. If you mock your Models, Repositories, and Services all at once, you aren't testing your application—you're just testing your mocks. Use Feature Tests with a real database whenever possible, and save mocking for external boundaries.

Official Packages & Ecosystem Last updated: Feb. 28, 2026, 10:09 p.m.

Laravel isn't just a framework; it’s a complete ecosystem of specialized tools. This section highlights official first-party packages that solve complex problems with a single command. From Cashier for subscription billing to Echo for real-time WebSockets, these packages are designed to integrate perfectly with the core framework, maintaining the same syntax and quality standards.

The ecosystem also provides tools for deployment and monitoring, such as Forge, Vapor, and Pulse. These services remove the "DevOps" burden from developers, allowing for "push-to-deploy" workflows and real-time performance tracking. By providing these official solutions, Laravel eliminates the "choice paralysis" often found in other ecosystems, giving you a clear, proven path from a local idea to a global production app.

Breeze & Jetstream (Auth Scaffolding)

Laravel provides two primary "starter kits" to jumpstart your application's authentication and frontend structure. Instead of spending hours building login forms, password resets, and email verification, you can install a scaffold that provides a professional, secure starting point.

Comparing the Kits

Choosing between Breeze and Jetstream depends on the complexity of your application and your familiarity with specific frontend stacks.

Feature Laravel Breeze Laravel Jetstream
Complexity Simple, minimal, easy to customize. Advanced, feature-rich, opinionated.
Styling Tailwind CSS. Tailwind CSS.
Stacks Blade, Vue, or React. Livewire or Inertia.js.
Auth Features Login, Register, Password Reset. Login, Register, Two-Factor Auth (2FA).
Profile Basic profile page. API Tokens, Profile Photos, Account Deletion.
Teams No. Yes (Built-in team management).

Laravel Breeze

Breeze is the "entry-level" starter kit. It is designed to be a minimal and simple starting point for building a Laravel application with authentication. It is the perfect choice if you want full control over your views and don't need advanced features like 2FA or team management out of the box.

  • Best For: Small to medium projects, learning Laravel, or developers who want to write their own custom logic on top of a clean base.
  • Installation:
    composer require laravel/breeze --dev
    php artisan breeze:install

Laravel Jetstream

Jetstream is a more robust application scaffolding for Laravel. It provides the implementation for more complex features that modern applications require.

Key Advanced Features:

  • Two-Factor Authentication: Uses a QR code and recovery codes via the Fortify engine.
  • API Support: Integrated with Laravel Sanctum, allowing users to generate API tokens with specific permissions (abilities).
  • Team Management: Allows users to create and join teams, manage team roles, and switch between team contexts.
  • Browser Sessions: Allows users to view and logout of their active sessions on other devices.
  • Best For: Enterprise SaaS applications, projects requiring high security (2FA), or apps that revolve around "Teams."
  • Installation:
    composer require laravel/jetstream
    php artisan jetstream:install livewire (or inertia)

The "Stack" Choice

When installing these kits, you will often have to choose between Livewire and Inertia.js:

  • Livewire: Allows you to build dynamic interfaces using mostly PHP. It feels very "Laravel-native."
  • Inertia.js: Allows you to build single-page apps (SPAs) using Vue or React, but keeps the routing and controllers inside Laravel.

Summary of Ecosystem Tools

Engine Responsibility
Laravel Fortify A frontend-agnostic authentication backend (logic only).
Laravel Sanctum Provides a featherweight authentication system for SPAs and simple APIs.

Note

Customization

Once installed, the code for Breeze and Jetstream is published directly into your application. You are encouraged to modify the controllers, views, and components to match your specific needs. They are not "locked" libraries.

Warning

Choosing Too Early

Switching from Breeze to Jetstream mid-project can be difficult because they have different directory structures and dependencies. It is better to start with Breeze if you are unsure; it is easier to add features to Breeze than it is to strip complexity out of Jetstream.

Cashier (Stripe/Paddle Subscriptions)

Laravel Cashier provides an expressive, fluent interface to subscription billing services through Stripe or Paddle. It handles almost all of the "boilerplate" billing code you would otherwise have to write manually, including managing subscriptions, handling coupons, swapping subscription quantities, and even generating PDF invoices.

Stripe vs. Paddle

Laravel offers two separate versions of Cashier depending on which payment provider you choose.

Feature Cashier (Stripe) Cashier (Paddle)
Model Direct payment processor. Merchant of Record (MoR).
Sales Tax You must calculate/collect via Stripe Tax. Paddle handles global sales tax/VAT for you.
Control High control over the checkout experience. Paddle manages the checkout "overlay."
Complexity Requires more manual tax/compliance setup. Simpler for international compliance.

Core Concepts

Cashier works by adding a Billable trait to your User model, which gives you access to various billing methods.

Subscribing a User

To create a subscription, you typically first create a "Setup Intent" or redirect the user to a pre-built Stripe Checkout page.

// Creating a subscription with a specific price ID from Stripe
$user->newSubscription('default', 'price_premium_monthly')->create($paymentMethod);

Checking Subscription Status

You can easily verify if a user is "pro" or has access to specific features.

if ($user->subscribed('default')) {
    // User is an active subscriber
}

if ($user->subscribedToPrice('price_premium_monthly', 'default')) {
    // User is on a specific plan
}

Handling Common Billing Tasks

Cashier simplifies complex billing flows into single method calls:

Task Method Example
Swap Plans $user->subscription('default')->swap('new-price-id');
Cancel $user->subscription('default')->cancel();
Resume $user->subscription('default')->resume();
Check Grace Period $user->subscription('default')->onGracePeriod();
Invoice PDF $user->downloadInvoice($invoiceId, ['vendor' => 'My App']);

Webhooks & Security

Since payments happen on Stripe/Paddle's servers, they need a way to tell your app when a payment succeeds or a subscription is canceled. This is done via Webhooks.

  • Laravel Hook: Cashier includes a built-in controller to handle these incoming signals.
  • Protection: Cashier automatically verifies the webhook signature to ensure the request actually came from Stripe/Paddle and not a malicious actor.

Invoices and Receipts

Cashier allows you to easily retrieve a collection of a user's invoices so they can be displayed in their account settings:

<table>
    @foreach ($user->invoices() as $invoice)
        <tr>
            <td>{{ $invoice->date()->toFormattedDateString() }}</td>
            <td>{{ $invoice->total() }}</td>
            <td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
        </tr>
    @endforeach
</table>

Note

Stripe Tax

For Stripe users, Cashier integrates with Stripe Tax, which can automatically calculate the tax for your customers based on their location during the checkout process.

Warning

Local Testing

Webhooks cannot reach your local machine because your localhost is not accessible from the internet. You must use a tool like the Stripe CLI (stripe listen --forward-to...) or a service like Ngrok to "tunnel" the webhooks to your local development environment.

Echo (Frontend WebSockets)

Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by Laravel. It works hand-in-hand with a WebSocket "broadcaster" to bring real-time, interactive features—like live chat, notifications, and status updates—to your application.

The Real-Time Stack

To use Echo, you need three main components working together:

Component Role
Event A standard Laravel event class that implements the ShouldBroadcast interface.
Broadcaster The "driver" that pushes the event (e.g., Reverb, Pusher, or Ably).
Echo (Client) The JavaScript library on the frontend that "listens" for the broadcast.

Broadcasting Drivers

Driver Description Best For
Laravel Reverb A first-party, high-performance WebSocket server written in PHP. Recommended for modern apps wanting full control and speed.
Pusher / Ably Managed cloud services. No server setup required. Apps that prefer not to manage their own WebSocket infrastructure.
Redis / Socket.io A community-driven approach using Node.js. Legacy setups or highly custom Socket.io requirements.

Channel Types

Not all data should be public. Laravel Echo supports three distinct types of channels:

Channel Type Description Use Case
Public Anyone can listen; no authorization required. Live stock tickers, public wall posts.
Private Requires user authentication and authorization logic. Private messages, user-specific notifications.
Presence Like Private, but also tracks who else is on the channel. "Who is online" lists, "User is typing..." indicators.

Broadcasting an Event

First, ensure your event implements ShouldBroadcast. Laravel will automatically detect this and push the event to your queue (or broadcast immediately).

namespace App\Events;

use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;

class OrderStatusUpdated implements ShouldBroadcast
{
    use Dispatchable;

    public function __construct(public $order) {}

    // Define which channel the event should be sent to
    public function broadcastOn(): Channel
    {
        return new PrivateChannel('orders.'.$this->order->id);
    }
}

Listening with Echo

Once the event is dispatched from the server, you "catch" it in your JavaScript using Echo.

import Echo from 'laravel-echo';

// Listening on a private channel
Echo.private(`orders.${orderId}`)
    .listen('OrderStatusUpdated', (e) => {
        console.log('New Status:', e.order.status);
    });

Presence Channels

Presence channels allow you to see a list of users currently viewing a page. This is incredibly powerful for collaborative tools.

Echo.join(`chat.${roomId}`)
    .here((users) => {
        // Runs when you first join; returns all users present
    })
    .joining((user) => {
        // Runs when a new user joins
    })
    .leaving((user) => {
        // Runs when a user leaves
    });

Note

Client-to-Client (Whisper)

Sometimes you want to send small updates between users without hitting the server (like a "typing" indicator). Echo allows you to "Whisper" these events:

channel.whisper('typing', { name: 'John' });

Warning

Security & Authorization

For Private and Presence channels, you must define authorization rules in your routes/channels.php file. If you forget this, Echo will fail to connect because it cannot verify the user has permission to hear the data.

Envoy (Remote Task Execution)

Laravel Envoy is a tool for executing common tasks you run on your remote servers. It uses a clean, minimal syntax—similar to Blade—to define "tasks" for deployment, database migrations, and server maintenance.

Under the hood, Envoy uses SSH to connect to your servers, so you don't need to install anything on the remote machines except for an SSH key.

Installation

Envoy is a standalone tool installed globally via Composer:

composer global require laravel/envoy

The Envoy.blade.php File

All of your tasks are defined in an Envoy.blade.php file in your project's root. This file can include @servers declarations, @task definitions, and even PHP logic.

Directive Purpose
@servers Defines the IP addresses or hostnames of your remote servers.
@task Contains the Bash commands to be executed on the servers.
@story Groups multiple tasks into a single execution sequence.
@setup Allows you to define variables and PHP code before tasks run.

Defining Tasks and Stories

Envoy allows you to run commands on multiple servers simultaneously or in sequence.

@servers(['web' => 'user@192.168.1.1', 'db' => 'user@192.168.1.2'])

@task('deploy', ['on' => 'web'])
    cd /var/www/html
    git pull origin main
    composer install --no-interaction --quiet --optimize-autoloader
    php artisan migrate --force
@endtask

@story('release')
    deploy
    optimize_stuff
@endstory

Key Features

Feature Description
Parallel Execution Run a task on all defined servers at once using the parallel option.
Confirmations Use confirm to prevent accidental execution: @task('deploy', ['on' => 'web', 'confirm' => true]).
Notifications Automatically send alerts to Slack, Discord, or Telegram after a task completes.
Variables Pass data into your tasks via the command line: envoy run deploy --branch=master.

Running Tasks

To execute a task or story, use the run command from your terminal:

  • Run a single task: envoy run deploy
  • Run with a variable: envoy run deploy --branch=develop

Envoy vs. Laravel Forge

Tool Approach Best For
Envoy Code-driven, manual execution. Custom deployment scripts and quick server tweaks.
Forge UI-driven, automated. Server provisioning and "Push to Deploy" automation.

Note

SSH Key Auth

Envoy requires SSH Key Authentication to function. It cannot handle interactive password prompts. Ensure your local public key is added to the ~/.ssh/authorized_keys file on your remote server before running Envoy.

Warning

Security

Avoid hardcoding sensitive credentials (like database passwords) directly in your Envoy.blade.php file. Instead, use environment variables on the remote server or pass them securely as command-line arguments.

Fortify (Headless Authentication)

Laravel Fortify is a frontend-agnostic authentication backend implementation for Laravel. It provides all the logic required to build secure authentication features—like login, registration, password resets, and two-factor authentication—without providing the actual UI (views).

While tools like Breeze and Jetstream use Fortify under the hood, you can use Fortify directly if you want to build a completely custom frontend (using Vue, React, or standard Blade) while keeping the robust security logic of the Laravel ecosystem.

What Fortify Handles

Fortify takes care of the "invisible" work. When you submit a login form to a Fortify route, it handles throttling, user validation, and session management automatically.

Feature Responsibility
Registration Validates new users and stores them in the database.
Authentication Handles login attempts, session persistence, and logout.
Password Reset Generates secure tokens and handles email links.
Two-Factor Auth Generates QR codes and verifies TOTP (Time-based One-Time Password) codes.
Email Verification Ensures users verify their email addresses before accessing routes.

The "Actions" Pattern

Fortify doesn't use standard controllers that you can edit. Instead, it uses Actions. When you install Fortify, several action classes are published to app/Actions/Fortify. You can modify these classes to change how users are validated or created.

Example: CreateNewUser.php

public function create(array $input): User
{
    Validator::make($input, [
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', 'unique:User'],
        'password' => $this->passwordRules(),
    ])->validate();

    return User::create([
        'name' => $input['name'],
        'email' => $input['email'],
        'password' => Hash::make($input['password']),
    ]);
}

Configuration and Features

You can enable or disable specific features in the config/fortify.php configuration file.

Feature Configuration Key
Registration Features::registration()
Reset Passwords Features::resetPasswords()
Email Verification Features::emailVerification()
Update Profiles Features::updateProfileInformation()
Two-Factor Auth Features::twoFactorAuthentication(['confirm' => true])

Defining the Views

Because Fortify is headless, you must tell it which Blade templates to show when a user visits /login or /register. You do this in the boot method of your App\Providers\FortifyServiceProvider.

use Laravel\Fortify\Fortify;

Fortify::loginView(function () {
    return view('auth.login');
});

Fortify::registerView(function () {
    return view('auth.register');
});

Security Features

Fortify is built with security as the priority:

  • Rate Limiting: Automatically throttles login attempts per IP and username to prevent brute-force attacks.
  • Password Validation: Enforces secure password requirements (length, uppercase, symbols).
  • Session Hijacking Protection: Provides logic for "confirming" passwords before sensitive actions.

Note

Fortify vs. Passport/Sanctum

Fortify is designed for Session-based authentication (standard web apps). If you are building a mobile app or a strictly stateless API, you should use Laravel Sanctum or Laravel Passport instead.

Warning

Service Provider Registration

After installing Fortify, you must manually register the FortifyServiceProvider in your bootstrap/providers.php file (or config/app.php in older versions). Without this, the authentication routes will not exist.

Horizon (Redis Queue Dashboard)

Laravel Horizon provides a beautiful, real-time dashboard and code-driven configuration for your Laravel-powered Redis queues. It allows you to easily monitor key metrics of your queue system, such as job throughput, runtime, and job failures.

While standard Laravel queues can run using simple CLI commands, Horizon acts as a "super-manager" for your queue workers, ensuring they stay running and scale according to your needs.

Key Features

Feature Description
Real-Time Dashboard A web-based UI to see everything happening in your queues.
Code-Driven Config Configure your workers in a PHP file (config/horizon.php) rather than server-side scripts.
Auto-Scaling Horizon can automatically spin up more worker processes when a queue gets busy and kill them when it's quiet.
Job Metrics Detailed analytics on how long jobs take to run and which jobs fail most often.
Failed Job Management Easily view, retry, or discard failed jobs directly from the UI.

Configuration

Instead of managing queue:work processes via Supervisor manually for every queue, you define "environments" in config/horizon.php.

'environments' => [
    'production' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => ['default', 'notifications'],
            'balance' => 'auto', // Options: simple, auto, or null
            'maxProcesses' => 10,
            'tries' => 3,
        ],
    ],
],

Balancing Strategies

Horizon can manage how processes are distributed across your queues:

  • null (Simple): Fixed number of processes for each queue.
  • simple: Splits processes evenly between the queues.
  • auto: The most powerful mode. It shifts worker processes to whichever queue has the most "backlog" (waiting jobs).

Monitoring & Tags

Horizon allows you to "tag" jobs so you can search for them in the dashboard. By default, if a job is associated with an Eloquent model, Horizon automatically tags it with that model's class and ID.

// In the dashboard, you can search for "App\Models\Order:1"
// to see every job that processed that specific order.

Installation & Execution

  1. Install: composer require laravel/horizon
  2. Publish Assets: php artisan horizon:install
  3. Run: php artisan horizon

In a production environment, you should use a process monitor like Supervisor to keep the php artisan horizon command running indefinitely.

Note

Redis Requirement

Horizon is designed specifically for Redis. It will not work with database, sqs, or beanstalkd queue drivers.

Warning

Dashboard Security

By default, the Horizon dashboard is only accessible in the local environment. To access it in production, you must define an authorization gate in your App\Providers\HorizonServiceProvider to restrict access to specific users or email addresses.

Octane (High-Performance Server)

Laravel Octane supercharges your application's performance by serving it using high-powered application servers like FrankenPHP, Swoole, or RoadRunner.

While traditional PHP follows a "shared-nothing" architecture—where the entire framework is booted and destroyed for every single request—Octane boots the framework once, keeps it in memory (RAM), and then feeds requests to it at lightning speed.

Performance Comparison

Feature Traditional (PHP-FPM) Laravel Octane
Bootstrapping Every single request. Once (at server start).
Response Time Faster than most, but has overhead. Extremely Low (often <10ms).
Memory Usage High (boots app repeatedly). Efficient (shared memory).
Concurrency Limited by process count. Handles thousands of concurrent hits.

Supported Servers

Server Key Strength
FrankenPHP Modern, built on Go, supports early hints and 103 HTTP status codes.
Swoole Massive concurrency, supports coroutines and high-level async tasks.
RoadRunner Stable, written in Go, works well as a binary outside of PHP.

The "Stateless" Rule

Because Octane keeps your application in memory, you must be careful about State. In a traditional app, global variables reset every request. In Octane, if you set a variable in a Singleton or a Static property, it stays there for the next user.

  • Bad: Storing user-specific data in a static property.
  • Good: Relying on Laravel's Container to reset services (Octane handles most of this for you).

Octane-Specific Features

Octane provides additional tools to take advantage of the persistent memory environment:

Concurrent Tasks

You can run multiple operations at the exact same time and wait for all of them to finish.

[$users, $orders] = Octane::concurrently([
    fn () => User::all(),
    fn () => Order::all(),
]);

Octane Cache

A specialized, ultra-fast cache driver that stores data in the server's memory rather than hitting Redis or a database.

Octane::cache()->put('stats', $stats, 10);

Installation

  1. Install: composer require laravel/octane
  2. Install Server: php artisan octane:install (Choose FrankenPHP, Swoole, or RoadRunner)
  3. Start: php artisan octane:start

Note

Dependency Injection

If you inject the Request object into a Singleton's constructor, that singleton will be "stuck" with the first request that ever hit the server. Always resolve the request within methods or use a Closure to ensure you are getting the current user's request.

Warning

Memory Leaks

Since the process never dies, a memory leak (like adding items to a global array indefinitely) will eventually crash your server. Octane includes a --max-requests flag to automatically restart workers after they handle a certain amount of traffic to mitigate this.

Pennant (Feature Flags)

Laravel Pennant is a simple, lightweight library for managing Feature Flags. Feature flags allow you to roll out new application features with confidence by decoupling code deployment from feature release. You can deploy code to production but keep the feature "hidden" or only visible to specific users.

Why Use Feature Flags?

Benefit Description
Gradual Rollouts Enable a feature for 10% of users to test stability before a full release.
Beta Testing Grant access to new tools only to users with a beta-tester tag.
Kill Switch Instantly disable a buggy feature in production without a new deployment.
A/B Testing Show different versions of a feature to different segments to measure engagement.

Defining Features

You typically define features in the boot method of your AppServiceProvider. Features can be simple "on/off" switches or return complex values.

use Laravel\Pennant\Feature;
use App\Models\User;

// A simple boolean feature
Feature::define('new-onboarding-flow', function (User $user) {
    return $user->created_at->gt('2025-01-01');
});

// A feature with multiple values (A/B testing)
Feature::define('purchase-button-color', function (User $user) {
    return Lottery::odds(1, 2)->choose(['red', 'blue']);
});

Checking Features

You can check the status of a feature in your Controllers, Blade templates, or Middleware.

In PHP Logic

if (Feature::active('new-onboarding-flow')) {
    // Show the new experience
}

In Blade Templates

@feature('new-onboarding-flow')
    <x-new-header />
@else
    <x-old-header />
@endfeature

Storage Drivers

Pennant needs to remember which users have which features active so the experience stays consistent for them.

Driver Best For
database Persistent storage. Recommended for production so flags survive cache clears.
array Testing. Resets every request; very fast.

Advanced Usage

  • Scope: Features are usually scoped to the authenticated User, but you can scope them to a Team, Project, or even an IP address.
  • Rich Values: Instead of just true / false, a feature can return strings, arrays, or integers (e.g., a feature flag that determines the "Max File Upload Size" for a specific tier).
  • Pre-Caching: To avoid N+1 queries when checking features for a list of users, you can use Feature::load(['feature-name']).

Managing via CLI

Pennant includes Artisan commands to manage flags manually from the terminal:

  • Activate for all: php artisan pnn:activate feature-name
  • Deactivate for all: php artisan pnn:deactivate feature-name
  • Purge old flags: php artisan pnn:purge feature-name

Note

Consistency

Once a feature is resolved for a specific user, Pennant "pins" that value in the database. Even if you change the logic in your code, the user will keep the value they first received until the feature is purged or re-resolved.

Warning

Technical Debt

Feature flags are powerful but can lead to "conditional hell" if left in the codebase too long. Once a feature is fully rolled out to 100% of users, make sure to delete the flag logic and the @feature tags to keep your code clean.

Pulse (Health & Performance Monitoring)

Laravel Pulse is a first-party, real-time application performance monitoring (APM) tool and dashboard. Unlike Laravel Telescope, which is designed for local debugging and deep inspection of individual requests, Pulse is built for production environment monitoring. It provides high-level aggregate insights into your application's health, bottlenecks, and usage patterns.

At-A-Glance Insights

Pulse uses a card-based UI to display critical data about your stack. It is designed to be lightweight enough to run directly on your production servers with minimal overhead.

Monitor Card Description
Server Stats Real-time CPU, memory, and disk usage for one or multiple servers.
Application Usage Identifies the most active users, users experiencing the slowest endpoints, and those dispatching the most jobs.
Exceptions Aggregates and tracks recurring exceptions, helping you spot anomalies in production.
Slow Queries Lists the slowest database queries and identifies the exact line of code that triggered them.
Queue Monitoring Visualizes throughput, pending jobs, and failure rates for your background tasks.
Slow Outgoing Requests Tracks slow external API calls made via the Laravel HTTP client.

Performance-First Architecture

Pulse is built to handle heavy production workloads using several strategies to ensure it doesn't slow down your application:

  • Minimal Data Storage: It stores only the essential aggregate data needed for its charts.
  • Storage Drivers: By default, Pulse uses your existing database (MySQL/PostgreSQL), but for high-traffic sites, it can be configured to use Redis Ingest.
  • Sampling: You can enable sampling (e.g., only record 10% of requests) to reduce the database write frequency on extremely busy applications.

Installation & Configuration

  1. Install: composer require laravel/pulse
  2. Setup: php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
  3. Migrate: php artisan migrate

To enable server monitoring, you must run the following command on each of your application servers (usually managed by Supervisor):

php artisan pulse:check

Customizing the Dashboard

Pulse is highly extensible because the dashboard is built with Livewire. You can easily reorder, remove, or even create your own custom cards to track business-specific metrics.

// Creating a custom card starts by extending the Pulse Card component
use Laravel\Pulse\Livewire\Card;

class TopSellers extends Card {
    public function render() {
        // Query your data and return a view
    }
}

Note

Authorization

In production, the /pulse dashboard is locked by default. You must authorize users by defining the viewPulse gate in your App\Providers\AppServiceProvider to ensure only your team can see your performance data.

Warning

Data Trimming

Pulse data can grow quickly in the database. Laravel provides a pulse:check command that handles periodic data trimming, but you should ensure this command is running as a daemon to prevent your pulse_entries table from bloating your storage.

Sail (Docker Development Environment)

Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker development environment. It allows you to build and run a professional-grade Laravel application stack—including MySQL, Redis, Meilisearch, and more—without requiring you to install any of these software packages on your local machine.

If you have Docker installed, Sail takes care of the rest, ensuring that every developer on your team is working in an identical environment.

The Sail Stack

Sail uses a docker-compose.yml file (located in your root directory) to define the "services" your application needs.

Service Purpose
laravel.test The main PHP container (running PHP 8.x, Composer, and Node.js).
mysql The database engine for your application.
redis Used for caching, sessions, and queues.
mailpit A local mail server and UI to intercept and view outgoing emails.
selenium Used for running browser tests (Laravel Dusk).

Getting Started

If you are creating a new app, Sail is often installed by default. To add it to an existing project:

  1. Install: composer require laravel/sail --dev
  2. Configure: php artisan sail:install (Select the services you want, like MySQL or Redis).
  3. Launch: ./vendor/bin/sail up (Use -d to run in the background).

Common Sail Commands

Since Sail wraps Docker, you use it to run commands "inside" the container. A good rule of thumb: if you usually run a command with php artisan, you now run it with sail artisan.

Task Command
Start Services sail up
Stop Services sail stop
Artisan Command sail artisan migrate
Composer sail composer install
Node / NPM sail npm run dev
Running Tests sail test

Pro Tip

Add alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail' to your shell configuration (like .zshrc or .bashrc) so you can just type sail instead of the full path.

Customizing your Environment

  • PHP Version: You can change the PHP version by modifying the build context in your docker-compose.yml and rebuilding the image.
  • Dockerfiles: If you need to install specific Linux packages (like libpng for image processing), you can "publish" the Sail Dockerfiles using php artisan sail:publish and edit them directly.
  • Devcontainers: Sail includes support for VS Code Devcontainers, allowing you to run your entire IDE inside the Docker container for a seamless experience.

Sharing Your Site

Sail includes a built-in "Share" feature (powered by Expose). This allows you to generate a public URL for your local site so you can show your progress to a client or test webhooks.

Command: sail share

Note

Performance on Windows/Mac

For the best performance on Windows, you must use WSL2 (Windows Subsystem for Linux) and keep your project files within the Linux filesystem (e.g., ~/code/my-app), not the Windows filesystem (/mnt/c/...).

Warning

Port Conflicts

If you already have MySQL or Redis running natively on your computer, Sail might fail to start because the ports (3306 or 6379) are already taken. You can change the ports Sail uses by setting variables like FORWARD_DB_PORT=3307 in your .env file.

Sanctum (API Token Authentication)

Laravel Sanctum provides a featherweight authentication system for Single Page Applications (SPAs), mobile applications, and simple, token-based APIs. It is the go-to choice for developers who need more than basic session authentication but don't require the full complexity of OAuth2 provided by Laravel Passport.

How Sanctum Works

Sanctum solves two primary problems: API Token Authentication and SPA Session Authentication.

Authentication Method Ideal Use Case Mechanism
API Tokens Mobile Apps, Third-party scripts, Server-to-server. Bearer tokens sent in the Authorization header.
SPA Cookie-based Vue, React, or Next.js apps on the same domain. Secure, HttpOnly cookies (prevents XSS/CSRF).

Issuing API Tokens

To issue tokens, your User model must use the HasApiTokens trait. This allows users to generate multiple tokens with specific "abilities" (permissions).

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

Creating a Token

$token = $user->createToken('mobile-app', ['server:update'])->plainTextToken;

// Return this string to your mobile app/client
return response()->json(['token' => $token]);

Protecting Routes

To protect your API routes, use the auth:sanctum middleware. It automatically checks for either a valid cookie or a valid Bearer token.

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Token Abilities (Permissions)

Sanctum allows you to limit what a token can do. This is great for giving a third-party script access to "read" data but not "delete" it.

Task Code Example
Check Ability $request->user()->tokenCan('server:update')
Revoke Token $user->tokens()->where('id', $id)->delete();
Revoke All $user->tokens()->delete();

SPA Authentication (CSRF Protection)

When building an SPA (like a Nuxt or React app) that lives on a subdomain of your API (e.g., app.myapp.com and api.myapp.com), Sanctum uses Laravel's built-in cookie-based session authentication.

  1. Initialize CSRF: The SPA makes a GET request to /sanctum/csrf-cookie.
  2. Login: The SPA sends credentials to /login.
  3. Encrypted Cookie: Laravel sets a secure cookie. Subsequent requests are authenticated as long as the cookie is sent.

Sanctum vs. Passport

Feature Sanctum Passport
Complexity Simple / Minimalist. Complex / Robust.
OAuth2 Support No. Yes (Full implementation).
Architecture Token or Session based. OAuth2 Grant types (Authorization Code, etc.).
Best For Internal APIs, Mobile apps. Large scale APIs used by external developers.

Note

Statefulness

For SPA authentication to work, you must ensure your SESSION_DOMAIN and SANCTUM_STATEFUL_DOMAINS are correctly configured in your .env file to allow the cookie to be shared between your frontend and backend.

Warning

Token Storage

When using API Tokens in a mobile or desktop app, store them securely (e.g., Keychain on iOS or EncryptedSharedPreferences on Android). In a web browser, do not store plain text tokens in localStorage as they are vulnerable to XSS attacks; use the SPA cookie-based method instead.

Scout (Full-Text Search)

Laravel Scout provides a driver-based solution for adding full-text search to your Eloquent models. Instead of writing complex SQL LIKE queries that are slow and lack "fuzzy" matching, Scout automatically synchronizes your data with a search engine index, allowing for lightning-fast, typo-tolerant searches.

The Search Workflow

Scout works by observing your Eloquent models. Whenever a model is created, updated, or deleted, Scout automatically updates the corresponding search index.

Component Responsibility
Searchable Trait Added to your model to enable search capabilities.
Search Engine The external service or database that stores the searchable index.
Scout Builder A fluent interface to perform searches: Post::search('Laravel')->get().

Supported Engines

Choosing the right engine depends on your project's scale and features like typo tolerance or geo-searching.

Engine Best For Pros Cons
Database Small/Medium apps. No external services; easy setup. No typo tolerance; slower at scale.
Meilisearch Self-hosted production. Open-source; extremely fast; fuzzy search. Requires managing separate server.
Algolia Enterprise / Managed. Zero-config; powerful dashboard; ultra-fast. Can become expensive with high volume.
Typesense Modern, fast apps. Native vector search support; open-source. Newer ecosystem compared to Algolia.

Basic Usage

Making a Model Searchable

Add the Laravel\Scout\Searchable trait and define which data should be indexed using toSearchableArray.

use Laravel\Scout\Searchable;

class Article extends Model
{
    use Searchable;

    /**
     * Get the indexable data array for the model.
     */
    public function toSearchableArray(): array
    {
        return [
            'id'    => (int) $this->id,
            'title' => $this->title,
            'body'  => strip_tags($this->body),
        ];
    }
}

Performing a Search

Searching is as simple as calling the search method on your model. It returns an Eloquent collection of matched models.

$articles = Article::search('Star Wars')->get();

// You can also use where clauses (strict equality only)
$articles = Article::search('Jedi')
    ->where('category_id', 5)
    ->paginate(15);

Key Features

  • Batch Importing: If you have existing data, run php artisan scout:import "App\Models\Article" to sync everything at once.
  • Queued Indexing: Highly recommended for production. By setting 'queue' => true in config/scout.php, indexing happens in the background.
  • Conditional Search: You can control if a model should be searchable based on its state:
public function shouldBeSearchable(): bool
{
    return $this->is_published;
}

Database Engine Strategies

If you use the Database engine, you can choose how it searches your columns:

  • SearchUsingFullText: Uses native database full-text indexes (MySQL/PostgreSQL).
  • SearchUsingPrefix: Searches for terms at the start of strings (e.g., word%).

Note

Hydration

When you perform a search, Scout gets the IDs of the matching records from the search engine and then queries your local database to retrieve the full Eloquent models. This ensures your results are always up-to-date with your actual database records.

Warning

Data Limits

External engines like Algolia have record size limits (typically 10KB to 100KB). Avoid sending entire HTML blobs or massive JSON fields in toSearchableArray. Only index the text that users will actually search for.

Socialite (OAuth Logins)

Laravel Socialite provides an expressive, fluent interface to OAuth authentication with a wide variety of social providers. It handles almost all of the boilerplate social login code, including exchanging authorization codes for access tokens and retrieving user details from the provider.

Supported Providers

Socialite officially supports the most popular platforms out of the box. For others, the community-driven Socialite Providers library adds support for hundreds more (like Slack, Discord, and Spotify).

Official Providers Community Examples (via Socialite Providers)
Google Discord
Facebook Slack
Twitter (X) Microsoft / Azure
GitHub Apple
LinkedIn TikTok

The Authentication Flow

Socialite typically requires two routes: one to redirect the user to the provider, and one to handle the callback after the user has authenticated.

Configuration (config/services.php)

You must add your credentials for each provider you wish to use.

'github' => [
    'client_id' => env('GITHUB_CLIENT_ID'),
    'client_secret' => env('GITHUB_CLIENT_SECRET'),
    'redirect' => 'https://example.com/auth/github/callback',
],

The Controller Logic

use Laravel\Socialite\Facades\Socialite;

class AuthController extends Controller
{
    // Redirect the user to the GitHub authentication page
    public function redirect()
    {
        return Socialite::driver('github')->redirect();
    }

    // Handle the callback from GitHub
    public function callback()
    {
        $githubUser = Socialite::driver('github')->user();

        // Find or create a user in your local database
        $user = User::updateOrCreate([
            'github_id' => $githubUser->id,
        ], [
            'name' => $githubUser->name,
            'email' => $githubUser->email,
            'github_token' => $githubUser->token,
            'github_refresh_token' => $githubUser->refreshToken,
        ]);

        Auth::login($user);

        return redirect('/dashboard');
    }
}

Retrievable User Data

When you call ->user(), Socialite returns a standardized user object regardless of which provider you are using. This makes it easy to switch providers or add new ones without rewriting your logic.

Property Description
getId() The unique ID assigned by the provider.
getNickname() The user's handle/username (if available).
getName() The user's full name.
getEmail() The user's email address.
getAvatar() The URL to the user's profile picture.
token The Access Token used for future API requests to the provider.

Advanced Scopes & Parameters

If you need more than just basic profile data (e.g., access to a user's GitHub repositories or Google Calendar), you can define Scopes.

return Socialite::driver('github')
    ->scopes(['read:user', 'public_repo'])
    ->redirect();

Stateless Authentication

By default, Socialite uses Laravel's session to verify the "state" of the request (to prevent CSRF). If you are building a stateless API (e.g., for a mobile app), you must use the stateless method:

$user = Socialite::driver('google')->stateless()->user();

Note

Handling Email Conflicts

It is common for users to have the same email address across different providers (e.g., the same email for GitHub and Google). You should decide whether to automatically "link" these accounts or require the user to log in with their original method to prevent account hijacking.

Warning

Redirect URLs

The redirect URL in your services.php must exactly match the redirect URI you registered in the provider's developer console (including https:// vs http://). Even a missing trailing slash can cause a "redirect_uri_mismatch" error.

Telescope (Debug Assistant)

Laravel Telescope is an elegant debug assistant for the Laravel framework. It provides insight into the requests coming into your application, exceptions, log entries, database queries, queued jobs, mail, notifications, cache operations, scheduled tasks, variable dumps, and more.

While Pulse (Section 9.9) is designed for production monitoring, Telescope is primarily a local development tool designed to help you understand exactly what happens during a single request or background process.

The Telescope Dashboard

Telescope provides a series of "Watchers" that record information about your application's execution.

Watcher What it Records
Requests Full HTTP request data: headers, payload, session, and response.
Commands Artisan commands executed and their output.
Queries Every SQL query, its execution time, and the code that triggered it.
Exceptions Detailed stack traces and data for every error.
Logs All entries written to your application logs.
Jobs Status and payloads of queued jobs.

Key Features

Request Tagging

Telescope automatically tags entries. For example, it might tag a request with the authenticated user's ID, making it easy to find all logs and queries associated with a specific user's session.

Deep Inspection

Clicking into a request allows you to see everything that happened because of that request: which emails were sent, which jobs were pushed to the queue, and every database query executed.

The "Dump" Screen

If you use the dump() function in your code, the output won't clutter your browser UI. Instead, it appears in real-time on the Telescope "Dumps" tab, keeping your frontend clean.

Installation & Environment

  1. Install: composer require laravel/telescope --dev
  2. Setup: php artisan telescope:install
  3. Migrate: php artisan migrate

Restricting to Local

By default, Telescope only records data in the local environment. If you decide to use it in production, you must secure the dashboard:

// In App\Providers\TelescopeServiceProvider.php
protected function gate(): void
{
    Gate::define('viewTelescope', function ($user) {
        return in_array($user->email, [
            'admin@example.com',
        ]);
    });
}

Managing Data

Telescope records a lot of information, which can fill up your database quickly.

  • Pruning: By default, Telescope entries older than 24 hours are deleted. You can customize this or run the pruning command manually: php artisan telescope:prune
  • Filtering: You can define which entries should be recorded. For example, you might want to ignore 200 OK responses but record all 404 or 500 errors.

Telescope vs. Pulse vs. Debugbar

Tool Focus Use Case
Telescope The "How" Debugging complex flows (Jobs, Mail, Queries) in dev.
Pulse The "Health" Monitoring server vitals and slow routes in production.
Debugbar The "Page" Quick, per-page performance stats at the bottom of the browser.

Note

Watcher Overhead

Each "Watcher" adds a small amount of overhead to your request as it writes to the database. In local development, this is negligible, but it's the reason why Telescope is generally not recommended for high-traffic production environments.

Warning

Sensitive Data

Telescope records request payloads. Ensure you use the Telescope::hideFields(['password', 'card_number']) feature in your TelescopeServiceProvider to prevent sensitive user information from being stored in your debug logs.

DocsAllOver

Where knowledge is just a click away ! DocsAllOver is a one-stop-shop for all your software programming needs, from beginner tutorials to advanced documentation

Get In Touch

We'd love to hear from you! Get in touch and let's collaborate on something great

Copyright copyright © Docsallover - Your One Shop Stop For Documentation