摘要

Laravel 12 作为 PHP 生态系统中最成熟的企业级框架,通过其优雅的架构设计、强大的服务容器和完善的生态系统,为现代 Web 应用开发提供了全方位的解决方案。本教程深入剖析 Laravel 12 的核心技术栈,从服务容器的依赖注入机制、中间件的执行流程、事件系统的异步处理到队列系统的任务调度,全面覆盖企业级应用开发的关键技术点。通过详细的架构设计指南、性能优化策略和实战案例,帮助开发者构建高性能、可扩展、易维护的 Laravel 12 应用,实现从初级开发者到架构师的技术进阶。

Laravel 12 高级实战教程 - 从架构设计到性能优化

前言

欢迎阅读《Laravel 12 高级实战教程》!本教程基于最新的 Laravel 12 版本,旨在帮助开发者深入理解 Laravel 的核心架构设计和高级技术特性,掌握企业级应用开发的最佳实践。Laravel 12 作为 PHP 生态中最成熟的企业级框架,通过其精心设计的服务容器、中间件系统、事件驱动架构和完善的生态系统,为现代 Web 应用开发提供了全方位的解决方案。本教程将从架构设计的角度出发,深入剖析 Laravel 12 的核心技术,帮助开发者构建高性能、可扩展、易维护的企业级应用。

为什么选择 Laravel 12?

Laravel 12 作为 PHP 生态系统中最成熟的企业级框架,通过其精心设计的架构和丰富的企业级特性,为现代 Web 应用开发提供了全方位的解决方案。选择 Laravel 12 的核心优势包括:

  • 优雅的架构设计:基于服务容器和依赖注入的模块化架构,实现了高度的代码组织和复用
  • 强大的服务容器:提供了灵活的依赖注入机制,支持自动解析、绑定上下文和服务提供者
  • 完善的中间件系统:支持请求生命周期的精细控制,实现认证、授权、日志等横切关注点
  • 事件驱动架构:通过事件和监听器实现组件间的解耦,提高系统的可维护性和可扩展性
  • 高性能队列系统:支持多种队列驱动,实现异步任务处理,提高系统响应速度和可靠性
  • 现代化的前端工具链:集成 Vite 构建工具,支持 React、Vue 等现代前端框架
  • 企业级认证授权:提供完整的认证系统、角色权限管理和 API 认证方案
  • 强大的数据库工具:Eloquent ORM、查询构建器、迁移系统和种子器,简化数据库操作
  • 完善的测试工具:集成 PHPUnit 和 Pest,支持单元测试、功能测试和浏览器测试
  • 丰富的生态系统:Laravel Cashier、Passport、 Sanctum 等官方包,覆盖企业应用的各种需求
  • 持续的性能优化:Laravel 12 在路由解析、服务容器、数据库查询等方面进行了深度优化
  • 优秀的可观测性:集成日志、监控和追踪工具,提供全面的系统运行状态 visibility
  • 完善的文档和社区:详尽的官方文档和活跃的全球社区,确保问题快速解决

本教程的目标

通过本教程的学习,您将能够:

  • 掌握 Laravel 12 核心架构:深入理解服务容器、中间件、事件系统等核心组件的工作原理
  • 设计企业级应用架构:运用领域驱动设计思想,构建模块化、可扩展的应用架构
  • 实现高性能 Laravel 应用:掌握数据库优化、缓存策略、队列使用等性能优化技术
  • 构建安全可靠的系统:实现企业级认证授权、数据验证、CSRF 保护等安全措施
  • 开发 RESTful API:设计符合 REST 规范的 API 接口,实现 API 版本控制和文档化
  • 集成现代前端框架:使用 Vite 构建工具,集成 React、Vue 等前端框架
  • 实现持续集成与部署:配置 CI/CD 流水线,实现自动化测试和部署
  • 监控与维护生产系统:搭建日志系统、监控告警,确保系统稳定运行
  • 解决复杂业务场景:运用 Laravel 生态系统,解决企业应用中的复杂业务需求
  • 成为 Laravel 架构师:具备 Laravel 应用架构设计和技术选型的能力

前置知识

本教程面向有一定开发经验的 PHP 开发者,适合具有以下技术背景的读者:

  • 扎实的 PHP 编程基础:熟悉 PHP 8.2+ 语法特性,如类型声明、箭头函数、命名空间等
  • 深入的面向对象编程:理解类、接口、抽象类、 traits 等 OOP 概念
  • 数据库设计经验:熟悉关系型数据库原理,掌握 SQL 查询优化技巧
  • Web 开发基础知识:了解 HTTP 协议、RESTful API 设计、前端基础技术
  • 现代开发工具链:熟悉 Composer、Git、Docker 等开发工具
  • 系统架构概念:了解 MVC 模式、依赖注入、中间件等架构概念

如果您是 Laravel 初学者,建议先完成官方文档的基础教程,掌握 Laravel 的基本使用方法后再开始本教程的学习。

教程结构

本教程采用模块化的结构设计,从核心架构到企业级应用,逐步深入 Laravel 12 的高级特性:

  1. 核心架构解析:深入理解服务容器、中间件、事件系统等核心组件的工作原理
  2. 服务容器与依赖注入:掌握服务容器的高级用法,实现灵活的依赖管理
  3. 中间件系统:深入理解中间件的执行流程,实现自定义中间件和中间件组
  4. 事件驱动架构:设计和实现事件系统,实现组件间的解耦
  5. 路由系统:掌握路由的高级用法,实现路由模型绑定、速率限制等功能
  6. 数据库优化:深入理解 Eloquent ORM,掌握查询优化、索引设计等技术
  7. 缓存策略:实现多级缓存,提高系统性能和响应速度
  8. 队列系统:设计和实现异步任务处理,提高系统可靠性和响应速度
  9. 认证与授权:实现企业级认证授权系统,包括角色权限管理和 API 认证
  10. API 开发:设计和实现 RESTful API,包括版本控制、文档化等
  11. 前端集成:集成现代前端框架,实现前后端分离架构
  12. 性能优化:掌握 Laravel 应用的性能优化策略,包括数据库、缓存、队列等
  13. 部署与监控:实现 CI/CD 流水线,搭建监控告警系统
  14. 企业级应用实战:通过完整的项目案例,综合运用所学知识

现在,让我们开始 Laravel 12 的高级学习之旅吧!

1. Laravel 12 企业级环境搭建

搭建一个稳定、高效的企业级 Laravel 12 开发环境是项目成功的基础。Laravel 12 对环境有严格的要求,同时为了满足企业级应用的性能和可靠性需求,我们需要对环境进行精细化配置。

1.1 系统要求

Laravel 12 需要以下软件和扩展:

  • PHP 8.2+:推荐使用 PHP 8.3 以获得最佳性能
  • Composer 2.5+:PHP 依赖管理工具
  • 数据库:MySQL 8.0+、PostgreSQL 14+、SQLite 3.36+ 或 SQL Server 2019+
  • Web 服务器:Nginx 1.20+ 或 Apache 2.4+
  • 缓存系统:Redis 7.0+ 或 Memcached 1.6+
  • 队列系统:Redis、Beanstalkd、Amazon SQS 或数据库

1.2 PHP 扩展要求

Laravel 12 需要启用以下 PHP 扩展:

  • BCMath
  • Ctype
  • Fileinfo
  • JSON
  • Mbstring
  • OpenSSL
  • PDO
  • Tokenizer
  • XML
  • CURL
  • GD
  • ZIP
  • OPcache
  • Redis(可选,用于缓存和队列)

1.3 环境搭建方案

企业级开发环境推荐使用以下方案:

方案一:Docker 容器化环境

Docker 提供了一致的开发环境,避免了环境差异导致的问题,是企业级开发的首选方案:

1
2
3
4
5
6
7
# 使用 Laravel Sail(Laravel 官方推荐的 Docker 开发环境)
composer create-project laravel/laravel:^12.0 project-name
cd project-name
./vendor/bin/sail up

# 或者使用自定义 Docker Compose 配置
# 创建 docker-compose.yml 文件,配置 Nginx、PHP、MySQL、Redis 等服务

方案二:本地开发环境

对于需要在本地直接运行的开发者,可以使用以下工具:

  • Windows:Laragon、WAMP 或 XAMPP
  • macOS:Homebrew + PHP、MAMP 或 Laravel Valet
  • Linux:直接安装 PHP、Nginx/Apache 和数据库

1.4 环境配置最佳实践

PHP 配置优化

1
2
3
4
5
6
7
8
9
10
11
12
; php.ini 优化配置
memory_limit = 512M
max_execution_time = 60
upload_max_filesize = 10M
post_max_size = 10M
opcache.enable = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 32531
opcache.validate_timestamps = 0
opcache.revalidate_freq = 0
opcache.jit_buffer_size = 128M
opcache.jit = 1255

开发环境与生产环境分离

  • 开发环境:启用调试模式,详细错误信息,自动代码重载
  • 生产环境:禁用调试模式,日志记录,启用 OPcache,优化数据库配置

1.5 环境验证

搭建完成后,使用以下命令验证环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 检查 PHP 版本和扩展
php -v
php -m

# 检查 Composer 版本
composer --version

# 创建 Laravel 项目并验证
composer create-project laravel/laravel:^12.0 test-project
cd test-project
php artisan serve

# 访问 http://localhost:8000 验证安装成功

1.6 企业级环境管理

  • 环境变量管理:使用 .env 文件和环境变量管理不同环境的配置
  • 依赖版本控制:使用 composer.lock 确保依赖版本的一致性
  • 自动化环境搭建:使用脚本或配置管理工具(如 Ansible)自动化环境搭建
  • 容器编排:生产环境使用 Kubernetes 等容器编排工具管理容器

现在,我们已经搭建了一个企业级的 Laravel 12 开发环境,接下来我们将学习 Laravel 12 的核心架构设计。

Windows 系统

  1. 使用 XAMPP

    • 访问 XAMPP 官网 下载最新版本的 XAMPP
    • 运行安装程序,选择安装 PHP 8.2 或更高版本
    • 安装完成后,启动 XAMPP 控制面板,启动 Apache 服务
  2. 使用 WAMP

    • 访问 WAMP 官网 下载最新版本的 WAMP
    • 运行安装程序,选择安装 PHP 8.2 或更高版本
    • 安装完成后,启动 WAMP 服务

macOS 系统

  1. 使用 Homebrew

    1
    2
    3
    4
    5
    # 安装 Homebrew(如果未安装)
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

    # 安装 PHP
    brew install php
  2. 使用 MAMP

    • 访问 MAMP 官网 下载最新版本的 MAMP
    • 运行安装程序,选择安装 PHP 8.2 或更高版本
    • 安装完成后,启动 MAMP 服务

Linux 系统

  1. Ubuntu/Debian

    1
    2
    3
    4
    5
    # 更新包列表
    sudo apt update

    # 安装 PHP 8.2 及所需扩展
    sudo apt install php8.2 php8.2-cli php8.2-mbstring php8.2-xml php8.2-mysql php8.2-curl php8.2-gd php8.2-zip
  2. CentOS/RHEL

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 安装 EPEL 仓库
    sudo yum install epel-release

    # 安装 REMI 仓库
    sudo yum install https://rpms.remirepo.net/enterprise/remi-release-8.rpm

    # 启用 PHP 8.2 模块
    sudo dnf module enable php:remi-8.2

    # 安装 PHP 及所需扩展
    sudo yum install php php-cli php-mbstring php-xml php-mysqlnd php-curl php-gd php-zip

1.2 Laravel 12 Composer 安装

Composer 是 PHP 的依赖管理工具,Laravel 12 依赖于 Composer 来管理其依赖项。确保安装最新版本的 Composer 以获得最佳的 Laravel 12 开发体验。

Windows 系统

  1. 访问 Composer 官网 下载 Composer-Setup.exe
  2. 运行安装程序,按照提示完成安装
  3. 安装完成后,打开命令提示符,运行 composer --version 验证安装成功

macOS/Linux 系统

1
2
3
4
5
6
7
8
# 下载并安装 Composer
curl -sS https://getcomposer.org/installer | php

# 移动到全局路径
sudo mv composer.phar /usr/local/bin/composer

# 验证安装成功
composer --version

安装完成后,Composer 将成为 Laravel 12 项目管理的重要工具,用于安装依赖、创建项目和更新框架。

1.3 Laravel 12 安装

Laravel 12 提供了多种安装方式,我们推荐使用 Composer 或 Laravel Installer。

使用 Composer 创建 Laravel 12 项目

1
2
3
4
5
# 创建 Laravel 12 项目
composer create-project laravel/laravel:^12.0 project-name

# 进入项目目录
cd project-name

使用 Laravel Installer 安装 Laravel 12

  1. 安装 Laravel Installer

    1
    composer global require laravel/installer
  2. 添加 Composer 全局目录到 PATH

    • Windows:将 %APPDATA%\Composer\vendor\bin 添加到系统环境变量 PATH
    • macOS/Linux:将 ~/.composer/vendor/bin 添加到 ~/.bashrc~/.zshrc 文件中
  3. 创建 Laravel 12 项目

    1
    2
    3
    4
    laravel new project-name

    # 进入项目目录
    cd project-name

Laravel 12 安装完成后,您就可以开始构建现代化的 Web 应用了。

1.4 Laravel 12 验证安装

安装完成后,我们可以通过以下命令验证 Laravel 12 项目是否创建成功:

1
2
# 启动 Laravel 12 开发服务器
php artisan serve

然后在浏览器中访问 http://localhost:8000,如果看到 Laravel 12 的欢迎页面,则说明安装成功。Laravel 12 的欢迎页面通常会显示当前 Laravel 版本信息和一些快速开始的链接。

1.5 Laravel 12 数据库配置

Laravel 12 默认使用 MySQL 作为数据库,但也支持 PostgreSQL、SQLite 等其他数据库。我们需要配置数据库连接以确保 Laravel 12 应用能够正常访问数据库。

  1. 创建数据库

    • 使用 MySQL 命令行或 phpMyAdmin 创建一个新的数据库
  2. 配置 Laravel 12 数据库连接

    • 打开 .env 文件,修改数据库配置:
    1
    2
    3
    4
    5
    6
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=your_database_name
    DB_USERNAME=your_database_username
    DB_PASSWORD=your_database_password

Laravel 12 数据库配置完成后,您就可以开始使用 Laravel 的数据库功能了,包括数据库迁移、Eloquent ORM 和查询构建器等。

现在,我们的 Laravel 12 开发环境已经搭建完成,接下来我们将创建一个新的 Laravel 12 项目并了解其目录结构。

2. Laravel 12 项目初始化和目录结构

2.1 Laravel 12 项目创建

我们已经在环境搭建章节中介绍了两种创建 Laravel 12 项目的方法,这里再简要回顾一下:

使用 Composer 创建 Laravel 12 项目

1
2
3
4
5
# 创建 Laravel 12 项目
composer create-project laravel/laravel:^12.0 project-name

# 进入项目目录
cd project-name

使用 Laravel Installer 创建 Laravel 12 项目

1
2
3
4
5
# 创建 Laravel 12 项目
laravel new project-name

# 进入项目目录
cd project-name

2.2 Laravel 12 目录结构

Laravel 12 项目的目录结构组织得非常清晰,遵循了现代 PHP 应用的最佳实践。下面是 Laravel 12 的主要目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
project-name/
├── app/ # Laravel 12 应用核心代码
│ ├── Console/ # Laravel 12 命令行工具
│ ├── Exceptions/ # Laravel 12 异常处理
│ ├── Http/ # Laravel 12 HTTP 相关代码
│ │ ├── Controllers/ # Laravel 12 控制器
│ │ ├── Middleware/ # Laravel 12 中间件
│ │ └── Requests/ # Laravel 12 请求验证
│ ├── Models/ # Laravel 12 数据模型
│ ├── Providers/ # Laravel 12 服务提供者
│ └── View/ # Laravel 12 视图相关代码
├── bootstrap/ # Laravel 12 引导文件
├── config/ # Laravel 12 配置文件
├── database/ # Laravel 12 数据库相关文件
│ ├── factories/ # Laravel 12 数据工厂
│ ├── migrations/ # Laravel 12 数据库迁移
│ └── seeders/ # Laravel 12 数据填充
├── public/ # Laravel 12 公共目录(网站根目录)
│ ├── assets/ # Laravel 12 静态资源
│ └── index.php # Laravel 12 入口文件
├── resources/ # Laravel 12 资源文件
│ ├── css/ # Laravel 12 CSS 文件
│ ├── js/ # Laravel 12 JavaScript 文件
│ └── views/ # Laravel 12 视图文件
├── routes/ # Laravel 12 路由文件
├── storage/ # Laravel 12 存储目录
│ ├── app/ # Laravel 12 应用存储
│ ├── framework/ # Laravel 12 框架存储
│ └── logs/ # Laravel 12 日志文件
├── tests/ # Laravel 12 测试文件
├── vendor/ # Laravel 12 依赖包
├── .env # Laravel 12 环境变量
├── .env.example # Laravel 12 环境变量示例
├── composer.json # Laravel 12 Composer 配置
├── package.json # Laravel 12 NPM 配置
├── phpunit.xml # Laravel 12 PHPUnit 配置
├── vite.config.js # Laravel 12 Vite 配置
└── artisan # Laravel 12 Artisan 命令行工具

Laravel 12 的目录结构设计合理,便于开发者组织和管理代码,同时也符合现代 PHP 应用的最佳实践。

2.3 Laravel 12 重要目录和文件详解

app 目录

app 目录包含了 Laravel 12 应用的核心代码,是我们开发的主要场所。

  • Console/:包含 Laravel 12 自定义的 Artisan 命令
  • Exceptions/:包含 Laravel 12 异常处理类
  • Http/:包含 Laravel 12 HTTP 相关的代码,如控制器、中间件和请求验证
  • Models/:包含 Laravel 12 Eloquent 模型类
  • Providers/:包含 Laravel 12 服务提供者类,用于注册服务和引导应用
  • View/:包含 Laravel 12 视图相关的代码,如视图组件

config 目录

config 目录包含了 Laravel 12 应用的配置文件,如数据库配置、邮件配置、缓存配置等。

database 目录

database 目录包含了 Laravel 12 数据库相关的文件:

  • factories/:包含 Laravel 12 数据工厂类,用于生成测试数据
  • migrations/:包含 Laravel 12 数据库迁移文件,用于定义数据库表结构
  • seeders/:包含 Laravel 12 数据填充类,用于填充初始数据

public 目录

public 目录是 Laravel 12 网站的根目录,包含了入口文件 index.php 和静态资源。

resources 目录

resources 目录包含了 Laravel 12 应用的资源文件:

  • css/:包含 Laravel 12 CSS 文件
  • js/:包含 Laravel 12 JavaScript 文件
  • views/:包含 Laravel 12 Blade 模板文件

routes 目录

routes 目录包含了 Laravel 12 应用的路由定义文件:

  • web.php:定义 Laravel 12 Web 路由
  • api.php:定义 Laravel 12 API 路由
  • console.php:定义 Laravel 12 控制台路由
  • channels.php:定义 Laravel 12 广播频道

storage 目录

storage 目录包含了 Laravel 12 应用的存储文件:

  • app/:用于存储 Laravel 12 应用生成的文件
  • framework/:用于存储 Laravel 12 框架生成的文件,如缓存
  • logs/:用于存储 Laravel 12 日志文件

vendor 目录

vendor 目录包含了 Laravel 12 Composer 安装的依赖包。

.env 文件

.env 文件包含了 Laravel 12 应用的环境变量,如数据库连接信息、邮件配置等。这个文件不应该提交到版本控制系统中。

artisan 文件

artisan 是 Laravel 12 的命令行工具,用于执行各种命令,如生成控制器、运行迁移等。

2.4 Laravel 12 配置文件

Laravel 12 的配置文件位于 config 目录中,采用 PHP 数组格式,易于理解和修改。下面是一些 Laravel 12 重要的配置文件:

config/app.php

Laravel 12 应用的主要配置文件,包含应用名称、时区、语言等配置。

config/database.php

Laravel 12 数据库配置文件,包含数据库连接信息。

config/mail.php

Laravel 12 邮件配置文件,包含邮件发送配置。

config/cache.php

Laravel 12 缓存配置文件,包含缓存驱动配置。

config/session.php

Laravel 12 会话配置文件,包含会话驱动配置。

Laravel 12 的配置文件设计合理,便于开发者根据项目需求进行定制和修改。

2.5 Laravel 12 环境变量

Laravel 12 使用 .env 文件来存储环境变量,这些变量可以在配置文件中使用,便于在不同环境中使用不同的配置。下面是一些 Laravel 12 重要的环境变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Laravel 12 应用配置
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost

# Laravel 12 数据库配置
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

# Laravel 12 邮件配置
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"

Laravel 12 的环境变量设计使得应用在不同环境(如开发、测试、生产)中可以使用不同的配置,提高了应用的可移植性和安全性。

2.6 Laravel 12 Artisan 命令行工具

Artisan 是 Laravel 12 提供的命令行工具,非常强大和灵活,是 Laravel 开发中的重要工具。下面是一些 Laravel 12 常用的 Artisan 命令:

查看所有 Laravel 12 命令

1
php artisan list

生成 Laravel 12 控制器

1
php artisan make:controller ControllerName

生成 Laravel 12 模型

1
php artisan make:model ModelName

生成 Laravel 12 迁移

1
php artisan make:migration create_table_name

运行 Laravel 12 迁移

1
php artisan migrate

生成 Laravel 12 数据填充

1
php artisan make:seeder SeederName

运行 Laravel 12 数据填充

1
php artisan db:seed

启动 Laravel 12 开发服务器

1
php artisan serve

Laravel 12 Artisan 命令行工具提供了丰富的命令,大大提高了开发效率,是 Laravel 12 开发中不可或缺的工具。

2.7 Laravel 12 项目初始化示例

现在,让我们创建一个简单的 Laravel 12 项目,来熟悉一下整个流程:

  1. 创建 Laravel 12 项目

    1
    2
    composer create-project laravel/laravel:^12.0 blog
    cd blog
  2. 启动 Laravel 12 开发服务器

    1
    php artisan serve
  3. 访问 Laravel 12 应用
    在浏览器中访问 http://localhost:8000,你应该能看到 Laravel 12 的欢迎页面。

  4. 查看 Laravel 12 目录结构
    浏览 Laravel 12 项目的目录结构,了解各个目录和文件的作用。

通过这个简单的 Laravel 12 项目初始化示例,你可以快速熟悉 Laravel 12 的基本使用流程,为后续的学习打下基础。

现在,我们已经了解了 Laravel 12 项目的初始化过程和目录结构,接下来我们将学习 Laravel 12 的路由系统和控制器。

3. Laravel 12 路由和控制器

3.1 Laravel 12 路由基础

路由是 Laravel 12 应用的入口点,它定义了 URL 如何映射到应用的功能。Laravel 12 的路由定义在 routes 目录下的文件中,主要包括 web.phpapi.php。Laravel 12 的路由系统非常强大,支持多种路由类型和参数。

Laravel 12 基本路由

在 Laravel 12 的 routes/web.php 文件中,我们可以定义基本的路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use Illuminate\Support\Facades\Route;

// Laravel 12 基本 GET 路由
Route::get('/', function () {
return view('welcome');
});

// Laravel 12 基本 POST 路由
Route::post('/submit', function () {
// 处理表单提交
});

// Laravel 12 基本 PUT 路由
Route::put('/update/{id}', function ($id) {
// 更新资源
});

// Laravel 12 基本 DELETE 路由
Route::delete('/delete/{id}', function ($id) {
// 删除资源
});

Laravel 12 支持多种 HTTP 请求方法的路由,包括 GET、POST、PUT、DELETE 等,满足不同场景的需求。

Laravel 12 路由参数

我们可以在 Laravel 12 路由中定义参数,这些参数会传递给路由处理函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Laravel 12 必选路由参数
Route::get('/user/{id}', function ($id) {
return 'User ' . $id;
});

// Laravel 12 可选路由参数
Route::get('/user/{name?}', function ($name = 'Guest') {
return 'Hello ' . $name;
});

// Laravel 12 带正则表达式约束的路由参数
Route::get('/user/{id}', function ($id) {
return 'User ' . $id;
})->where('id', '[0-9]+');

// Laravel 12 全局参数约束
// 在 RouteServiceProvider 中定义

Laravel 12 的路由参数系统非常灵活,支持必选参数、可选参数和带正则表达式约束的参数,满足各种复杂场景的需求。

Laravel 12 命名路由

我们可以为 Laravel 12 路由指定一个名称,这样在生成 URL 或重定向时可以使用这个名称,提高代码的可维护性:

1
2
3
4
5
6
7
8
9
10
// Laravel 12 命名路由定义
Route::get('/user/{id}', function ($id) {
return 'User ' . $id;
})->name('user.show');

// Laravel 12 使用命名路由生成 URL
$url = route('user.show', ['id' => 1]);

// Laravel 12 使用命名路由重定向
return redirect()->route('user.show', ['id' => 1]);

Laravel 12 的命名路由系统使得 URL 生成和重定向更加灵活和可维护,特别是在路由 URL 发生变化时,只需要修改路由定义,而不需要修改所有使用该路由的地方。

Laravel 12 路由组

Laravel 12 路由组允许我们为一组路由共享属性,如中间件、命名空间、前缀等,减少代码重复:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Laravel 12 带前缀的路由组
Route::prefix('admin')->group(function () {
Route::get('/users', function () {
return 'Admin Users';
});
Route::get('/posts', function () {
return 'Admin Posts';
});
});

// Laravel 12 带命名空间的路由组
Route::namespace('Admin')->group(function () {
// 控制器在 App\Http\Controllers\Admin 命名空间下
Route::get('/admin/users', 'UserController@index');
});

// Laravel 12 带中间件的路由组
Route::middleware('auth')->group(function () {
Route::get('/dashboard', function () {
return 'Dashboard';
});
});

Laravel 12 的路由组系统非常强大,可以组合使用多种属性,如同时指定前缀、命名空间和中间件,使路由定义更加清晰和简洁。

3.2 Laravel 12 控制器

控制器是 Laravel 12 中处理 HTTP 请求的类,它们将请求逻辑组织成可维护的方法。Laravel 12 控制器位于 app/Http/Controllers 目录中,是 MVC 架构中的重要组成部分。

创建 Laravel 12 控制器

我们可以使用 Laravel 12 Artisan 命令生成控制器:

1
2
3
4
5
6
7
8
# 创建 Laravel 12 基本控制器
php artisan make:controller UserController

# 创建 Laravel 12 资源控制器
php artisan make:controller PostController --resource

# 创建 Laravel 12 带有模型的资源控制器
php artisan make:controller CommentController --resource --model=Comment

Laravel 12 提供了多种控制器生成方式,满足不同场景的需求,提高开发效率。

Laravel 12 基本控制器

一个 Laravel 12 基本控制器如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
public function index()
{
// Laravel 12 显示用户列表
}

public function show($id)
{
// Laravel 12 显示单个用户
}

public function create()
{
// Laravel 12 显示创建用户的表单
}

public function store(Request $request)
{
// Laravel 12 存储新用户
}

public function edit($id)
{
// Laravel 12 显示编辑用户的表单
}

public function update(Request $request, $id)
{
// Laravel 12 更新用户
}

public function destroy($id)
{
// Laravel 12 删除用户
}
}

Laravel 12 基本控制器提供了一种组织请求处理逻辑的方式,将相关的方法集中在一个类中,提高代码的可维护性和可读性。

Laravel 12 资源控制器

Laravel 12 资源控制器是一种特殊的控制器,它为典型的 CRUD 操作提供了预设的方法,减少重复代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
/**
* Laravel 12 显示所有帖子
*/
public function index()
{
$posts = Post::all();
return view('posts.index', compact('posts'));
}

/**
* Laravel 12 显示创建帖子的表单
*/
public function create()
{
return view('posts.create');
}

/**
* Laravel 12 存储新帖子
*/
public function store(Request $request)
{
$post = Post::create($request->all());
return redirect()->route('posts.show', $post->id);
}

/**
* Laravel 12 显示单个帖子
*/
public function show(Post $post)
{
return view('posts.show', compact('post'));
}

/**
* Laravel 12 显示编辑帖子的表单
*/
public function edit(Post $post)
{
return view('posts.edit', compact('post'));
}

/**
* Laravel 12 更新帖子
*/
public function update(Request $request, Post $post)
{
$post->update($request->all());
return redirect()->route('posts.show', $post->id);
}

/**
* Laravel 12 删除帖子
*/
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('posts.index');
}
}

Laravel 12 资源控制器自动为 CRUD 操作生成预设方法,包括 index、create、store、show、edit、update 和 destroy,大大减少了重复代码,提高了开发效率。

3.3 Laravel 12 路由与控制器结合

我们可以将 Laravel 12 路由指向控制器的方法,而不是使用闭包函数,这样可以更好地组织代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use App\Http\Controllers\UserController;

// Laravel 12 基本路由指向控制器方法
Route::get('/users', [UserController::class, 'index']);
Route::get('/users/{id}', [UserController::class, 'show']);

// Laravel 12 资源路由
Route::resource('posts', 'PostController');

// Laravel 12 部分资源路由
Route::resource('posts', 'PostController')->only([
'index', 'show'
]);

Route::resource('posts', 'PostController')->except([
'create', 'store', 'update', 'destroy'
]);

// Laravel 12 API 资源路由(无创建和编辑方法)
Route::apiResource('products', 'ProductController');

Laravel 12 的路由与控制器结合使用,使得代码结构更加清晰,职责更加分明,提高了代码的可维护性和可读性。

3.4 Laravel 12 路由模型绑定

Laravel 12 路由模型绑定允许我们自动将路由参数绑定到对应的模型实例,减少重复代码,提高开发效率:

Laravel 12 隐式路由模型绑定

1
2
3
4
5
6
7
8
9
// Laravel 12 路由定义
Route::get('/posts/{post}', [PostController::class, 'show']);

// Laravel 12 控制器方法
public function show(Post $post)
{
// Laravel 12 会自动查找 ID 为 $post 的 Post 模型实例
return view('posts.show', compact('post'));
}

Laravel 12 显式路由模型绑定

我们可以在 Laravel 12 的 RouteServiceProvider 中定义显式绑定:

1
2
3
4
5
6
7
8
9
10
11
12
// 在 Laravel 12 RouteServiceProvider 的 boot 方法中
public function boot()
{
parent::boot();

Route::model('user', App\Models\User::class);
}

// 然后在 Laravel 12 路由中使用
Route::get('/users/{user}', function (App\Models\User $user) {
return $user;
});

Laravel 12 的路由模型绑定系统使得控制器代码更加简洁,减少了手动查找模型实例的代码,提高了代码的可读性和可维护性。

3.5 Laravel 12 中间件

Laravel 12 中间件是处理 HTTP 请求的过滤器,它们可以在请求到达控制器之前或之后执行操作,用于实现认证、日志记录、CSRF 保护等功能。

Laravel 12 应用中间件到路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Laravel 12 单个路由
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware('auth');

// Laravel 12 路由组
Route::middleware(['auth', 'admin'])->group(function () {
Route::get('/admin/dashboard', function () {
return view('admin.dashboard');
});
});

// Laravel 12 控制器
class UserController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('admin')->only('index');
$this->middleware('guest')->except('index');
}
}

Laravel 12 中间件系统非常灵活,可以应用于单个路由、路由组或整个控制器,也可以通过 onlyexcept 方法指定中间件应用于控制器的哪些方法。

3.6 Laravel 12 路由和控制器实战示例

现在,让我们创建一个简单的 Laravel 12 博客应用,来演示路由和控制器的使用:

  1. 创建 Laravel 12 控制器

    1
    php artisan make:controller PostController --resource
  2. 定义 Laravel 12 资源路由
    routes/web.php 文件中:

    1
    2
    3
    use App\Http\Controllers\PostController;

    Route::resource('posts', PostController::class);
  3. 实现 Laravel 12 控制器方法
    app/Http/Controllers/PostController.php 文件中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    namespace App\Http\Controllers;

    use Illuminate\Http\Request;

    class PostController extends Controller
    {
    public function index()
    {
    return view('posts.index');
    }

    public function create()
    {
    return view('posts.create');
    }

    public function store(Request $request)
    {
    // Laravel 12 这里我们暂时只做重定向
    return redirect()->route('posts.index');
    }

    public function show($id)
    {
    return view('posts.show', ['id' => $id]);
    }

    public function edit($id)
    {
    return view('posts.edit', ['id' => $id]);
    }

    public function update(Request $request, $id)
    {
    // Laravel 12 这里我们暂时只做重定向
    return redirect()->route('posts.show', $id);
    }

    public function destroy($id)
    {
    // Laravel 12 这里我们暂时只做重定向
    return redirect()->route('posts.index');
    }
    }
  4. 创建 Laravel 12 视图文件
    我们将在后面的章节中创建视图文件。

通过这个 Laravel 12 路由和控制器实战示例,你可以了解如何在实际项目中使用 Laravel 12 的路由和控制器,为后续的学习打下基础。

3.7 Laravel 12 路由缓存

对于 Laravel 12 生产环境,我们可以缓存路由以提高性能,减少路由解析时间:

1
2
3
4
5
# 生成 Laravel 12 路由缓存
php artisan route:cache

# 清除 Laravel 12 路由缓存
php artisan route:clear

Laravel 12 路由缓存功能可以显著提高应用的性能,特别是在路由数量较多的情况下。但需要注意的是,每次修改路由后都需要重新生成路由缓存。

3.8 Laravel 12 路由列表

我们可以使用 Laravel 12 Artisan 命令查看应用的所有路由,了解路由的定义和映射关系:

1
php artisan route:list

Laravel 12 路由列表命令会显示应用中所有已定义的路由,包括路由方法、URI、名称、对应的控制器方法等信息,是开发过程中非常有用的工具。

现在,我们已经了解了 Laravel 12 的路由系统和控制器,接下来我们将学习 Laravel 12 的视图系统。

4. Laravel 12 视图

4.1 Laravel 12 视图基础

视图是 Laravel 12 应用的用户界面,Laravel 12 使用 Blade 模板引擎来渲染视图。Laravel 12 视图文件位于 resources/views 目录中,使用 .blade.php 扩展名。Blade 模板引擎提供了简洁的语法和强大的功能,是 Laravel 12 视图系统的核心。

Laravel 12 创建视图

我们可以在 Laravel 12 的 resources/views 目录中创建视图文件,使用 .blade.php 扩展名:

1
2
3
4
5
# 创建 Laravel 12 简单视图
resources/views/welcome.blade.php

# 创建 Laravel 12 嵌套目录视图
resources/views/posts/index.blade.php

Laravel 12 支持嵌套目录结构来组织视图文件,这样可以更好地管理复杂应用的视图。

Laravel 12 渲染视图

在 Laravel 12 控制器中,我们可以使用 view 辅助函数来渲染视图,传递数据到视图:

1
2
3
4
5
6
7
8
9
10
11
12
// Laravel 12 基本渲染
return view('welcome');

// Laravel 12 渲染嵌套目录视图
return view('posts.index');

// Laravel 12 传递数据到视图
return view('welcome', ['name' => 'John']);

// Laravel 12 使用 compact 传递数据
$name = 'John';
return view('welcome', compact('name'));

Laravel 12 提供了多种渲染视图的方式,包括基本渲染、嵌套目录视图渲染和数据传递,满足不同场景的需求。

4.2 Laravel 12 Blade 模板引擎

Blade 是 Laravel 12 提供的强大模板引擎,它提供了简洁的语法和丰富的功能,是 Laravel 12 视图系统的核心组件。Laravel 12 的 Blade 模板引擎支持模板继承、包含、条件语句、循环等多种功能,使视图开发更加高效和灵活。

Laravel 12 Blade 模板引擎基本语法

Laravel 12 Blade 模板引擎提供了简洁的语法和强大的功能,以下是常用的基本语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{{-- Laravel 12 Blade 注释 --}}

{{-- Laravel 12 输出变量(自动转义) --}}
Hello, {{ $name }}!

{{-- Laravel 12 输出变量(不转义) --}}
{!! $html !!}

{{-- Laravel 12 控制结构 --}}
@if (count($posts) > 0)
There are {{ count($posts) }} posts.
@else
There are no posts.
@endif

{{-- Laravel 12 循环 --}}
@foreach ($posts as $post)
<p>{{ $post->title }}</p>
@endforeach

@for ($i = 0; $i < 10; $i++)
<p>Item {{ $i }}</p>
@endfor

@while (true)
<p>Loop forever</p>
@endwhile

{{-- Laravel 12 包含子视图 --}}
@include('partials.header')

{{-- Laravel 12 继承布局 --}}
@extends('layouts.app')

@section('content')
<p>This is the content section</p>
@endsection

Laravel 12 Blade 模板引擎的基本语法简洁明了,支持注释、变量输出、控制结构、循环、子视图包含和布局继承等功能,使视图开发更加高效和灵活。

Laravel 12 Blade 指令

Laravel 12 Blade 提供了许多有用的指令,简化视图开发:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{{-- Laravel 12 条件指令 --}}
@if (condition)
// 内容
@elseif (anotherCondition)
// 内容
@else
// 内容
@endif

{{-- Laravel 12 身份验证指令 --}}
@auth
// 已登录用户可见
@endauth

@guest
// 未登录用户可见
@endguest

{{-- Laravel 12 环境指令 --}}
@env('local')
// 本地环境可见
@endenv

{{-- Laravel 12 推送到栈 --}}
@push('scripts')
<script src="/js/script.js"></script>
@endpush

{{-- Laravel 12 栈输出 --}}
@stack('scripts')

{{-- Laravel 12 组件 --}}
<x-alert type="error" :message="$message" />

{{-- Laravel 12 插槽 --}}
<x-card>
<x-slot name="header">
Card Header
</x-slot>

Card Content

<x-slot name="footer">
Card Footer
</x-slot>
</x-card>

Laravel 12 Blade 指令提供了丰富的功能,包括条件判断、身份验证状态检查、环境检测、资源管理和组件系统等,使视图开发更加简洁和高效。

Laravel 12 自定义指令

Laravel 12 允许我们在 AppServiceProvider 中定义自定义 Blade 指令,扩展 Blade 模板引擎的功能:

1
2
3
4
5
6
7
8
9
10
11
// 在 Laravel 12 AppServiceProvider 的 boot 方法中
public function boot()
{
// 定义 Laravel 12 自定义指令
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
}

// 在 Laravel 12 视图中使用
@datetime($date)

Laravel 12 自定义指令功能非常强大,可以根据项目需求创建各种自定义指令,简化视图代码,提高开发效率。

4.3 Laravel 12 布局

Laravel 12 布局允许我们创建一致的页面结构,减少代码重复,提高视图的可维护性。Laravel 12 布局使用 Blade 模板引擎的 @extends@yield 指令来实现,是 Laravel 12 视图系统的重要组成部分。

Laravel 12 创建布局

我们可以在 Laravel 12 中创建布局文件,定义一致的页面结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{{-- Laravel 12 布局文件: resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title', 'Laravel 12 App')</title>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
@stack('styles')
</head>
<body>
<header>
<nav>
<!-- Laravel 12 导航菜单 -->
</nav>
</header>

<main>
@yield('content')
</main>

<footer>
<!-- Laravel 12 页脚内容 -->
</footer>

<script src="{{ asset('js/app.js') }}"></script>
@stack('scripts')
</body>
</html>

Laravel 12 布局文件使用 @yield 指令定义内容区域,使用 @stack 指令定义可堆叠的资源区域,使页面结构更加清晰和可维护。

Laravel 12 使用布局

我们可以在 Laravel 12 视图中使用布局,继承一致的页面结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{{-- Laravel 12 视图文件: resources/views/posts/index.blade.php --}}
@extends('layouts.app')

@section('title', 'Posts')

@push('styles')
<link rel="stylesheet" href="{{ asset('css/posts.css') }}">
@endpush

@section('content')
<h1>Posts</h1>

@foreach ($posts as $post)
<div class="post">
<h2>{{ $post->title }}</h2>
<p>{{ $post->content }}</p>
</div>
@endforeach
@endsection

@push('scripts')
<script src="{{ asset('js/posts.js') }}"></script>
@endpush

Laravel 12 使用布局的方式非常简单,通过 @extends 指令继承布局文件,通过 @section 指令填充内容区域,通过 @push 指令添加页面特定的资源,使视图开发更加模块化和可维护。

4.4 Laravel 12 组件系统

Laravel 12 组件系统是现代前端开发理念在后端框架中的完美体现,通过封装可重用的视图片段,实现了视图层的模块化和组件化开发。Laravel 12 组件系统支持类组件和匿名组件两种模式,提供了完整的属性传递、插槽系统和生命周期钩子,为复杂应用的视图层开发提供了强大的支持。

Laravel 12 组件类型与架构

Laravel 12 提供了两种类型的组件实现方式,每种都有其特定的使用场景和优势:

  1. 类组件:基于 PHP 类的组件实现,支持完整的逻辑处理和生命周期钩子
  2. 匿名组件:纯 Blade 模板实现的组件,适用于简单的展示型组件

Laravel 12 创建组件

我们可以使用 Laravel 12 Artisan 命令生成组件,创建可重用的视图片段:

1
2
3
4
5
6
7
8
# 创建 Laravel 12 类组件
php artisan make:component Alert

# 创建 Laravel 12 嵌套组件
php artisan make:component Forms/Input

# 创建 Laravel 12 匿名组件(手动创建文件)
# resources/views/components/alert.blade.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

Laravel 12 提供了便捷的 Artisan 命令来生成组件,支持基本组件和嵌套组件,使组件开发更加高效和规范化。

#### Laravel 12 组件结构与高级特性

Laravel 12 组件系统采用了现代化的组件架构,由组件类和组件视图两部分组成,支持完整的属性类型声明、默认值设置、插槽系统和生命周期钩子。

##### 1. 组件视图结构

```php
{{-- Laravel 12 组件视图: resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type }}" role="alert">
@if (isset($icon))
<i class="{{ $icon }}"></i>
@endif
<div class="alert-content">
@if (isset($title))
<h4 class="alert-title">{{ $title }}</h4>
@endif
<div class="alert-message">{{ $slot }}</div>
</div>
@if (isset($dismissible) && $dismissible)
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
@endif
</div>
2. 组件属性与类型声明

Laravel 12 组件支持类型声明和默认值设置,提高代码的可读性和可靠性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Laravel 12 组件类: app/View/Components/Alert.php
namespace App\View\Components;

use Illuminate\View\Component;

class Alert extends Component
{
/**
* 组件类型
*/
public string $type;

/**
* 是否可关闭
*/
public bool $dismissible;

/**
* 图标类名
*/
public ?string $icon;

/**
* 标题
*/
public ?string $title;

/**
* 创建组件实例
*/
public function __construct(
string $type = 'info',
bool $dismissible = false,
?string $icon = null,
?string $title = null
) {
$this->type = $type;
$this->dismissible = $dismissible;
$this->icon = $icon;
$this->title = $title;
}

/**
* 渲染组件
*/
public function render()
{
return view('components.alert');
}
}
3. 组件高级特性

Laravel 12 组件系统还支持以下高级特性:

  • 动态组件:根据运行时数据动态渲染不同的组件
  • 组件组合:将多个组件组合成更复杂的组件
  • 组件缓存:缓存组件渲染结果提高性能
  • 组件事件:通过事件系统实现组件间通信

Laravel 12 组件结构设计合理,支持传递属性、插槽和复杂逻辑,使组件更加灵活和可重用,是构建现代化前端界面的强大工具。

Laravel 12 组件使用与最佳实践

Laravel 12 组件的使用方式非常灵活,支持多种传递属性和内容的方式,同时提供了丰富的最佳实践来确保组件的可维护性和可扩展性。

1. 组件基本使用
1
2
3
4
5
6
7
8
9
10
11
12
{{-- Laravel 12 组件基本使用 - 使用属性传递数据 --}}
<x-alert type="success" title="操作成功" icon="fas fa-check-circle">
操作已成功完成!
</x-alert>

{{-- Laravel 12 组件使用 - 布尔属性简写 --}}
<x-alert type="warning" dismissible>
此操作需要注意!
</x-alert>

{{-- Laravel 12 组件使用 - 数组属性 --}}
<x-form-input name="user" :options="['name' => '用户名', 'email' => '邮箱']" />
2. 高级插槽用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{{-- Laravel 12 组件使用 - 具名插槽 --}}
<x-card>
<x-slot name="header">
<div class="flex justify-between items-center">
<h3>用户信息</h3>
<button class="btn btn-sm btn-outline-secondary">编辑</button>
</div>
</x-slot>

<x-slot name="body">
<div class="user-profile">
<img src="{{ $user->avatar }}" alt="用户头像" class="avatar">
<div class="user-details">
<p><strong>姓名:</strong>{{ $user->name }}</p>
<p><strong>邮箱:</strong>{{ $user->email }}</p>
</div>
</div>
</x-slot>

<x-slot name="footer">
<div class="flex justify-end">
<button class="btn btn-primary mr-2">保存</button>
<button class="btn btn-outline-secondary">取消</button>
</div>
</x-slot>
</x-card>
3. 组件使用最佳实践
  1. 组件命名规范:使用小写字母和连字符命名组件,如 alert, form-input
  2. 组件职责单一:每个组件只负责一个特定的功能,保持组件的简洁性
  3. 组件参数验证:在组件类中使用类型声明和默认值,确保组件参数的正确性
  4. 组件文档:为复杂组件添加文档注释,说明组件的用途和参数
  5. 组件测试:为重要组件编写测试用例,确保组件的可靠性
  6. 组件版本控制:为组件添加版本标识,便于后续的维护和升级

Laravel 12 组件使用方式简洁直观,通过 <x-组件名> 标签来使用组件,支持传递属性、使用插槽和复杂逻辑,使视图开发更加模块化和可维护。合理使用组件可以大大提高开发效率,减少代码重复,是构建现代化 Laravel 应用的重要手段。

Laravel 12 组件类高级特性

Laravel 12 组件类提供了丰富的高级特性,支持复杂的逻辑处理、数据转换和生命周期管理,使组件具备更强大的功能和灵活性。

1. 组件类高级结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Laravel 12 组件类: app/View/Components/Alert.php
namespace App\View\Components;

use Illuminate\View\Component;
use Illuminate\Support\Str;

class Alert extends Component
{
/**
* 组件类型
*/
public string $type;

/**
* 是否可关闭
*/
public bool $dismissible;

/**
* 图标类名
*/
public ?string $icon;

/**
* 标题
*/
public ?string $title;

/**
* 组件 ID
*/
public string $id;

/**
* 创建组件实例
*/
public function __construct(
string $type = 'info',
bool $dismissible = false,
?string $icon = null,
?string $title = null
) {
$this->type = $type;
$this->dismissible = $dismissible;
$this->icon = $icon ?? $this->getDefaultIcon($type);
$this->title = $title;
$this->id = 'alert-' . Str::random(8);
}

/**
* 获取默认图标
*/
protected function getDefaultIcon(string $type): string
{
$icons = [
'success' => 'fas fa-check-circle',
'error' => 'fas fa-exclamation-circle',
'warning' => 'fas fa-exclamation-triangle',
'info' => 'fas fa-info-circle',
];

return $icons[$type] ?? $icons['info'];
}

/**
* 获取视图数据
*/
public function data(): array
{
return [
'alertClasses' => $this->getAlertClasses(),
'hasIcon' => !empty($this->icon),
];
}

/**
* 获取警告框类名
*/
protected function getAlertClasses(): string
{
$classes = [
'alert',
'alert-' . $this->type,
];

if ($this->dismissible) {
$classes[] = 'alert-dismissible';
}

return implode(' ', $classes);
}

/**
* 渲染组件
*/
public function render()
{
return view('components.alert');
}
}
2. 组件生命周期钩子

Laravel 12 组件支持生命周期钩子,允许在组件渲染的不同阶段执行自定义逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// Laravel 12 组件类: app/View/Components/DataTable.php
namespace App\View\Components;

use Illuminate\View\Component;
use Illuminate\Support\Facades\Cache;

class DataTable extends Component
{
/**
* 表格数据
*/
public array $data;

/**
* 表格配置
*/
public array $config;

/**
* 创建组件实例
*/
public function __construct(array $data, array $config = [])
{
$this->data = $data;
$this->config = array_merge($this->getDefaultConfig(), $config);
}

/**
* 获取默认配置
*/
protected function getDefaultConfig(): array
{
return [
'pagination' => true,
'search' => true,
'sorting' => true,
'perPage' => 10,
];
}

/**
* 组件挂载时执行
*/
public function mount()
{
// 组件挂载时的逻辑
$this->initializeData();
}

/**
* 初始化数据
*/
protected function initializeData()
{
// 可以在这里执行数据转换、过滤等操作
$this->data = $this->processData($this->data);
}

/**
* 处理数据
*/
protected function processData(array $data): array
{
// 数据处理逻辑
return array_map(function ($item) {
// 处理每个数据项
return $item;
}, $data);
}

/**
* 渲染组件
*/
public function render()
{
return view('components.data-table');
}
}
3. 组件类最佳实践
  1. 类型声明:使用 PHP 类型声明确保组件参数的类型安全
  2. 默认值设置:为可选参数提供合理的默认值
  3. 数据转换:在构造函数中执行数据转换和预处理
  4. 辅助方法:将复杂逻辑拆分为多个小的辅助方法
  5. 数据传递:使用 data() 方法传递额外的视图数据
  6. 生命周期管理:合理使用生命周期钩子执行初始化和清理操作
  7. 依赖注入:在组件类中使用依赖注入获取服务

Laravel 12 组件类是组件系统的核心,通过提供强大的逻辑处理能力和灵活的配置选项,使组件能够处理复杂的业务场景,同时保持代码的清晰和可维护性。

4.5 Laravel 12 视图助手函数系统

Laravel 12 提供了丰富的视图助手函数,涵盖 URL 生成、表单处理、认证授权、缓存操作、国际化等多个领域,大大简化了视图开发,提高了代码的可读性和可维护性。

1. URL 助手函数

Laravel 12 的 URL 助手函数提供了全面的 URL 生成和管理功能,支持各种复杂场景的 URL 构建需求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{{-- Laravel 12 生成应用基础 URL --}}
{{ url('/') }}

{{-- Laravel 12 生成安全 HTTPS URL --}}
{{ secure_url('/dashboard') }}

{{-- Laravel 12 生成资源文件 URL(带版本控制) --}}
{{ asset('css/app.css') }}

{{-- Laravel 12 生成带自定义协议的 URL --}}
{{ url('/api', [], true) }} {{-- 强制 HTTPS --}}

{{-- Laravel 12 生成命名路由 URL --}}
{{ route('posts.show', ['id' => 1]) }}

{{-- Laravel 12 生成带查询参数的路由 URL --}}
{{ route('posts.index', ['category' => 'tech', 'page' => 2]) }}

{{-- Laravel 12 生成控制器动作 URL --}}
{{ action([PostController::class, 'show'], ['id' => 1]) }}

{{-- Laravel 12 生成邮件可点击链接 --}}
{{ url()->signedRoute('invitations.accept', ['invitation' => $invitation->id]) }}

{{-- Laravel 12 生成临时签名 URL(带过期时间) --}}
{{ URL::temporarySignedRoute('files.download', now()->addHour(), ['file' => $file->id]) }}

URL 助手最佳实践

  • 使用命名路由生成 URL,提高代码可维护性
  • 对于资源文件,使用 asset() 函数自动处理版本缓存
  • 对于需要安全验证的链接,使用签名路由
  • 避免硬编码 URL,使用助手函数动态生成

2. 表单助手函数

Laravel 12 的表单助手函数简化了表单开发,特别是在 CSRF 保护、方法伪造和表单验证等方面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{{-- Laravel 12 CSRF 令牌字段(现代语法) --}}
@csrf

{{-- Laravel 12 HTTP 方法伪造字段 --}}
@method('PUT')

{{-- Laravel 12 旧输入值获取 --}}
<input type="text" name="name" value="{{ old('name', $user->name ?? '') }}">

{{-- Laravel 12 表单错误信息 --}}
@error('email')
<div class="alert alert-danger">{{ $message }}</div>
@enderror

{{-- Laravel 12 表单验证状态 --}}
<input type="text" name="email" class="form-control @error('email') is-invalid @enderror">

表单助手最佳实践

  • 始终使用 @csrf 保护表单提交
  • 对于 PUT/PATCH/DELETE 请求,使用 @method 指令
  • 使用 old() 函数保留表单提交失败时的输入值
  • 结合 Blade 错误指令显示验证错误信息

3. 认证与授权助手

Laravel 12 提供了丰富的认证和授权助手函数,简化了用户状态检查和权限验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{{-- Laravel 12 检查用户是否已认证 --}}
@auth
<div class="user-menu">欢迎,{{ auth()->user()->name }}</div>
@endauth

{{-- Laravel 12 检查用户是否为访客 --}}
@guest
<a href="{{ route('login') }}">登录</a>
@endguest

{{-- Laravel 12 检查用户是否具有特定角色 --}}
@role('admin')
<a href="{{ route('admin.dashboard') }}">管理后台</a>
@endrole

{{-- Laravel 12 检查用户是否具有特定权限 --}}
@can('edit', $post)
<a href="{{ route('posts.edit', $post->id) }}">编辑</a>
@endcan

{{-- Laravel 12 检查用户是否为模型所有者 --}}
@owner($post)
<a href="{{ route('posts.delete', $post->id) }}">删除</a>
@endowner

认证助手最佳实践

  • 使用 Blade 指令 @auth@guest 替代 auth()->check()
  • 对于权限检查,使用 @can 指令替代手动权限判断
  • 结合 Gates 和 Policies 使用授权指令,实现细粒度权限控制

4. 缓存助手函数

Laravel 12 提供了缓存助手函数,支持在视图中直接进行缓存操作,提高页面渲染性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{{-- Laravel 12 缓存视图片段(5分钟) --}}
@cache('sidebar', 300)
<div class="sidebar">
<!-- 复杂的侧边栏内容 -->
</div>
@endcache

{{-- Laravel 12 带标签的缓存 --}}
@cache('user-profile-'.$user->id, 60, ['users', 'profiles'])
<div class="user-profile">
<!-- 用户资料内容 -->
</div>
@endcache

{{-- Laravel 12 获取缓存值 --}}
{{ cache('site_settings')['copyright'] }}

缓存助手最佳实践

  • 对渲染成本高的视图片段使用缓存
  • 为缓存项添加合适的过期时间
  • 使用缓存标签便于批量失效
  • 避免在视图中进行复杂的缓存逻辑

5. 国际化助手函数

Laravel 12 的国际化助手函数支持多语言应用的开发,提供了简洁的翻译和本地化功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{{-- Laravel 12 基本翻译 --}}
{{ __('Welcome to our application') }}

{{-- Laravel 12 带参数的翻译 --}}
{{ __('Hello, :name', ['name' => $user->name]) }}

{{-- Laravel 12 复数形式翻译 --}}
{{ trans_choice('There is :count product|There are :count products', $count) }}

{{-- Laravel 12 日期本地化 --}}
{{ $post->created_at->translatedFormat('l, d F Y') }}

{{-- Laravel 12 数字本地化 --}}
{{ number_format($price, 2, ',', '.') }}

国际化助手最佳实践

  • 将所有用户可见文本放入翻译文件
  • 使用参数化翻译提高灵活性
  • 为不同语言提供适当的复数形式
  • 结合 Carbon 实例使用日期本地化

6. 其他实用助手函数

Laravel 12 还提供了许多其他实用的视图助手函数,覆盖了各种常见的开发需求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{{-- Laravel 12 生成随机字符串 --}}
{{ Str::random(16) }}

{{-- Laravel 12 格式化文件大小 --}}
{{ number_format($fileSize / 1024, 2) }} KB

{{-- Laravel 12 检查环境 --}}
@env('local')
<div class="debug-info">Local Environment</div>
@endenv

{{-- Laravel 12 检查配置值 --}}
@if(config('app.debug'))
<div class="debug-mode">Debug Mode Enabled</div>
@endif

{{-- Laravel 12 显示环境变量 --}}
{{ env('APP_NAME') }}

综合最佳实践

  • 合理使用助手函数简化视图代码
  • 避免在视图中进行复杂的业务逻辑处理
  • 结合 Blade 指令和助手函数提高代码可读性
  • 遵循 Laravel 编码规范,保持代码风格一致

Laravel 12 的视图助手函数系统是一个强大的工具集,通过合理使用这些助手函数,可以大大提高视图开发效率,减少重复代码,同时保持代码的清晰和可维护性。

4.6 Laravel 12 视图实战示例

现在,让我们为之前创建的 Laravel 12 博客应用创建视图文件,演示 Laravel 12 视图系统的实际使用:

  1. 创建 Laravel 12 布局文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    {{-- Laravel 12 布局文件: resources/views/layouts/app.blade.php --}}
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield('title', 'Laravel 12 Blog')</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    </head>
    <body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container">
    <a class="navbar-brand" href="{{ route('posts.index') }}">Laravel 12 Blog</a>
    <div class="collapse navbar-collapse">
    <ul class="navbar-nav mr-auto">
    <li class="nav-item">
    <a class="nav-link" href="{{ route('posts.index') }}">Home</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" href="{{ route('posts.create') }}">Create Post</a>
    </li>
    </ul>
    </div>
    </div>
    </nav>

    <div class="container mt-4">
    @yield('content')
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    </body>
    </html>
  2. 创建 Laravel 12 帖子列表视图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    {{-- Laravel 12 帖子列表视图: resources/views/posts/index.blade.php --}}
    @extends('layouts.app')

    @section('title', 'Posts')

    @section('content')
    <h1>Posts</h1>

    <div class="mb-4">
    <a href="{{ route('posts.create') }}" class="btn btn-primary">Create Post</a>
    </div>

    <div class="list-group">
    <!-- Laravel 12 暂时使用模拟数据 -->
    <a href="{{ route('posts.show', ['id' => 1]) }}" class="list-group-item list-group-item-action">
    <h5 class="mb-1">Post 1</h5>
    <p class="mb-1">This is the first post.</p>
    </a>
    <a href="{{ route('posts.show', ['id' => 2]) }}" class="list-group-item list-group-item-action">
    <h5 class="mb-1">Post 2</h5>
    <p class="mb-1">This is the second post.</p>
    </a>
    </div>
    @endsection
  3. 创建 Laravel 12 帖子创建视图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    {{-- Laravel 12 帖子创建视图: resources/views/posts/create.blade.php --}}
    @extends('layouts.app')

    @section('title', 'Create Post')

    @section('content')
    <h1>Create Post</h1>

    <form action="{{ route('posts.store') }}" method="POST">
    @csrf

    <div class="mb-3">
    <label for="title" class="form-label">Title</label>
    <input type="text" class="form-control" id="title" name="title" required>
    </div>

    <div class="mb-3">
    <label for="content" class="form-label">Content</label>
    <textarea class="form-control" id="content" name="content" rows="3" required></textarea>
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
    </form>
    @endsection
  4. 创建 Laravel 12 帖子显示视图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {{-- Laravel 12 帖子显示视图: resources/views/posts/show.blade.php --}}
    @extends('layouts.app')

    @section('title', 'Post')

    @section('content')
    <h1>Post {{ $id }}</h1>

    <div class="card">
    <div class="card-body">
    <h5 class="card-title">Post {{ $id }}</h5>
    <p class="card-text">This is post {{ $id }} content.</p>
    <a href="{{ route('posts.edit', ['id' => $id]) }}" class="btn btn-primary">Edit</a>
    <form action="{{ route('posts.destroy', ['id' => $id]) }}" method="POST" style="display: inline;">
    @csrf
    @method('DELETE')
    <button type="submit" class="btn btn-danger">Delete</button>
    </form>
    <a href="{{ route('posts.index') }}" class="btn btn-secondary">Back</a>
    </div>
    </div>
    @endsection
  5. 创建 Laravel 12 帖子编辑视图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    {{-- Laravel 12 帖子编辑视图: resources/views/posts/edit.blade.php --}}
    @extends('layouts.app')

    @section('title', 'Edit Post')

    @section('content')
    <h1>Edit Post {{ $id }}</h1>

    <form action="{{ route('posts.update', ['id' => $id]) }}" method="POST">
    @csrf
    @method('PUT')

    <div class="mb-3">
    <label for="title" class="form-label">Title</label>
    <input type="text" class="form-control" id="title" name="title" value="Post {{ $id }}" required>
    </div>

    <div class="mb-3">
    <label for="content" class="form-label">Content</label>
    <textarea class="form-control" id="content" name="content" rows="3" required>This is post {{ $id }} content.</textarea>
    </div>

    <button type="submit" class="btn btn-primary">Update</button>
    <a href="{{ route('posts.show', ['id' => $id]) }}" class="btn btn-secondary">Cancel</a>
    </form>
    @endsection

4.7 Laravel 12 视图缓存

对于 Laravel 12 生产环境,Laravel 12 会自动缓存编译后的 Blade 模板,以提高性能。视图缓存可以显著减少模板编译时间,提升应用响应速度:

1
2
3
4
5
# Laravel 12 清除视图缓存
php artisan view:clear

# Laravel 12 预热视图缓存
php artisan view:cache

Laravel 12 视图缓存机制非常智能,当模板文件发生变化时,会自动重新编译,确保视图始终是最新的。在生产环境中使用视图缓存可以显著提升 Laravel 12 应用的性能,减少服务器负载,提高用户体验。

现在,我们已经了解了 Laravel 12 的视图系统,接下来我们将学习 Laravel 12 的数据库操作。

5. Laravel 12 数据库操作

Laravel 12 提供了多种数据库操作方式,包括数据库迁移、Eloquent ORM、查询构建器和原始 SQL 查询。Laravel 12 的数据库操作非常强大和灵活,支持多种数据库系统,如 MySQL、PostgreSQL、SQLite 和 SQL Server 等。

5.1 Laravel 12 数据库配置

Laravel 12 的数据库配置位于 config/database.php 文件中,我们可以在 .env 文件中设置数据库连接信息。Laravel 12 支持多种数据库系统,包括 MySQL、PostgreSQL、SQLite 和 SQL Server 等,配置简单直观。

Laravel 12 配置数据库连接

Laravel 12 支持多种数据库系统,我们可以在 .env 文件中配置数据库连接信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Laravel 12 MySQL 配置
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

# Laravel 12 PostgreSQL 配置
# DB_CONNECTION=pgsql
# DB_HOST=127.0.0.1
# DB_PORT=5432
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

# Laravel 12 SQLite 配置
# DB_CONNECTION=sqlite
# DB_DATABASE=/path/to/database.sqlite

Laravel 12 的数据库配置非常灵活,我们可以根据项目需求选择不同的数据库系统,并在 .env 文件中设置相应的连接信息。

5.2 Laravel 12 数据库迁移

Laravel 12 数据库迁移是一种版本控制数据库结构的方法,允许我们定义和修改数据库表结构。Laravel 12 数据库迁移使用 PHP 代码来定义数据库结构,而不是 SQL 语句,使数据库结构管理更加灵活和可维护。

Laravel 12 创建迁移

我们可以使用 Laravel 12 Artisan 命令生成迁移,创建数据库表结构:

1
2
3
4
5
6
7
8
# Laravel 12 创建迁移
php artisan make:migration create_users_table

# Laravel 12 创建带表结构的迁移
php artisan make:migration create_users_table --create=users

# Laravel 12 创建修改表的迁移
php artisan make:migration add_email_to_users_table --table=users

Laravel 12 提供了便捷的 Artisan 命令来生成迁移文件,支持创建新表和修改现有表的迁移,使数据库结构管理更加高效和规范化。

Laravel 12 迁移结构

Laravel 12 迁移文件由一个包含 up()down() 方法的类组成,结构清晰,易于维护:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
/**
* 运行迁移
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}

/**
* 回滚迁移
*/
public function down()
{
Schema::dropIfExists('users');
}
}

Laravel 12 迁移结构设计合理,up() 方法用于创建或修改数据库表结构,down() 方法用于回滚这些更改,使数据库结构管理更加灵活和可维护。

Laravel 12 运行迁移

我们可以使用 Laravel 12 Artisan 命令运行和管理迁移,执行数据库结构变更:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Laravel 12 运行所有未运行的迁移
php artisan migrate

# Laravel 12 回滚最后一次迁移
php artisan migrate:rollback

# Laravel 12 回滚所有迁移
php artisan migrate:reset

# Laravel 12 回滚所有迁移并重新运行
php artisan migrate:fresh

# Laravel 12 回滚所有迁移并重新运行,同时运行种子
php artisan migrate:fresh --seed

Laravel 12 提供了完整的迁移管理命令,支持运行、回滚、重置和刷新迁移等操作,使数据库结构管理更加灵活和可控。

Laravel 12 迁移列类型

Laravel 12 提供了多种列类型,用于定义数据库表字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$table->id(); // Laravel 12 自增 ID
$table->string('name'); // Laravel 12 字符串
$table->text('description'); // Laravel 12 文本
$table->integer('age'); // Laravel 12 整数
$table->bigInteger('price'); // Laravel 12 大整数
$table->float('rate'); // Laravel 12 浮点数
$table->double('score'); // Laravel 12 双精度浮点数
$table->decimal('amount', 8, 2); // Laravel 12 小数
$table->boolean('active'); // Laravel 12 布尔值
$table->date('birthday'); // Laravel 12 日期
$table->time('time'); // Laravel 12 时间
$table->datetime('created_at'); // Laravel 12 日期时间
$table->timestamp('updated_at'); // Laravel 12 时间戳
$table->json('data'); // Laravel 12 JSON
$table->enum('status', ['active', 'inactive']); // Laravel 12 枚举
$table->foreignId('user_id'); // Laravel 12 外键

Laravel 12 提供了丰富的列类型,满足不同场景的数据库字段需求,使数据库结构定义更加灵活和精确。

Laravel 12 列修饰符

Laravel 12 提供了多种列修饰符,用于定义数据库字段的属性:

1
2
3
4
5
6
7
$table->string('name')->nullable(); // Laravel 12 可为空
$table->string('email')->unique(); // Laravel 12 唯一
$table->string('password')->default('secret'); // Laravel 12 默认值
$table->integer('age')->unsigned(); // Laravel 12 无符号
$table->string('name')->length(100); // Laravel 12 长度
$table->integer('order')->autoIncrement(); // Laravel 12 自增
$table->string('name')->comment('用户名称'); // Laravel 12 注释

Laravel 12 列修饰符非常强大,可以定义字段的各种属性,如可为空、唯一、默认值、长度、自增和注释等,使数据库字段定义更加精确和灵活。

Laravel 12 索引

Laravel 12 提供了多种索引类型,用于优化数据库查询性能:

1
2
3
4
5
6
7
$table->index('email'); // Laravel 12 普通索引
$table->unique('email'); // Laravel 12 唯一索引
$table->primary('id'); // Laravel 12 主键
$table->foreign('user_id')->references('id')->on('users'); // Laravel 12 外键

// Laravel 12 复合索引
$table->index(['first_name', 'last_name']);

Laravel 12 索引非常强大,可以定义普通索引、唯一索引、主键和外键等多种类型的索引,以及复合索引,大大提高数据库查询性能。

5.3 Laravel 12 数据库填充

Laravel 12 数据库填充用于向数据库中添加初始数据,如默认用户、配置数据等。Laravel 12 数据库填充使用填充器和模型工厂来生成和插入数据,使数据填充更加高效和灵活。

Laravel 12 创建填充器

我们可以使用 Laravel 12 Artisan 命令创建填充器和模型工厂,用于生成和插入初始数据:

1
2
3
4
5
# Laravel 12 创建填充器
php artisan make:seeder UserSeeder

# Laravel 12 创建模型工厂
php artisan make:factory UserFactory

Laravel 12 提供了便捷的 Artisan 命令来创建填充器和模型工厂,使数据填充更加高效和规范化。

Laravel 12 填充器结构

Laravel 12 填充器由一个包含 run() 方法的类组成,用于向数据库中插入初始数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class UserSeeder extends Seeder
{
/**
* 运行数据库填充
*/
public function run()
{
DB::table('users')->insert([
'name' => 'Admin',
'email' => 'admin@example.com',
'password' => bcrypt('password'),
]);
}
}

Laravel 12 填充器结构设计合理,通过 run() 方法执行数据填充操作,可以使用 DB 门面或模型工厂来插入数据,使数据填充更加灵活和可控。

Laravel 12 模型工厂

Laravel 12 模型工厂用于生成模型的测试数据,支持使用 Faker 库生成各种类型的假数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
/**
* Laravel 12 模型关联
*/
protected $model = User::class;

/**
* 定义模型默认状态
*/
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => bcrypt('password'),
'remember_token' => Str::random(10),
];
}
}

Laravel 12 模型工厂非常强大,支持生成各种类型的假数据,如姓名、邮箱、地址等,使数据填充更加高效和灵活。

Laravel 12 使用模型工厂

我们可以在 Laravel 12 填充器中使用模型工厂来生成和插入大量测试数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use App\Models\User;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
/**
* 运行数据库填充
*/
public function run()
{
// Laravel 12 创建 10 个用户
User::factory()->count(10)->create();

// Laravel 12 创建带特定属性的用户
User::factory()->count(5)->create([
'role' => 'admin',
]);
}
}

Laravel 12 模型工厂非常强大,可以快速生成大量测试数据,也可以指定特定属性,使数据填充更加灵活和可控。

Laravel 12 运行填充器

我们可以使用 Laravel 12 Artisan 命令运行填充器,向数据库中插入初始数据:

1
2
3
4
5
6
7
8
# Laravel 12 运行指定填充器
php artisan db:seed --class=UserSeeder

# Laravel 12 运行所有填充器
php artisan db:seed

# Laravel 12 重新迁移并运行填充器
php artisan migrate:fresh --seed

Laravel 12 提供了完整的填充器管理命令,支持运行指定填充器、运行所有填充器,以及重新迁移并运行填充器等操作,使数据填充更加灵活和可控。

5.4 Laravel 12 Eloquent ORM

Eloquent 是 Laravel 12 提供的 ORM(对象关系映射),允许我们使用 PHP 对象来操作数据库。Laravel 12 Eloquent ORM 非常强大,支持模型关联、查询作用域、访问器和修改器等多种功能,使数据库操作更加简洁和优雅。

Laravel 12 创建模型

我们可以使用 Laravel 12 Artisan 命令创建模型,定义数据库表结构的 PHP 表示:

1
2
3
4
5
6
7
8
# Laravel 12 创建模型
php artisan make:model User

# Laravel 12 创建模型和迁移
php artisan make:model User -m

# Laravel 12 创建模型、迁移和控制器
php artisan make:model User -mcr

Laravel 12 提供了便捷的 Artisan 命令来创建模型,支持创建带迁移和控制器的模型,使模型开发更加高效和规范化。

Laravel 12 模型结构

Laravel 12 模型由一个继承自 Model 类的 PHP 类组成,结构清晰,易于维护:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
use HasFactory;

/**
* Laravel 12 可批量赋值的属性
*/
protected $fillable = [
'name',
'email',
'password',
];

/**
* Laravel 12 隐藏的属性
*/
protected $hidden = [
'password',
'remember_token',
];

/**
* Laravel 12 类型转换
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}

Laravel 12 模型结构设计合理,支持定义可批量赋值的属性、隐藏的属性和类型转换等,使模型操作更加安全和灵活。

Laravel 12 基本查询

Laravel 12 Eloquent ORM 提供了丰富的查询方法,用于从数据库中检索数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use App\Models\User;

// Laravel 12 获取所有记录
$users = User::all();

// Laravel 12 根据 ID 获取记录
$user = User::find(1);

// Laravel 12 根据条件获取记录
$user = User::where('email', 'admin@example.com')->first();

// Laravel 12 获取符合条件的所有记录
$users = User::where('age', '>', 18)->get();

// Laravel 12 排序
$users = User::orderBy('name', 'asc')->get();

// Laravel 12 分页
$users = User::paginate(10);

// Laravel 12 聚合函数
$count = User::count();
$max = User::max('age');
$min = User::min('age');
$avg = User::avg('age');
$sum = User::sum('score');

Laravel 12 基本查询非常强大,支持获取所有记录、根据 ID 获取记录、根据条件获取记录、排序、分页和聚合函数等多种操作,使数据库查询更加简洁和优雅。

Laravel 12 创建记录

Laravel 12 Eloquent ORM 提供了多种方法来创建数据库记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Laravel 12 使用 create 方法
$user = User::create([
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => bcrypt('password'),
]);

// Laravel 12 使用 save 方法
$user = new User;
$user->name = 'John Doe';
$user->email = 'john@example.com';
$user->password = bcrypt('password');
$user->save();

Laravel 12 创建记录非常简单,支持使用 create 方法和 save 方法来创建数据库记录,使数据插入操作更加简洁和优雅。

Laravel 12 更新记录

Laravel 12 Eloquent ORM 提供了多种方法来更新数据库记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Laravel 12 使用 update 方法
$user = User::find(1);
$user->update([
'name' => 'Jane Doe',
]);

// Laravel 12 使用 save 方法
$user = User::find(1);
$user->name = 'Jane Doe';
$user->save();

// Laravel 12 批量更新
User::where('active', 1)->update(['status' => 'verified']);

Laravel 12 更新记录非常灵活,支持使用 update 方法、save 方法和批量更新来修改数据库记录,使数据更新操作更加简洁和优雅。

Laravel 12 删除记录

Laravel 12 Eloquent ORM 提供了多种方法来删除数据库记录:

1
2
3
4
5
6
7
8
9
10
// Laravel 12 使用 delete 方法
$user = User::find(1);
$user->delete();

// Laravel 12 使用 destroy 方法
User::destroy(1);
User::destroy([1, 2, 3]);

// Laravel 12 批量删除
User::where('active', 0)->delete();

Laravel 12 删除记录非常灵活,支持使用 delete 方法、destroy 方法和批量删除来移除数据库记录,使数据删除操作更加简洁和优雅。

Laravel 12 软删除

Laravel 12 Eloquent ORM 支持软删除,允许我们标记记录为已删除而不是真正从数据库中删除:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Laravel 12 在模型中启用软删除
use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Model
{
use HasFactory, SoftDeletes;
}

// Laravel 12 添加 deleted_at 字段到迁移
$table->softDeletes();

// Laravel 12 软删除记录
$user = User::find(1);
$user->delete();

// Laravel 12 查询包含软删除的记录
$users = User::withTrashed()->get();

// Laravel 12 只查询软删除的记录
$users = User::onlyTrashed()->get();

// Laravel 12 恢复软删除的记录
$user = User::withTrashed()->find(1);
$user->restore();

// Laravel 12 强制删除记录
$user = User::withTrashed()->find(1);
$user->forceDelete();

Laravel 12 软删除非常强大,支持标记记录为已删除、恢复软删除的记录、强制删除记录和查询软删除的记录等操作,使数据删除操作更加灵活和安全。

5.5 Laravel 12 关联关系

Laravel 12 Eloquent ORM 支持多种关联关系:一对一、一对多、多对多、多态关联等。Laravel 12 关联关系非常强大,允许我们轻松定义和查询模型之间的关系,使数据库操作更加简洁和优雅。

Laravel 12 一对一关联

Laravel 12 Eloquent ORM 支持一对一关联,允许我们定义两个模型之间的一对一关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Laravel 12 User 模型
public function profile()
{
return $this->hasOne(Profile::class);
}

// Laravel 12 Profile 模型
public function user()
{
return $this->belongsTo(User::class);
}

// Laravel 12 使用关联
$user = User::find(1);
$profile = $user->profile;

$profile = Profile::find(1);
$user = $profile->user;

Laravel 12 一对一关联非常强大,支持定义关联关系、使用关联和反向使用关联等操作,使模型之间的关系管理更加简洁和优雅。

Laravel 12 一对多关联

Laravel 12 Eloquent ORM 支持一对多关联,允许我们定义一个模型与多个其他模型之间的关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Laravel 12 Post 模型
public function comments()
{
return $this->hasMany(Comment::class);
}

// Laravel 12 Comment 模型
public function post()
{
return $this->belongsTo(Post::class);
}

// Laravel 12 使用关联
$post = Post::find(1);
$comments = $post->comments;

$comment = Comment::find(1);
$post = $comment->post;

Laravel 12 一对多关联非常强大,支持定义关联关系、使用关联和反向使用关联等操作,使模型之间的关系管理更加简洁和优雅。

Laravel 12 多对多关联

Laravel 12 Eloquent ORM 支持多对多关联,允许我们定义两个模型之间的多对多关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Laravel 12 User 模型
public function roles()
{
return $this->belongsToMany(Role::class);
}

// Laravel 12 Role 模型
public function users()
{
return $this->belongsToMany(User::class);
}

// Laravel 12 使用关联
$user = User::find(1);
$roles = $user->roles;

$role = Role::find(1);
$users = $role->users;

// Laravel 12 附加关联
$user->roles()->attach($roleId);

// Laravel 12 分离关联
$user->roles()->detach($roleId);

// Laravel 12 同步关联
$user->roles()->sync([1, 2, 3]);

Laravel 12 多对多关联非常强大,支持定义关联关系、使用关联、附加关联、分离关联和同步关联等操作,使模型之间的关系管理更加简洁和优雅。

Laravel 12 关联查询

Laravel 12 Eloquent ORM 提供了多种方法来查询关联数据,优化数据库查询性能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Laravel 12 预加载关联
$users = User::with('profile')->get();

// Laravel 12 嵌套预加载
$posts = Post::with('comments.user')->get();

// Laravel 12 延迟预加载
$users = User::all();
$users->load('profile');

// Laravel 12 条件预加载
$users = User::with(['profile' => function ($query) {
$query->where('active', 1);
}])->get();

Laravel 12 关联查询非常强大,支持预加载关联、嵌套预加载、延迟预加载和条件预加载等操作,大大提高了数据库查询性能,避免了 N+1 查询问题。

5.6 Laravel 12 查询构建器

Laravel 12 查询构建器提供了一种流畅的接口来构建 SQL 查询,支持各种数据库操作。Laravel 12 查询构建器非常强大,允许我们使用链式方法来构建复杂的 SQL 查询,而不需要直接编写 SQL 语句。

Laravel 12 基本查询

Laravel 12 查询构建器提供了丰富的方法来构建和执行 SQL 查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Illuminate\Support\Facades\DB;

// Laravel 12 选择
$users = DB::table('users')->get();

// Laravel 12 条件
$users = DB::table('users')->where('age', '>', 18)->get();

// Laravel 12 排序
$users = DB::table('users')->orderBy('name', 'asc')->get();

// Laravel 12 限制
$users = DB::table('users')->limit(10)->offset(5)->get();

// Laravel 12 聚合
$count = DB::table('users')->count();
$max = DB::table('users')->max('age');

Laravel 12 基本查询非常强大,支持选择、条件、排序、限制和聚合等操作,使数据库查询更加简洁和优雅。

Laravel 12 连接

Laravel 12 查询构建器支持多种类型的数据库连接,用于关联多个表的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Laravel 12 内连接
$users = DB::table('users')
->join('profiles', 'users.id', '=', 'profiles.user_id')
->select('users.*', 'profiles.bio')
->get();

// Laravel 12 左连接
$users = DB::table('users')
->leftJoin('profiles', 'users.id', '=', 'profiles.user_id')
->select('users.*', 'profiles.bio')
->get();

// Laravel 12 右连接
$users = DB::table('users')
->rightJoin('profiles', 'users.id', '=', 'profiles.user_id')
->select('users.*', 'profiles.bio')
->get();

// Laravel 12 交叉连接
$users = DB::table('users')
->crossJoin('roles')
->get();

Laravel 12 连接非常强大,支持内连接、左连接、右连接和交叉连接等多种类型的连接操作,使多表查询更加简洁和优雅。

Laravel 12 高级条件

Laravel 12 查询构建器支持多种高级条件查询,允许我们构建复杂的查询条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Laravel 12 基本条件
$users = DB::table('users')->where('age', '>', 18)->get();

// Laravel 12 多个条件
$users = DB::table('users')
->where('age', '>', 18)
->where('active', 1)
->get();

// Laravel 12 或条件
$users = DB::table('users')
->where('age', '>', 18)
->orWhere('active', 1)
->get();

// Laravel 12 分组条件
$users = DB::table('users')
->where(function ($query) {
$query->where('age', '>', 18)
->orWhere('active', 1);
})
->where('verified', 1)
->get();

// Laravel 12 范围条件
$users = DB::table('users')
->whereBetween('age', [18, 30])
->get();

// Laravel 12 列表条件
$users = DB::table('users')
->whereIn('id', [1, 2, 3])
->get();

// Laravel 12 空值条件
$users = DB::table('users')
->whereNull('email_verified_at')
->get();

Laravel 12 高级条件非常强大,支持基本条件、多个条件、或条件、分组条件、范围条件、列表条件和空值条件等多种查询方式,使复杂查询的构建更加简洁和优雅。

Laravel 12 插入、更新和删除

Laravel 12 查询构建器支持插入、更新和删除操作,用于修改数据库中的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Laravel 12 插入
DB::table('users')->insert([
'name' => 'John Doe',
'email' => 'john@example.com',
]);

// Laravel 12 插入并返回 ID
$id = DB::table('users')->insertGetId([
'name' => 'John Doe',
'email' => 'john@example.com',
]);

// Laravel 12 批量插入
DB::table('users')->insert([
['name' => 'John Doe', 'email' => 'john@example.com'],
['name' => 'Jane Doe', 'email' => 'jane@example.com'],
]);

// Laravel 12 更新
DB::table('users')->where('id', 1)->update([
'name' => 'Jane Doe',
]);

// Laravel 12 自增
DB::table('users')->where('id', 1)->increment('age');

// Laravel 12 自减
DB::table('users')->where('id', 1)->decrement('age');

// Laravel 12 删除
DB::table('users')->where('id', 1)->delete();

// Laravel 12 截断表
DB::table('users')->truncate();

Laravel 12 插入、更新和删除操作非常强大,支持插入单条记录、插入并返回 ID、批量插入、更新记录、自增、自减、删除记录和截断表等操作,使数据修改更加简洁和优雅。

5.7 Laravel 12 原始 SQL 查询

在某些情况下,我们可能需要使用原始 SQL 查询。Laravel 12 支持执行原始 SQL 查询,允许我们直接编写和执行 SQL 语句,为复杂查询提供更大的灵活性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Laravel 12 原始选择
$users = DB::select('SELECT * FROM users WHERE active = ?', [1]);

// Laravel 12 原始插入
DB::insert('INSERT INTO users (name, email) VALUES (?, ?)', ['John Doe', 'john@example.com']);

// Laravel 12 原始更新
DB::update('UPDATE users SET active = ? WHERE id = ?', [1, 1]);

// Laravel 12 原始删除
DB::delete('DELETE FROM users WHERE id = ?', [1]);

// Laravel 12 原始语句
DB::statement('DROP TABLE IF EXISTS users');

// Laravel 12 事务中的原始查询
DB::transaction(function () {
DB::update('UPDATE users SET votes = votes + 1 WHERE id = ?', [1]);
DB::delete('DELETE FROM posts WHERE user_id = ?', [1]);
});

5.8 Laravel 12 事务

Laravel 12 事务用于确保一系列数据库操作要么全部成功,要么全部失败,保证数据的一致性和完整性。

Laravel 12 自动事务

1
2
3
4
5
6
7
use Illuminate\Support\Facades\DB;

DB::transaction(function () {
// Laravel 12 数据库操作
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
});

Laravel 12 手动事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Illuminate\Support\Facades\DB;

// Laravel 12 开始事务
DB::beginTransaction();

try {
// Laravel 12 数据库操作
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();

// Laravel 12 提交事务
DB::commit();
} catch (\Exception $e) {
// Laravel 12 回滚事务
DB::rollBack();

// 处理异常
}

Laravel 12 事务嵌套

1
2
3
4
5
6
7
8
9
DB::transaction(function () {
// Laravel 12 数据库操作
DB::table('users')->update(['votes' => 1]);

DB::transaction(function () {
// Laravel 12 数据库操作
DB::table('posts')->delete();
}, 5); // 最多尝试 5 次
}, 3); // 最多尝试 3 次

Laravel 12 事务非常强大,支持自动事务、手动事务和事务嵌套等多种方式,并提供了事务重试机制,使数据库操作更加安全和可靠。

5.9 Laravel 12 实战示例

现在,让我们为之前创建的 Laravel 12 博客应用添加数据库功能:

  1. 创建帖子迁移

    1
    php artisan make:migration create_posts_table
  2. 编辑迁移文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;

    class CreatePostsTable extends Migration
    {
    public function up()
    {
    Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('content');
    $table->timestamps();
    });
    }

    public function down()
    {
    Schema::dropIfExists('posts');
    }
    }
  3. 运行迁移

    1
    php artisan migrate
  4. 创建帖子模型

    1
    php artisan make:model Post
  5. 编辑模型文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    namespace App\Models;

    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;

    class Post extends Model
    {
    use HasFactory;

    protected $fillable = [
    'title',
    'content',
    ];
    }
  6. 更新控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    namespace App\Http\Controllers;

    use App\Models\Post;
    use Illuminate\Http\Request;

    class PostController extends Controller
    {
    public function index()
    {
    $posts = Post::all();
    return view('posts.index', compact('posts'));
    }

    public function create()
    {
    return view('posts.create');
    }

    public function store(Request $request)
    {
    Post::create($request->all());
    return redirect()->route('posts.index');
    }

    public function show(Post $post)
    {
    return view('posts.show', compact('post'));
    }

    public function edit(Post $post)
    {
    return view('posts.edit', compact('post'));
    }

    public function update(Request $request, Post $post)
    {
    $post->update($request->all());
    return redirect()->route('posts.show', $post);
    }

    public function destroy(Post $post)
    {
    $post->delete();
    return redirect()->route('posts.index');
    }
    }
  7. 更新路由

    1
    2
    3
    use App\Http\Controllers\PostController;

    Route::resource('posts', PostController::class);
  8. 更新视图

    • 更新 posts/index.blade.php 以显示实际数据
    • 更新 posts/show.blade.php 以显示实际数据
    • 更新 posts/edit.blade.php 以显示实际数据
  9. 创建帖子填充器

    1
    php artisan make:seeder PostSeeder
  10. 编辑填充器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    use Illuminate\Database\Seeder;
    use App\Models\Post;

    class PostSeeder extends Seeder
    {
    public function run()
    {
    Post::create([
    'title' => 'First Post',
    'content' => 'This is the first post content.',
    ]);

    Post::create([
    'title' => 'Second Post',
    'content' => 'This is the second post content.',
    ]);
    }
    }
  11. 运行填充器

    1
    php artisan db:seed --class=PostSeeder

现在,我们的博客应用已经具备了完整的数据库功能,可以创建、查看、编辑和删除帖子。

5.10 Laravel 12 数据库优化

Laravel 12 提供了多种数据库优化技术,帮助我们提高应用的性能和可靠性。

Laravel 12 索引优化

  • 为经常查询的列添加索引
  • 为外键添加索引
  • 避免过多的索引(会影响插入和更新性能)
  • 使用复合索引优化多列查询
  • 在 Laravel 12 迁移中使用 $table->index() 方法添加索引

Laravel 12 查询优化

  • 使用预加载减少 N+1 查询问题:Post::with('comments')->get();
  • 只选择需要的列:User::select('id', 'name')->get();
  • 使用分页避免一次性获取过多数据:Post::paginate(10);
  • 避免在循环中执行数据库操作
  • 使用缓存减少数据库查询:Cache::remember('users', 60, function () { return User::all(); });

Laravel 12 连接池优化

  • 在生产环境中配置合适的数据库连接池大小
  • 关闭不需要的连接
  • 使用连接池监控工具
  • 在 Laravel 12 中通过 config/database.php 配置连接池参数

通过这些 Laravel 12 数据库优化技术,你可以显著提高应用的性能和响应速度,为用户提供更好的体验。

现在,我们已经了解了 Laravel 的数据库操作,接下来我们将学习 Laravel 的表单处理。

6. Laravel 12 表单处理

Laravel 12 表单处理是 Web 应用中的常见任务,Laravel 12 提供了强大的表单处理功能,包括表单验证、CSRF 保护等。

6.1 Laravel 12 基本表单处理

Laravel 12 创建表单

在 Laravel 12 中,我们可以使用 HTML 表单或 Laravel 12 提供的表单助手来创建表单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 基本 HTML 表单 -->
<form action="{{ route('posts.store') }}" method="POST">
@csrf

<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" name="title" required>
</div>

<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control" id="content" name="content" rows="3" required></textarea>
</div>

<button type="submit" class="btn btn-primary">Submit</button>
</form>

Laravel 12 处理表单提交

在 Laravel 12 控制器中,我们可以使用 Request 对象来获取表单数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
use Illuminate\Http\Request;

/**
* Store a newly created Laravel 12 resource in storage.
*/
public function store(Request $request)
{
// Laravel 12 获取单个字段
$title = $request->input('title');
$content = $request->input('content');

// Laravel 12 获取所有字段
$data = $request->all();

// Laravel 12 获取部分字段
$data = $request->only(['title', 'content']);

// Laravel 12 排除某些字段
$data = $request->except(['_token']);

// Laravel 12 检查字段是否存在
if ($request->has('title')) {
// 字段存在
}

// Laravel 12 检查多个字段是否存在
if ($request->has(['title', 'content'])) {
// 所有字段都存在
}

// Laravel 12 检查字段是否有值
if ($request->filled('title')) {
// 字段有值
}

// Laravel 12 检查字段是否为空
if ($request->missing('title')) {
// 字段不存在
}

// Laravel 12 存储数据
Post::create($data);

// Laravel 12 重定向
return redirect()->route('posts.index');
}

6.2 Laravel 12 表单验证

Laravel 12 提供了多种表单验证方法,包括控制器验证、请求类验证等,帮助我们确保用户输入的数据符合要求。

Laravel 12 控制器验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Validator;

/**
* Store a newly created Laravel 12 resource in storage.
*/
public function store(Request $request)
{
// Laravel 12 验证规则
$rules = [
'title' => 'required|min:3|max:255',
'content' => 'required|min:10',
];

// Laravel 12 验证消息
$messages = [
'title.required' => 'Title is required',
'title.min' => 'Title must be at least 3 characters',
'content.required' => 'Content is required',
'content.min' => 'Content must be at least 10 characters',
];

// Laravel 12 验证属性名称
$attributes = [
'title' => 'Post Title',
'content' => 'Post Content',
];

// Laravel 12 执行验证
$request->validate($rules, $messages, $attributes);

// 或者使用 Laravel 12 Validator 门面
/*
$validator = Validator::make($request->all(), $rules, $messages, $attributes);

if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
*/

// Laravel 12 验证通过,处理数据
Post::create($request->all());

return redirect()->route('posts.index');
}

Laravel 12 请求类验证

Laravel 12 请求类验证是一种更优雅的验证方式,它将验证逻辑从控制器中分离出来,使代码更加清晰和可维护。

1
2
# Laravel 12 创建请求类
php artisan make:request StorePostRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Laravel 12 请求类 - app/Http/Requests/StorePostRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
/**
* Laravel 12 - 确定用户是否有权限提交此请求
*/
public function authorize()
{
return true; // Laravel 12 默认返回 false,需要改为 true
}

/**
* Laravel 12 - 获取应用于此请求的验证规则
*/
public function rules()
{
return [
'title' => 'required|min:3|max:255',
'content' => 'required|min:10',
];
}

/**
* Laravel 12 - 获取验证错误的自定义消息
*/
public function messages()
{
return [
'title.required' => 'Title is required',
'title.min' => 'Title must be at least 3 characters',
'content.required' => 'Content is required',
'content.min' => 'Content must be at least 10 characters',
];
}

/**
* Laravel 12 - 获取自定义验证属性名称
*/
public function attributes()
{
return [
'title' => 'Post Title',
'content' => 'Post Content',
];
}
}

Laravel 12 在控制器中使用请求类:

1
2
3
4
5
6
7
8
9
10
11
12
13
use App\Http\Requests\StorePostRequest;

/**
* Store a newly created Laravel 12 resource in storage.
*/
public function store(StorePostRequest $request)
{
// Laravel 12 验证通过,直接处理数据
Post::create($request->validated());

// Laravel 12 重定向
return redirect()->route('posts.index');
}

Laravel 12 验证规则

Laravel 12 提供了丰富的验证规则,帮助我们确保用户输入的数据符合要求:

规则描述
required字段必须存在且不为空
min字符串长度或数值最小值
max字符串长度或数值最大值
email必须是有效的电子邮件地址
unique必须在指定表中是唯一的
exists必须在指定表中存在
regex必须匹配指定的正则表达式
confirmed必须与同名的 _confirmed 字段值匹配
nullable字段可以为空
date必须是有效的日期
boolean必须是布尔值
numeric必须是数值
integer必须是整数
alpha必须只包含字母
alpha_dash必须只包含字母、数字、破折号和下划线
alpha_num必须只包含字母和数字
url必须是有效的 URL
ip必须是有效的 IP 地址
json必须是有效的 JSON 字符串

Laravel 12 验证规则非常强大,可以组合使用多个规则来满足复杂的验证需求。例如:

1
2
3
4
5
// Laravel 12 组合验证规则
'title' => 'required|min:3|max:255|unique:posts',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
'age' => 'required|integer|min:18|max:100',

Laravel 12 显示验证错误

在 Laravel 12 视图中,我们可以使用 $errors 变量来显示验证错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<form action="{{ route('posts.store') }}" method="POST">
@csrf

<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control @error('title') is-invalid @enderror" id="title" name="title" value="{{ old('title') }}" required>
@error('title')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>

<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control @error('content') is-invalid @enderror" id="content" name="content" rows="3" required>{{ old('content') }}</textarea>
@error('content')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>

<button type="submit" class="btn btn-primary">Submit</button>
</form>

Laravel 12 提供了以下用于显示验证错误的方法:

  • @error 指令:用于检查特定字段的错误
  • $errors->any():检查是否有任何错误
  • $errors->all():获取所有错误消息
  • $errors->get('field'):获取特定字段的错误消息
  • old('field'):获取之前输入的值,用于表单回显
1
2
3
4
5
6
7
8

#### Laravel 12 自定义验证规则

Laravel 12 允许我们创建自定义验证规则,以满足特定的验证需求:

```bash
# Laravel 12 创建验证规则
php artisan make:rule Uppercase
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Laravel 12 自定义验证规则 - app/Rules/Uppercase.php
namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Uppercase implements Rule
{
/**
* Laravel 12 - 确定验证规则是否通过
*/
public function passes($attribute, $value)
{
return strtoupper($value) === $value;
}

/**
* Laravel 12 - 获取验证错误消息
*/
public function message()
{
return 'The :attribute must be uppercase.';
}
}

Laravel 12 使用自定义验证规则:

1
2
3
4
5
6
7
8
9
10
11
use App\Rules\Uppercase;

/**
* Laravel 12 - 获取应用于此请求的验证规则
*/
public function rules()
{
return [
'title' => ['required', new Uppercase],
];
}

Laravel 12 自定义验证规则非常灵活,可以根据具体的业务需求创建各种复杂的验证规则,提高应用的安全性和可靠性。

6.3 Laravel 12 CSRF 保护

CSRF(Cross-Site Request Forgery)是一种常见的 Web 攻击方式,Laravel 12 提供了内置的 CSRF 保护,帮助我们防止恶意网站伪造请求。

Laravel 12 添加 CSRF 令牌

在 Laravel 12 表单中,我们需要添加 CSRF 令牌:

1
2
3
4
5
<form action="{{ route('posts.store') }}" method="POST">
@csrf

<!-- 表单字段 -->
</form>

Laravel 12 AJAX 请求中的 CSRF 保护

对于 Laravel 12 AJAX 请求,我们可以在请求头中添加 CSRF 令牌:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Laravel 12 在 HTML 中添加元标签
<meta name="csrf-token" content="{{ csrf_token() }}">

// Laravel 12 在 JavaScript 中设置请求头
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});

// 或者在单个 Laravel 12 请求中设置
$.ajax({
url: '/posts',
type: 'POST',
data: {
title: 'Test',
content: 'Test content'
},
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
success: function(response) {
console.log(response);
}
});

Laravel 12 排除路由的 CSRF 保护

在某些情况下,我们可能需要排除某些路由的 CSRF 保护,例如 API 路由:

1
2
3
4
5
// Laravel 12 CSRF 中间件配置 - app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
'api/*',
'webhook/*',
];

6.4 请求类

请求类不仅可以用于验证,还可以用于处理请求数据的转换和准备。

自定义请求类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// app/Http/Requests/StorePostRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
/**
* 确定用户是否有权限提交此请求
*/
public function authorize()
{
return true;
}

/**
* 获取应用于此请求的验证规则
*/
public function rules()
{
return [
'title' => 'required|min:3|max:255',
'content' => 'required|min:10',
];
}

/**
* 准备要验证的数据
*/
protected function prepareForValidation()
{
$this->merge([
'title' => trim($this->title),
'content' => trim($this->content),
]);
}

/**
* 获取经过转换的输入数据
*/
public function validated()
{
return array_merge(parent::validated(), [
'slug' => str_slug($this->title),
]);
}
}

6.5 表单数据持久化

当表单验证失败时,我们通常希望保持用户已经输入的数据,Laravel 提供了 old() 辅助函数来实现这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<input type="text" class="form-control" id="title" name="title" value="{{ old('title') }}" required>

<textarea class="form-control" id="content" name="content" rows="3" required>{{ old('content') }}</textarea>

<select class="form-select" name="category" required>
<option value="">Select Category</option>
<option value="1" {{ old('category') == 1 ? 'selected' : '' }}>Category 1</option>
<option value="2" {{ old('category') == 2 ? 'selected' : '' }}>Category 2</option>
</select>

<input type="radio" id="status-active" name="status" value="active" {{ old('status') == 'active' ? 'checked' : '' }}>
<label for="status-active">Active</label>

<input type="radio" id="status-inactive" name="status" value="inactive" {{ old('status') == 'inactive' ? 'checked' : '' }}>
<label for="status-inactive">Inactive</label>

<input type="checkbox" id="featured" name="featured" {{ old('featured') ? 'checked' : '' }}>
<label for="featured">Featured</label>

6.6 实战示例

现在,让我们为之前创建的博客应用添加表单验证:

  1. 创建请求类

    1
    2
    php artisan make:request StorePostRequest
    php artisan make:request UpdatePostRequest
  2. 编辑 StorePostRequest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    namespace App\Http\Requests;

    use Illuminate\Foundation\Http\FormRequest;

    class StorePostRequest extends FormRequest
    {
    public function authorize()
    {
    return true;
    }

    public function rules()
    {
    return [
    'title' => 'required|min:3|max:255',
    'content' => 'required|min:10',
    ];
    }
    }
  3. 编辑 UpdatePostRequest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    namespace App\Http\Requests;

    use Illuminate\Foundation\Http\FormRequest;

    class UpdatePostRequest extends FormRequest
    {
    public function authorize()
    {
    return true;
    }

    public function rules()
    {
    return [
    'title' => 'required|min:3|max:255',
    'content' => 'required|min:10',
    ];
    }
    }
  4. 更新控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    use App\Http\Requests\StorePostRequest;
    use App\Http\Requests\UpdatePostRequest;

    public function store(StorePostRequest $request)
    {
    Post::create($request->validated());
    return redirect()->route('posts.index');
    }

    public function update(UpdatePostRequest $request, Post $post)
    {
    $post->update($request->validated());
    return redirect()->route('posts.show', $post);
    }
  5. 更新视图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <!-- resources/views/posts/create.blade.php -->
    <form action="{{ route('posts.store') }}" method="POST">
    @csrf

    <div class="mb-3">
    <label for="title" class="form-label">Title</label>
    <input type="text" class="form-control @error('title') is-invalid @enderror" id="title" name="title" value="{{ old('title') }}" required>
    @error('title')
    <div class="invalid-feedback">
    {{ $message }}
    </div>
    @enderror
    </div>

    <div class="mb-3">
    <label for="content" class="form-label">Content</label>
    <textarea class="form-control @error('content') is-invalid @enderror" id="content" name="content" rows="3" required>{{ old('content') }}</textarea>
    @error('content')
    <div class="invalid-feedback">
    {{ $message }}
    </div>
    @enderror
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
    </form>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <!-- resources/views/posts/edit.blade.php -->
    <form action="{{ route('posts.update', $post) }}" method="POST">
    @csrf
    @method('PUT')

    <div class="mb-3">
    <label for="title" class="form-label">Title</label>
    <input type="text" class="form-control @error('title') is-invalid @enderror" id="title" name="title" value="{{ old('title', $post->title) }}" required>
    @error('title')
    <div class="invalid-feedback">
    {{ $message }}
    </div>
    @enderror
    </div>

    <div class="mb-3">
    <label for="content" class="form-label">Content</label>
    <textarea class="form-control @error('content') is-invalid @enderror" id="content" name="content" rows="3" required>{{ old('content', $post->content) }}</textarea>
    @error('content')
    <div class="invalid-feedback">
    {{ $message }}
    </div>
    @enderror
    </div>

    <button type="submit" class="btn btn-primary">Update</button>
    <a href="{{ route('posts.show', $post) }}" class="btn btn-secondary">Cancel</a>
    </form>

现在,我们的博客应用已经具备了完整的表单处理功能,包括表单验证和 CSRF 保护。

6.7 高级表单处理

复杂表单

对于复杂的表单,我们可以使用嵌套字段和数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- 嵌套字段 -->
<form action="{{ route('users.store') }}" method="POST">
@csrf

<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>

<div class="mb-3">
<label class="form-label">Address</label>
<div class="row">
<div class="col">
<input type="text" class="form-control" name="address[street]" placeholder="Street">
</div>
<div class="col">
<input type="text" class="form-control" name="address[city]" placeholder="City">
</div>
<div class="col">
<input type="text" class="form-control" name="address[zip]" placeholder="Zip Code">
</div>
</div>
</div>

<button type="submit" class="btn btn-primary">Submit</button>
</form>

在控制器中获取嵌套字段:

1
2
3
4
5
6
7
8
9
10
public function store(Request $request)
{
$name = $request->input('name');
$address = $request->input('address');
$street = $request->input('address.street');
$city = $request->input('address.city');
$zip = $request->input('address.zip');

// 处理数据
}

动态表单

对于动态表单,我们可以使用 JavaScript 来添加和删除表单字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<form action="{{ route('products.store') }}" method="POST">
@csrf

<div class="mb-3">
<label for="name" class="form-label">Product Name</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>

<div class="mb-3">
<label class="form-label">Variations</label>
<div id="variations">
<div class="variation row mb-2">
<div class="col">
<input type="text" class="form-control" name="variations[0][name]" placeholder="Variation Name">
</div>
<div class="col">
<input type="number" class="form-control" name="variations[0][price]" placeholder="Price">
</div>
<div class="col-auto">
<button type="button" class="btn btn-danger remove-variation">Remove</button>
</div>
</div>
</div>
<button type="button" class="btn btn-primary mt-2" id="add-variation">Add Variation</button>
</div>

<button type="submit" class="btn btn-primary">Submit</button>
</form>

<script>
document.addEventListener('DOMContentLoaded', function() {
let variationIndex = 1;

// 添加变体
document.getElementById('add-variation').addEventListener('click', function() {
const variationsContainer = document.getElementById('variations');
const variationDiv = document.createElement('div');
variationDiv.className = 'variation row mb-2';
variationDiv.innerHTML = `
<div class="col">
<input type="text" class="form-control" name="variations[${variationIndex}][name]" placeholder="Variation Name">
</div>
<div class="col">
<input type="number" class="form-control" name="variations[${variationIndex}][price]" placeholder="Price">
</div>
<div class="col-auto">
<button type="button" class="btn btn-danger remove-variation">Remove</button>
</div>
`;
variationsContainer.appendChild(variationDiv);

// 添加删除事件
variationDiv.querySelector('.remove-variation').addEventListener('click', function() {
variationDiv.remove();
});

variationIndex++;
});

// 删除变体
document.querySelectorAll('.remove-variation').forEach(button => {
button.addEventListener('click', function() {
this.closest('.variation').remove();
});
});
});
</script>

在控制器中处理动态表单数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function store(Request $request)
{
$name = $request->input('name');
$variations = $request->input('variations');

foreach ($variations as $variation) {
$variationName = $variation['name'];
$variationPrice = $variation['price'];

// 处理变体数据
}

// 处理其他数据
}

现在,我们已经了解了 Laravel 的表单处理,接下来我们将学习 Laravel 的认证系统。

7. 认证系统

认证系统是 Web 应用中的重要组成部分,Laravel 提供了强大的内置认证功能,包括用户注册、登录、密码重置等。

7.1 认证系统基础

Laravel 的认证系统基于以下组件:

  • 认证控制器:处理用户注册、登录、密码重置等请求
  • 认证中间件:保护需要认证的路由
  • 认证视图:提供用户界面
  • 用户模型:存储用户信息
  • 密码哈希:安全存储密码

7.2 快速开始

Laravel 提供了 auth 命令来快速生成认证系统:

1
2
3
4
5
6
7
8
9
10
11
# 生成认证系统
php artisan make:auth

# 在 Laravel 8+ 中,使用 jetstream
composer require laravel/jetstream
php artisan jetstream:install livewire
# 或使用 inertia
php artisan jetstream:install inertia

# 运行迁移
php artisan migrate

7.3 手动实现认证

如果我们需要更灵活的认证系统,可以手动实现认证功能。

1. 用户模型

Laravel 已经默认包含了 User 模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// app/Models/User.php
namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
use HasFactory, Notifiable;

/**
* 可批量赋值的属性
*/
protected $fillable = [
'name',
'email',
'password',
];

/**
* 隐藏的属性
*/
protected $hidden = [
'password',
'remember_token',
];

/**
* 类型转换
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}

2. 认证路由

我们可以在 routes/web.php 中定义认证路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 认证路由
Route::get('/register', [AuthController::class, 'showRegistrationForm'])->name('register');
Route::post('/register', [AuthController::class, 'register']);
Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login');
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');

// 密码重置路由
Route::get('/password/reset', [ForgotPasswordController::class, 'showLinkRequestForm'])->name('password.request');
Route::post('/password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email');
Route::get('/password/reset/{token}', [ResetPasswordController::class, 'showResetForm'])->name('password.reset');
Route::post('/password/reset', [ResetPasswordController::class, 'reset'])->name('password.update');

// 需要认证的路由
Route::middleware('auth')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
});

3. 认证控制器

我们可以创建认证控制器:

1
2
3
4
# 创建认证控制器
php artisan make:controller AuthController
php artisan make:controller ForgotPasswordController
php artisan make:controller ResetPasswordController
认证控制器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// app/Http/Controllers/AuthController.php
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

class AuthController extends Controller
{
/**
* 显示注册表单
*/
public function showRegistrationForm()
{
return view('auth.register');
}

/**
* 处理注册请求
*/
public function register(Request $request)
{
// 验证请求
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);

// 创建用户
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);

// 登录用户
Auth::login($user);

// 重定向到 dashboard
return redirect()->route('dashboard');
}

/**
* 显示登录表单
*/
public function showLoginForm()
{
return view('auth.login');
}

/**
* 处理登录请求
*/
public function login(Request $request)
{
// 验证请求
$request->validate([
'email' => 'required|string|email',
'password' => 'required|string',
]);

// 尝试登录
if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) {
// 登录成功,重定向到 dashboard
return redirect()->intended('dashboard');
}

// 登录失败,重定向回登录页面
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
]);
}

/**
* 处理登出请求
*/
public function logout(Request $request)
{
Auth::logout();

$request->session()->invalidate();
$request->session()->regenerateToken();

return redirect('/');
}
}
密码重置控制器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// app/Http/Controllers/ForgotPasswordController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;

class ForgotPasswordController extends Controller
{
/**
* 显示密码重置链接请求表单
*/
public function showLinkRequestForm()
{
return view('auth.forgot-password');
}

/**
* 发送密码重置链接
*/
public function sendResetLinkEmail(Request $request)
{
$request->validate(['email' => 'required|email']);

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

return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// app/Http/Controllers/ResetPasswordController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;

class ResetPasswordController extends Controller
{
/**
* 显示密码重置表单
*/
public function showResetForm(Request $request, $token = null)
{
return view('auth.reset-password')->with(
['token' => $token, 'email' => $request->email]
);
}

/**
* 重置密码
*/
public function reset(Request $request)
{
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => 'required|min:8|confirmed',
]);

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

return $status === Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withErrors(['email' => [__($status)]]);
}
}

4. 认证视图

我们需要创建认证视图:

注册视图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<!-- resources/views/auth/register.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Register') }}</div>

<div class="card-body">
<form method="POST" action="{{ route('register') }}">
@csrf

<div class="row mb-3">
<label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Name') }}</label>

<div class="col-md-6">
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>

@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>

<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">

@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>

<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">

@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-3">
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>

<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>

<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
登录视图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<!-- resources/views/auth/login.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Login') }}</div>

<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@csrf

<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>

<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>

@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>

<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">

@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-3">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
</div>

<div class="row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Login') }}
</button>

@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
密码重置视图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!-- resources/views/auth/forgot-password.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Forgot Your Password?') }}</div>

<div class="card-body">
<form method="POST" action="{{ route('password.email') }}">
@csrf

<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>

<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>

@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!-- resources/views/auth/reset-password.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>

<div class="card-body">
<form method="POST" action="{{ route('password.update') }}">
@csrf

<input type="hidden" name="token" value="{{ $token }}">

<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>

<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>

@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>

<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">

@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>

<div class="row mb-3">
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>

<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>

<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Reset Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

5. 仪表板控制器

我们需要创建一个仪表板控制器来显示用户登录后的页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app/Http/Controllers/DashboardController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class DashboardController extends Controller
{
/**
* 显示仪表板
*/
public function index()
{
return view('dashboard');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- resources/views/dashboard.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>

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

{{ __('You are logged in!') }}
</div>
</div>
</div>
</div>
</div>
@endsection

7.4 认证中间件

Laravel 提供了 auth 中间件来保护需要认证的路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 使用中间件保护路由
Route::get('/dashboard', [DashboardController::class, 'index'])->middleware('auth');

// 使用中间件组
Route::middleware('auth')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
Route::get('/profile', [ProfileController::class, 'index']);
});

// 在控制器中使用中间件
class DashboardController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
}

7.5 认证辅助函数

Laravel 提供了多个认证辅助函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 检查用户是否已认证
if (auth()->check()) {
// 用户已认证
}

// 检查用户是否为访客
if (auth()->guest()) {
// 用户未认证
}

// 获取当前用户
$user = auth()->user();

// 获取当前用户 ID
$userId = auth()->id();

// 登录用户
auth()->login($user);

// 登出用户
auth()->logout();

// 尝试登录
auth()->attempt(['email' => $email, 'password' => $password]);

// 验证用户密码
if (Hash::check($password, $user->password)) {
// 密码正确
}

// 哈希密码
$hashedPassword = Hash::make($password);

7.6 自定义认证

自定义认证 guard

我们可以创建自定义的认证 guard 来使用不同的认证方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],

'api' => [
'driver' => 'token',
'provider' => 'users',
],

'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
],

'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],

'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
],

使用自定义 guard:

1
2
3
4
5
6
7
8
9
10
// 登录 admin
auth('admin')->attempt(['email' => $email, 'password' => $password]);

// 获取当前 admin
$admin = auth('admin')->user();

// 保护路由
Route::middleware('auth:admin')->group(function () {
// 管理员路由
});

自定义认证字段

我们可以修改认证逻辑来使用不同的字段进行认证:

1
2
3
4
5
6
7
8
// 使用用户名认证
auth()->attempt(['username' => $username, 'password' => $password]);

// 在 LoginController 中修改
public function username()
{
return 'username';
}

7.7 邮箱验证

Laravel 提供了内置的邮箱验证功能:

1. 更新用户模型

1
2
3
4
5
// app/Models/User.php
class User extends Authenticatable implements MustVerifyEmail
{
// ...
}

2. 注册路由

1
2
3
4
// routes/web.php
Route::get('/email/verify', [VerificationController::class, 'show'])->name('verification.notice');
Route::get('/email/verify/{id}/{hash}', [VerificationController::class, 'verify'])->name('verification.verify');
Route::post('/email/resend', [VerificationController::class, 'resend'])->name('verification.resend');

3. 创建验证控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// app/Http/Controllers/VerificationController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\EmailVerificationRequest;

class VerificationController extends Controller
{
/**
* 显示邮箱验证通知
*/
public function show()
{
return view('auth.verify-email');
}

/**
* 验证邮箱
*/
public function verify(EmailVerificationRequest $request)
{
$request->fulfill();

return redirect()->route('dashboard');
}

/**
* 重新发送验证邮件
*/
public function resend(Request $request)
{
$request->user()->sendEmailVerificationNotification();

return back()->with('message', 'Verification link sent!');
}
}

4. 创建验证视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- resources/views/auth/verify-email.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Verify Your Email Address') }}</div>

<div class="card-body">
@if (session('message'))
<div class="alert alert-success" role="alert">
{{ session('message') }}
</div>
@endif

{{ __('Before proceeding, please check your email for a verification link.') }}
{{ __('If you did not receive the email') }},
<form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
@csrf
<button type="submit" class="btn btn-link p-0 m-0 align-baseline">{{ __('click here to request another') }}</button>.
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

5. 保护需要验证的路由

1
2
3
4
// 使用 verified 中间件
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});

7.8 实战示例

现在,让我们为之前创建的博客应用添加认证功能:

  1. 创建认证控制器

    1
    2
    php artisan make:controller AuthController
    php artisan make:controller DashboardController
  2. 编辑认证控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    namespace App\Http\Controllers;

    use App\Models\User;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Auth;
    use Illuminate\Support\Facades\Hash;

    class AuthController extends Controller
    {
    public function showRegistrationForm()
    {
    return view('auth.register');
    }

    public function register(Request $request)
    {
    $request->validate([
    'name' => 'required|string|max:255',
    'email' => 'required|string|email|max:255|unique:users',
    'password' => 'required|string|min:8|confirmed',
    ]);

    $user = User::create([
    'name' => $request->name,
    'email' => $request->email,
    'password' => Hash::make($request->password),
    ]);

    Auth::login($user);

    return redirect()->route('dashboard');
    }

    public function showLoginForm()
    {
    return view('auth.login');
    }

    public function login(Request $request)
    {
    $request->validate([
    'email' => 'required|string|email',
    'password' => 'required|string',
    ]);

    if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) {
    return redirect()->intended('dashboard');
    }

    return back()->withErrors([
    'email' => 'The provided credentials do not match our records.',
    ]);
    }

    public function logout(Request $request)
    {
    Auth::logout();

    $request->session()->invalidate();
    $request->session()->regenerateToken();

    return redirect('/');
    }
    }
  3. 编辑仪表板控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    namespace App\Http\Controllers;

    use Illuminate\Http\Request;

    class DashboardController extends Controller
    {
    public function __construct()
    {
    $this->middleware('auth');
    }

    public function index()
    {
    return view('dashboard');
    }
    }
  4. 创建认证视图

    • 创建 resources/views/auth/register.blade.php
    • 创建 resources/views/auth/login.blade.php
    • 创建 resources/views/dashboard.blade.php
  5. 定义路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 认证路由
    Route::get('/register', [AuthController::class, 'showRegistrationForm'])->name('register');
    Route::post('/register', [AuthController::class, 'register']);
    Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login');
    Route::post('/login', [AuthController::class, 'login']);
    Route::post('/logout', [AuthController::class, 'logout'])->name('logout');

    // 仪表板路由
    Route::middleware('auth')->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
    });
  6. 更新布局文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    <!-- resources/views/layouts/app.blade.php -->
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container">
    <a class="navbar-brand" href="{{ route('posts.index') }}">Blog</a>
    <div class="collapse navbar-collapse">
    <ul class="navbar-nav mr-auto">
    <li class="nav-item">
    <a class="nav-link" href="{{ route('posts.index') }}">Home</a>
    </li>
    @auth
    <li class="nav-item">
    <a class="nav-link" href="{{ route('posts.create') }}">Create Post</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" href="{{ route('dashboard') }}">Dashboard</a>
    </li>
    @endauth
    </ul>
    <ul class="navbar-nav ml-auto">
    @guest
    <li class="nav-item">
    <a class="nav-link" href="{{ route('login') }}">Login</a>
    </li>
    <li class="nav-item">
    <a class="nav-link" href="{{ route('register') }}">Register</a>
    </li>
    @else
    <li class="nav-item dropdown">
    <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    {{ Auth::user()->name }}
    </a>
    <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
    <a class="dropdown-item" href="{{ route('dashboard') }}">Dashboard</a>
    <form method="POST" action="{{ route('logout') }}">
    @csrf
    <button type="submit" class="dropdown-item">Logout</button>
    </form>
    </div>
    </li>
    @endguest
    </ul>
    </div>
    </div>
    </nav>
  7. 运行迁移

    1
    php artisan migrate

现在,我们的博客应用已经具备了完整的认证功能,包括用户注册、登录和注销。

7.9 安全建议

在实现认证系统时,我们应该遵循以下安全建议:

  1. 使用密码哈希:永远不要明文存储密码,使用 Laravel 提供的 Hash 门面来哈希密码
  2. 使用 HTTPS:在生产环境中使用 HTTPS 来保护用户数据
  3. 实现 CSRF 保护:使用 Laravel 的 CSRF 保护来防止跨站请求伪造攻击
  4. 限制登录尝试:实现登录尝试限制,防止暴力破解
  5. 使用安全的会话管理:使用 Laravel 的会话管理功能
  6. 实现邮箱验证:验证用户邮箱,防止垃圾注册
  7. 使用强密码策略:要求用户使用强密码
  8. 定期更新密码:鼓励用户定期更新密码
  9. 实现双因素认证:对于敏感操作,实现双因素认证
  10. 安全存储令牌:安全存储重置密码令牌等临时令牌

现在,我们已经了解了 Laravel 的认证系统,接下来我们将学习 Laravel 的文件上传和存储功能。

8. 文件上传和存储

文件上传和存储是 Web 应用中常见的功能,Laravel 提供了强大的文件处理功能,支持本地存储和云存储。

8.1 文件上传基础

1. 表单设置

要上传文件,我们需要在表单中添加 enctype="multipart/form-data" 属性:

1
2
3
4
5
6
7
8
<form method="POST" action="{{ route('upload') }}" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label for="file" class="form-label">Upload File</label>
<input type="file" class="form-control" id="file" name="file">
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</form>

2. 处理上传

在控制器中,我们可以使用 $request->file() 方法来获取上传的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// app/Http/Controllers/UploadController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UploadController extends Controller
{
/**
* 处理文件上传
*/
public function upload(Request $request)
{
// 验证文件
$request->validate([
'file' => 'required|file|max:1024', // 1MB 限制
]);

// 获取上传的文件
$file = $request->file('file');

// 检查文件是否上传成功
if ($file->isValid()) {
// 存储文件
$path = $file->store('uploads');
// 或使用自定义文件名
// $path = $file->storeAs('uploads', 'custom-name.jpg');

// 返回成功信息
return back()->with('success', 'File uploaded successfully!')->with('path', $path);
}

// 返回错误信息
return back()->with('error', 'File upload failed!');
}
}

3. 路由定义

1
2
3
// routes/web.php
Route::get('/upload', [UploadController::class, 'showForm'])->name('upload.form');
Route::post('/upload', [UploadController::class, 'upload'])->name('upload');

8.2 本地存储

Laravel 的本地存储使用 storage/app 目录作为根目录。我们可以通过配置文件 config/filesystems.php 来修改存储设置。

1. 存储磁盘

Laravel 支持多种存储磁盘,默认包括:

  • local:本地存储,使用 storage/app 目录
  • public:公共存储,使用 storage/app/public 目录
  • s3:Amazon S3 存储

2. 公共存储

对于需要公开访问的文件,我们可以使用公共存储:

1
2
3
4
5
// 存储到公共磁盘
$path = $file->store('images', 'public');

// 创建符号链接(首次使用时需要)
php artisan storage:link

创建符号链接后,我们可以通过以下 URL 访问文件:

1
<img src="{{ asset('storage/' . $path) }}" alt="Image">

3. 存储路径

Laravel 提供了多个辅助函数来获取存储路径:

1
2
3
4
5
6
7
8
9
10
11
// 获取存储根路径
$root = storage_path();

// 获取 app 目录路径
$appPath = storage_path('app');

// 获取 public 目录路径
$publicPath = storage_path('app/public');

// 获取 uploads 目录路径
$uploadsPath = storage_path('app/uploads');

8.3 云存储

Laravel 支持多种云存储服务,包括 Amazon S3、Google Cloud Storage、Azure Blob Storage 等。

1. Amazon S3

安装依赖

1
composer require league/flysystem-aws-s3-v3

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// config/filesystems.php
'disks' => [
// ...
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
],
],

使用

1
2
3
4
5
// 存储到 S3
$path = $file->store('images', 's3');

// 获取 S3 文件 URL
$url = Storage::disk('s3')->url($path);

2. Google Cloud Storage

安装依赖

1
composer require league/flysystem-google-cloud-storage

配置

1
2
3
4
5
6
7
8
9
10
11
12
// config/filesystems.php
'disks' => [
// ...
'gcs' => [
'driver' => 'gcs',
'project_id' => env('GOOGLE_CLOUD_PROJECT_ID'),
'key_file' => env('GOOGLE_CLOUD_KEY_FILE'), // 可选
'bucket' => env('GOOGLE_CLOUD_STORAGE_BUCKET'),
'path_prefix' => env('GOOGLE_CLOUD_STORAGE_PATH_PREFIX'), // 可选
'storage_api_uri' => env('GOOGLE_CLOUD_STORAGE_API_URI'), // 可选
],
],

使用

1
2
3
4
5
// 存储到 Google Cloud Storage
$path = $file->store('images', 'gcs');

// 获取文件 URL
$url = Storage::disk('gcs')->url($path);

8.4 文件验证

在上传文件时,我们应该验证文件的类型、大小等属性:

1. 基本验证规则

1
2
3
4
5
6
7
8
9
10
$request->validate([
// 验证文件存在且是有效文件
'file' => 'required|file',
// 验证文件大小(1MB)
'file' => 'required|file|max:1024',
// 验证文件类型
'file' => 'required|file|mimes:jpg,jpeg,png,gif',
// 验证文件类型和大小
'file' => 'required|file|mimes:jpg,jpeg,png|max:2048',
]);

2. 自定义验证规则

我们可以创建自定义验证规则来验证文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// app/Rules/FileExtension.php
namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class FileExtension implements Rule
{
/**
* 允许的扩展名
*/
protected $extensions;

/**
* 创建新的验证规则实例
*/
public function __construct($extensions)
{
$this->extensions = $extensions;
}

/**
* 验证规则
*/
public function passes($attribute, $value)
{
$extension = $value->getClientOriginalExtension();
return in_array(strtolower($extension), $this->extensions);
}

/**
* 获取验证失败的错误消息
*/
public function message()
{
return 'The :attribute must be a file of type: ' . implode(', ', $this->extensions);
}
}

使用自定义规则

1
2
3
4
5
use App\Rules\FileExtension;

$request->validate([
'file' => ['required', 'file', new FileExtension(['jpg', 'jpeg', 'png'])],
]);

3. 验证图片尺寸

我们可以使用 dimensions 规则来验证图片尺寸:

1
2
3
4
5
6
7
8
9
10
$request->validate([
// 验证图片最小尺寸
'image' => 'required|image|dimensions:min_width=100,min_height=100',
// 验证图片最大尺寸
'image' => 'required|image|dimensions:max_width=1000,max_height=1000',
// 验证图片尺寸范围
'image' => 'required|image|dimensions:min_width=100,min_height=100,max_width=1000,max_height=1000',
// 验证图片比例
'image' => 'required|image|dimensions:ratio=16/9',
]);

8.5 文件管理

Laravel 提供了 Storage 门面来管理文件:

1. 存储文件

1
2
3
4
5
6
7
8
9
10
11
12
13
use Illuminate\Support\Facades\Storage;

// 存储文件
Storage::put('file.txt', 'Contents');

// 存储文件(指定磁盘)
Storage::disk('public')->put('file.txt', 'Contents');

// 存储上传的文件
Storage::putFile('photos', $request->file('photo'));

// 存储上传的文件(指定磁盘)
Storage::disk('s3')->putFile('photos', $request->file('photo'));

2. 读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
// 读取文件内容
$contents = Storage::get('file.txt');

// 检查文件是否存在
if (Storage::exists('file.txt')) {
// 文件存在
}

// 获取文件大小
$size = Storage::size('file.txt');

// 获取文件修改时间
$time = Storage::lastModified('file.txt');

3. 下载文件

1
2
3
4
5
6
7
8
9
10
// 下载文件
return Storage::download('file.txt');

// 下载文件(自定义文件名)
return Storage::download('file.txt', 'custom-name.txt');

// 下载文件(自定义头信息)
return Storage::download('file.txt', 'custom-name.txt', [
'Content-Type' => 'text/plain',
]);

4. 删除文件

1
2
3
4
5
6
7
8
// 删除文件
Storage::delete('file.txt');

// 删除多个文件
Storage::delete(['file1.txt', 'file2.txt']);

// 清空目录
Storage::deleteDirectory('photos');

5. 目录操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 创建目录
Storage::makeDirectory('photos');

// 删除目录
Storage::deleteDirectory('photos');

// 列出目录中的文件
$files = Storage::files('photos');

// 列出目录中的所有文件(包括子目录)
$files = Storage::allFiles('photos');

// 列出目录
$directories = Storage::directories('photos');

// 列出所有目录(包括子目录)
$directories = Storage::allDirectories('photos');

8.6 实战示例

现在,让我们为之前创建的博客应用添加图片上传功能:

1. 更新帖子迁移

1
2
3
4
5
6
7
8
9
10
11
// database/migrations/xxxx_xx_xx_create_posts_table.php
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->string('image')->nullable(); // 添加图片字段
$table->timestamps();
});
}

2. 更新帖子模型

1
2
3
4
5
6
// app/Models/Post.php
protected $fillable = [
'title',
'content',
'image', // 添加图片字段
];

3. 更新帖子表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- resources/views/posts/create.blade.php -->
<form method="POST" action="{{ route('posts.store') }}" enctype="multipart/form-data">
@csrf

<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control @error('title') is-invalid @enderror"
id="title" name="title" value="{{ old('title') }}" required>
@error('title')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>

<div class="mb-3">
<label for="content" class="form-label">Content</label>
<textarea class="form-control @error('content') is-invalid @enderror"
id="content" name="content" rows="3" required>{{ old('content') }}</textarea>
@error('content')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>

<div class="mb-3">
<label for="image" class="form-label">Image</label>
<input type="file" class="form-control @error('image') is-invalid @enderror"
id="image" name="image">
@error('image')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>

<button type="submit" class="btn btn-primary">Submit</button>
</form>

4. 更新帖子控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// app/Http/Controllers/PostController.php
use Illuminate\Http\Request;
use App\Models\Post;

class PostController extends Controller
{
// ...

/**
* 存储新帖子
*/
public function store(Request $request)
{
// 验证请求
$request->validate([
'title' => 'required|min:3|max:255',
'content' => 'required|min:10',
'image' => 'nullable|image|max:2048', // 验证图片
]);

// 处理图片上传
$imagePath = null;
if ($request->hasFile('image')) {
$imagePath = $request->file('image')->store('posts', 'public');
}

// 创建帖子
$post = Post::create([
'title' => $request->title,
'content' => $request->content,
'image' => $imagePath, // 存储图片路径
]);

// 重定向到帖子详情页
return redirect()->route('posts.show', $post->id)
->with('success', 'Post created successfully!');
}

/**
* 更新帖子
*/
public function update(Request $request, Post $post)
{
// 验证请求
$request->validate([
'title' => 'required|min:3|max:255',
'content' => 'required|min:10',
'image' => 'nullable|image|max:2048', // 验证图片
]);

// 处理图片上传
if ($request->hasFile('image')) {
// 删除旧图片
if ($post->image) {
Storage::disk('public')->delete($post->image);
}
// 存储新图片
$imagePath = $request->file('image')->store('posts', 'public');
} else {
// 保持旧图片
$imagePath = $post->image;
}

// 更新帖子
$post->update([
'title' => $request->title,
'content' => $request->content,
'image' => $imagePath, // 更新图片路径
]);

// 重定向到帖子详情页
return redirect()->route('posts.show', $post->id)
->with('success', 'Post updated successfully!');
}

/**
* 删除帖子
*/
public function destroy(Post $post)
{
// 删除图片
if ($post->image) {
Storage::disk('public')->delete($post->image);
}

// 删除帖子
$post->delete();

// 重定向到帖子列表页
return redirect()->route('posts.index')
->with('success', 'Post deleted successfully!');
}
}

5. 更新帖子视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- resources/views/posts/show.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<h1>{{ $post->title }}</h1>
@if ($post->image)
<img src="{{ asset('storage/' . $post->image) }}" alt="{{ $post->title }}" class="img-fluid mb-4">
@endif
<div class="card">
<div class="card-body">
{{ $post->content }}
</div>
</div>
<div class="mt-4">
<a href="{{ route('posts.edit', $post->id) }}" class="btn btn-primary">Edit</a>
<form action="{{ route('posts.destroy', $post->id) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure?');">Delete</button>
</form>
</div>
</div>
</div>
</div>
@endsection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- resources/views/posts/index.blade.php -->
@extends('layouts.app')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<h1>Posts</h1>
@auth
<a href="{{ route('posts.create') }}" class="btn btn-primary mb-4">Create Post</a>
@endauth
@foreach ($posts as $post)
<div class="card mb-4">
@if ($post->image)
<img src="{{ asset('storage/' . $post->image) }}" alt="{{ $post->title }}" class="card-img-top">
@endif
<div class="card-body">
<h2 class="card-title">{{ $post->title }}</h2>
<p class="card-text">{{ Str::limit($post->content, 100) }}</p>
<a href="{{ route('posts.show', $post->id) }}" class="btn btn-primary">Read More</a>
</div>
</div>
@endforeach
</div>
</div>
</div>
@endsection

6. 创建符号链接

1
php artisan storage:link

现在,我们的博客应用已经具备了图片上传功能,用户可以在创建和编辑帖子时上传图片。

8.7 安全建议

在实现文件上传功能时,我们应该遵循以下安全建议:

  1. 验证文件类型:使用 mimesimage 规则验证文件类型
  2. 限制文件大小:使用 max 规则限制文件大小,防止上传过大的文件
  3. 重命名文件:使用 storeAs 方法重命名上传的文件,避免文件名冲突和安全问题
  4. 使用安全的存储位置:对于敏感文件,使用本地存储而不是公共存储
  5. 扫描上传的文件:使用第三方库扫描上传的文件,防止恶意文件
  6. 设置适当的文件权限:确保存储目录有适当的文件权限
  7. 使用 HTTPS:在生产环境中使用 HTTPS 传输文件
  8. 实现文件上传速率限制:防止恶意用户上传大量文件
  9. 使用内容安全策略:限制可以从哪些来源加载文件
  10. 定期清理未使用的文件:定期清理未使用的上传文件,节省存储空间

8.8 高级技巧

1. 图片处理

我们可以使用 intervention/image 库来处理图片:

安装依赖

1
composer require intervention/image

配置

1
2
3
4
5
6
7
8
9
10
// config/app.php
'providers' => [
// ...
Intervention\Image\ImageServiceProvider::class,
],

'aliases' => [
// ...
'Image' => Intervention\Image\Facades\Image::class,
],

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Intervention\Image\Facades\Image;

// 处理上传的图片
if ($request->hasFile('image')) {
$image = $request->file('image');

// 生成唯一文件名
$filename = time() . '.' . $image->getClientOriginalExtension();

// 调整图片大小
Image::make($image)->resize(800, 600, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save(public_path('storage/posts/' . $filename));

// 存储图片路径
$imagePath = 'posts/' . $filename;
}

2. 多文件上传

我们可以使用数组形式的文件输入来上传多个文件:

1
2
3
4
5
6
7
8
<form method="POST" action="{{ route('upload.multiple') }}" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label for="files" class="form-label">Upload Files</label>
<input type="file" class="form-control" id="files" name="files[]" multiple>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</form>

处理多文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function uploadMultiple(Request $request)
{
// 验证文件
$request->validate([
'files.*' => 'required|file|max:1024',
]);

// 存储文件
$paths = [];
foreach ($request->file('files') as $file) {
$paths[] = $file->store('uploads', 'public');
}

// 返回成功信息
return back()->with('success', 'Files uploaded successfully!')->with('paths', $paths);
}

3. 远程文件上传

我们可以使用 Storage 门面来上传远程文件:

1
2
3
4
5
6
7
8
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Http;

// 下载远程文件
$response = Http::get('https://example.com/image.jpg');

// 存储远程文件
Storage::put('remote-image.jpg', $response->body());

现在,我们已经了解了 Laravel 的文件上传和存储功能,接下来我们将学习 Laravel 的部署方法。

9. 部署

部署是将 Laravel 应用从开发环境转移到生产环境的过程。在本章节中,我们将学习如何部署 Laravel 应用到不同的环境。

9.1 本地服务器

在开发过程中,我们可以使用 Laravel 内置的开发服务器来测试应用:

1
2
3
4
5
6
7
8
# 启动开发服务器
php artisan serve

# 指定端口
php artisan serve --port=8080

# 指定主机和端口
php artisan serve --host=0.0.0.0 --port=8080

开发服务器默认运行在 http://localhost:8000

9.2 Laravel Valet

Laravel Valet 是为 macOS 设计的轻量级开发环境,提供了快速的本地开发体验:

1. 安装 Valet

1
2
3
4
5
6
7
8
9
10
11
12
13
# 安装 Valet
composer global require laravel/valet

# 安装 Valet 服务
valet install

# park 当前目录
cd ~/Sites
valet park

# 或 link 特定目录
cd ~/Sites/laravel-app
valet link

2. 使用 Valet

1
2
3
4
5
6
7
8
9
10
11
# 查看所有站点
valet sites

# 重启 Valet
valet restart

# 停止 Valet
valet stop

# 卸载 Valet
valet uninstall

9.3 部署前准备

在部署 Laravel 应用之前,我们需要进行一些准备工作:

1. 环境变量

确保在 .env 文件中设置了正确的环境变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 应用配置
APP_NAME="Laravel"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://example.com

# 数据库配置
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

# 邮件配置
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

2. 生成应用密钥

1
2
# 生成应用密钥
php artisan key:generate

3. 运行迁移

1
2
3
4
5
# 运行迁移
php artisan migrate --force

# 运行种子
php artisan db:seed --force

4. 优化应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 缓存配置
php artisan config:cache

# 缓存路由
php artisan route:cache

# 缓存视图
php artisan view:cache

# 清除缓存
php artisan cache:clear

# 清除已编译的类文件
php artisan clear-compiled

# 优化自动加载
composer dump-autoload --optimize

9.4 生产环境部署

1. 使用 Git

我们可以使用 Git 来部署 Laravel 应用:

步骤 1:在服务器上初始化 Git 仓库

1
2
3
4
5
6
# 在服务器上创建目录
mkdir -p /var/www/laravel-app
cd /var/www/laravel-app

# 初始化 Git 仓库
git init --bare

步骤 2:创建 post-receive 钩子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 创建钩子目录
mkdir -p /var/www/laravel-app/hooks

# 创建 post-receive 钩子
cat > /var/www/laravel-app/hooks/post-receive << 'EOF'
#!/bin/bash

# 设置目标目录
TARGET_DIR="/var/www/html/laravel-app"

# 确保目标目录存在
mkdir -p "$TARGET_DIR"

# 切换到目标目录
cd "$TARGET_DIR"

# 从裸仓库检出代码
GIT_WORK_TREE="$TARGET_DIR" git checkout -f

# 安装依赖
composer install --optimize-autoloader --no-dev

# 生成应用密钥
php artisan key:generate --force

# 运行迁移
php artisan migrate --force

# 优化应用
php artisan config:cache
php artisan route:cache
php artisan view:cache

# 设置权限
chown -R www-data:www-data "$TARGET_DIR"
chmod -R 755 "$TARGET_DIR/storage"
chmod -R 755 "$TARGET_DIR/bootstrap/cache"

# 重启 PHP-FPM
systemctl restart php8.1-fpm
EOF

# 设置执行权限
chmod +x /var/www/laravel-app/hooks/post-receive

步骤 3:在本地添加远程仓库

1
2
3
4
5
# 添加远程仓库
git remote add production ssh://user@server:/var/www/laravel-app

# 推送代码
git push production master

2. 使用 Laravel Envoyer

Laravel Envoyer 是一个自动化部署服务,可以轻松部署 Laravel 应用:

步骤 1:创建 Envoyer 项目

  1. 登录 Laravel Envoyer
  2. 创建新项目
  3. 连接到 GitHub、GitLab 或 Bitbucket 仓库

步骤 2:配置服务器

  1. 添加服务器
  2. 设置部署路径
  3. 配置 SSH 密钥

步骤 3:配置部署钩子

  1. 设置部署前钩子
  2. 设置部署后钩子
  3. 配置通知

步骤 4:部署应用

  1. 触发部署
  2. 监控部署过程
  3. 查看部署日志

3. 使用 Laravel Forge

Laravel Forge 是一个服务器管理和部署服务,可以轻松创建和管理服务器:

步骤 1:创建 Forge 账户

  1. 登录 Laravel Forge
  2. 连接到服务器提供商(DigitalOcean、AWS、Linode 等)

步骤 2:创建服务器

  1. 选择服务器提供商
  2. 配置服务器规格
  3. 部署服务器

步骤 3:创建站点

  1. 添加新站点
  2. 配置域名
  3. 连接到 Git 仓库

步骤 4:部署应用

  1. 触发部署
  2. 配置环境变量
  3. 运行迁移

9.5 服务器配置

1. Nginx 配置

基本配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
server {
listen 80;
server_name example.com;
root /var/www/html/laravel-app/public;

add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";

index index.php;

charset utf-8;

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location = /favicon.ico {
access_log off;
log_not_found off;
}

location = /robots.txt {
access_log off;
log_not_found off;
}

error_page 404 /index.php;

location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}

location ~ /\.(?!well-known).* {
deny all;
}
}

使用 HTTPS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2;
server_name example.com;
root /var/www/html/laravel-app/public;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

index index.php;

charset utf-8;

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location = /favicon.ico {
access_log off;
log_not_found off;
}

location = /robots.txt {
access_log off;
log_not_found off;
}

error_page 404 /index.php;

location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}

location ~ /\.(?!well-known).* {
deny all;
}
}

2. Apache 配置

基本配置

1
2
3
4
5
6
7
8
9
10
11
12
13
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/html/laravel-app/public

<Directory /var/www/html/laravel-app/public>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>

ErrorLog ${APACHE_LOG_DIR}/laravel-app-error.log
CustomLog ${APACHE_LOG_DIR}/laravel-app-access.log combined
</VirtualHost>

使用 HTTPS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>

<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/html/laravel-app/public

SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

<Directory /var/www/html/laravel-app/public>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>

ErrorLog ${APACHE_LOG_DIR}/laravel-app-error.log
CustomLog ${APACHE_LOG_DIR}/laravel-app-access.log combined
</VirtualHost>

9.5 数据库配置

1. MySQL

安装 MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 安装 MySQL
apt update
apt install mysql-server

# 配置 MySQL
mysql_secure_installation

# 创建数据库和用户
mysql -u root -p

CREATE DATABASE laravel_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'laravel_user'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON laravel_app.* TO 'laravel_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

配置 Laravel

1
2
3
4
5
6
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_app
DB_USERNAME=laravel_user
DB_PASSWORD=password

2. PostgreSQL

安装 PostgreSQL

1
2
3
4
5
6
7
8
9
10
11
12
13
# 安装 PostgreSQL
apt update
apt install postgresql postgresql-contrib

# 登录 PostgreSQL
sudo -u postgres psql

# 创建数据库和用户
CREATE DATABASE laravel_app;
CREATE USER laravel_user WITH PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE laravel_app TO laravel_user;
ALTER USER laravel_user WITH SUPERUSER;
\q

配置 Laravel

1
2
3
4
5
6
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=laravel_app
DB_USERNAME=laravel_user
DB_PASSWORD=password

9.6 环境变量配置

在生产环境中,我们应该使用 .env 文件来存储敏感信息:

1. 创建 .env 文件

1
2
3
4
5
# 复制 .env.example 文件
cp .env.example .env

# 编辑 .env 文件
nano .env

2. 环境变量最佳实践

  • 不要提交 .env 文件:将 .env 文件添加到 .gitignore
  • 使用不同的环境变量:为开发、测试和生产环境使用不同的 .env 文件
  • 使用环境变量管理服务:如 Laravel Envoyer、AWS Parameter Store 或 HashiCorp Vault
  • 设置适当的权限:确保 .env 文件的权限为 600

9.7 优化策略

1. 缓存优化

1
2
3
4
5
6
7
8
9
10
11
# 缓存配置
php artisan config:cache

# 缓存路由
php artisan route:cache

# 缓存视图
php artisan view:cache

# 清除缓存
php artisan cache:clear

2. 数据库优化

  • 使用索引:为经常查询的列添加索引
  • 使用查询构建器:使用 Laravel 的查询构建器来优化查询
  • 使用 Eager Loading:避免 N+1 查询问题
  • 使用数据库连接池:在高流量应用中使用连接池

3. 前端优化

  • 压缩资源:使用 Laravel Mix 或 Vite 来压缩 CSS 和 JavaScript
  • 使用 CDN:使用内容分发网络来加速静态资源
  • 启用浏览器缓存:设置适当的缓存头
  • 使用 HTTP/2:启用 HTTP/2 来加速资源加载

4. 服务器优化

  • 使用 OPcache:启用 PHP OPcache 来提高性能
  • 使用 Redis:使用 Redis 作为缓存和会话存储
  • 使用队列:将耗时任务放入队列
  • 使用负载均衡:在高流量应用中使用负载均衡

9.8 常见问题解决

1. 权限问题

问题storage 目录权限不足

解决

1
2
3
4
# 设置权限
chown -R www-data:www-data /var/www/html/laravel-app
chmod -R 755 /var/www/html/laravel-app/storage
chmod -R 755 /var/www/html/laravel-app/bootstrap/cache

2. 500 错误

问题:服务器返回 500 错误

解决

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看日志
cat /var/log/nginx/error.log
cat /var/log/php8.1-fpm.log

# 检查 .env 文件
cat .env

# 生成应用密钥
php artisan key:generate

# 清除缓存
php artisan cache:clear
php artisan config:cache

3. 数据库连接问题

问题:无法连接到数据库

解决

1
2
3
4
5
6
7
8
# 检查数据库服务状态
systemctl status mysql

# 测试数据库连接
mysql -u laravel_user -p laravel_app

# 检查 .env 文件中的数据库配置
cat .env

4. 路由问题

问题:路由返回 404 错误

解决

1
2
3
4
5
6
7
8
9
10
11
12
# 清除路由缓存
php artisan route:clear
php artisan route:cache

# 查看路由
php artisan route:list

# 检查 .htaccess 文件(Apache)
cat public/.htaccess

# 检查 Nginx 配置
cat /etc/nginx/sites-available/laravel-app

9.9 云服务部署

1. AWS

使用 AWS Elastic Beanstalk

  1. 安装 EB CLI

    1
    pip install awsebcli
  2. 初始化 EB 环境

    1
    2
    cd laravel-app
    eb init
  3. 创建环境

    1
    eb create production-env
  4. 部署应用

    1
    eb deploy

使用 AWS EC2

  1. 创建 EC2 实例
  2. 安装 LAMP/LEMP 堆栈
  3. 部署 Laravel 应用
  4. 配置安全组

2. DigitalOcean

使用 DigitalOcean Droplets

  1. 创建 Droplet
  2. 选择 LAMP/LEMP 一键应用
  3. 部署 Laravel 应用
  4. 配置域名和 SSL

使用 DigitalOcean App Platform

  1. 连接 GitHub 仓库
  2. 选择 Laravel 应用
  3. 配置环境变量
  4. 部署应用

3. Vercel

使用 Vercel

  1. 安装 Vercel CLI

    1
    npm install -g vercel
  2. 部署应用

    1
    2
    cd laravel-app
    vercel
  3. 配置环境变量

    1
    vercel env add

4. Heroku

使用 Heroku

  1. 安装 Heroku CLI

    1
    npm install -g heroku
  2. 登录 Heroku

    1
    heroku login
  3. 创建 Heroku 应用

    1
    heroku create laravel-app
  4. 部署应用

    1
    git push heroku master
  5. 配置环境变量

    1
    2
    3
    4
    heroku config:set APP_KEY=$(php artisan key:generate --show)
    heroku config:set APP_ENV=production
    heroku config:set APP_DEBUG=false
    heroku config:set APP_URL=$(heroku apps:info -s | grep web_url | cut -d= -f2)

9.10 容器化部署

1. 使用 Docker

步骤 1:创建 Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 使用官方 PHP 镜像
FROM php:8.1-fpm

# 设置工作目录
WORKDIR /var/www/html

# 安装依赖
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip \
nginx \
supervisor

# 清理缓存
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# 安装 PHP 扩展
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

# 安装 Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# 复制配置文件
COPY nginx.conf /etc/nginx/nginx.conf
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# 复制应用代码
COPY . /var/www/html

# 安装依赖
RUN composer install --optimize-autoloader --no-dev

# 生成应用密钥
RUN php artisan key:generate

# 设置权限
RUN chown -R www-data:www-data /var/www/html
RUN chmod -R 755 /var/www/html/storage
RUN chmod -R 755 /var/www/html/bootstrap/cache

# 暴露端口
EXPOSE 80

# 启动服务
CMD ["/usr/bin/supervisord"]

步骤 2:创建 docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: '3'
services:
app:
build: .
ports:
- "8000:80"
volumes:
- .:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: laravel_app
MYSQL_USER: laravel_user
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:

步骤 3:构建和运行

1
2
3
4
5
6
7
8
9
10
11
# 构建镜像
docker-compose build

# 运行容器
docker-compose up -d

# 查看日志
docker-compose logs

# 进入容器
docker-compose exec app bash

2. 使用 Docker Hub

  1. 构建镜像

    1
    docker build -t username/laravel-app .
  2. 推送镜像

    1
    docker push username/laravel-app
  3. 拉取和运行镜像

    1
    2
    docker pull username/laravel-app
    docker run -p 8000:80 username/laravel-app

9.11 监控和维护

1. 日志监控

  • Laravel 日志storage/logs/laravel.log
  • 服务器日志/var/log/nginx/error.log/var/log/php8.1-fpm.log
  • 使用监控服务:如 Papertrail、Logentries 或 AWS CloudWatch

2. 性能监控

  • 使用 Laravel Telescope:在开发环境中监控应用
  • 使用 New Relic:监控生产环境性能
  • 使用 Datadog:监控服务器和应用性能
  • 使用 Blackfire:分析应用性能

3. 定期维护

  • 备份数据库:定期备份数据库
  • 更新依赖:定期更新 Composer 依赖
  • 更新 Laravel:定期更新 Laravel 版本
  • 清理日志:定期清理日志文件
  • 清理缓存:定期清理缓存

9.12 实战示例

现在,让我们部署之前创建的博客应用:

1. 准备部署

1
2
3
4
5
6
7
8
9
10
11
# 进入项目目录
cd laravel-app

# 确保所有更改已提交
git status
git add .
git commit -m "Prepare for deployment"

# 创建 .env 文件
cp .env.example .env
nano .env

2. 配置服务器

安装必要的软件

1
2
3
4
5
6
7
8
9
# 更新系统
apt update && apt upgrade -y

# 安装必要的软件
apt install -y nginx php8.1 php8.1-fpm php8.1-mysql php8.1-cli php8.1-mbstring php8.1-xml php8.1-zip php8.1-gd git composer mysql-server

# 启动服务
systemctl start nginx php8.1-fpm mysql
systemctl enable nginx php8.1-fpm mysql

配置数据库

1
2
3
4
5
6
7
8
9
# 登录 MySQL
mysql -u root -p

# 创建数据库和用户
CREATE DATABASE blog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'blog_user'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON blog.* TO 'blog_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

配置 Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 创建 Nginx 配置文件
cat > /etc/nginx/sites-available/blog << 'EOF'
server {
listen 80;
server_name example.com;
root /var/www/html/blog/public;

add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";

index index.php;

charset utf-8;

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location = /favicon.ico {
access_log off;
log_not_found off;
}

location = /robots.txt {
access_log off;
log_not_found off;
}

error_page 404 /index.php;

location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}

location ~ /\.(?!well-known).* {
deny all;
}
}
EOF

# 启用站点
ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/

# 测试配置
nginx -t

# 重启 Nginx
systemctl restart nginx

3. 部署应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 创建项目目录
mkdir -p /var/www/html/blog
cd /var/www/html/blog

# 克隆代码
git clone https://github.com/username/blog.git .

# 安装依赖
composer install --optimize-autoloader --no-dev

# 配置 .env 文件
cp .env.example .env
nano .env

# 生成应用密钥
php artisan key:generate

# 运行迁移
php artisan migrate --force

# 生成存储链接
php artisan storage:link

# 优化应用
php artisan config:cache
php artisan route:cache
php artisan view:cache

# 设置权限
chown -R www-data:www-data /var/www/html/blog
chmod -R 755 /var/www/html/blog/storage
chmod -R 755 /var/www/html/blog/bootstrap/cache

4. 测试应用

现在,我们可以通过浏览器访问 http://example.com 来测试我们的博客应用。

9.13 部署最佳实践

  1. 使用版本控制:使用 Git 来管理代码
  2. 使用 CI/CD:使用持续集成和持续部署
  3. 使用环境变量:使用环境变量来存储敏感信息
  4. 使用容器化:使用 Docker 来容器化应用
  5. 使用负载均衡:在高流量应用中使用负载均衡
  6. 使用 CDN:使用内容分发网络来加速静态资源
  7. 使用缓存:使用 Redis 或 Memcached 来缓存数据
  8. 使用队列:将耗时任务放入队列
  9. 使用监控:监控应用性能和错误
  10. 使用备份:定期备份数据库和文件

10. 结语

恭喜你完成了《Laravel 教程 - Web 开发实战入门》的学习!在本教程中,我们学习了:

  1. Laravel 基础:Laravel 的历史、特点和优势
  2. 环境搭建:PHP、Composer 和 Laravel 的安装
  3. 项目初始化:Laravel 项目的创建和目录结构
  4. 路由和控制器:Laravel 的路由系统和控制器
  5. 视图:Blade 模板引擎和视图组件
  6. 数据库操作:迁移、模型、查询构建器和 Eloquent ORM
  7. 表单处理:表单验证、CSRF 保护和请求类
  8. 认证系统:用户注册、登录、密码重置和邮箱验证
  9. 文件上传和存储:本地存储、云存储和文件管理
  10. 部署:本地服务器、生产环境部署和优化策略

Laravel 是一个功能强大、优雅的 PHP 框架,它提供了许多工具和功能来帮助我们快速开发 Web 应用。通过本教程的学习,你应该已经掌握了 Laravel 的核心概念和基本用法,可以开始开发自己的 Laravel 应用了。

后续学习建议

  1. 深入学习 Laravel 核心:阅读 Laravel 官方文档,深入学习 Laravel 的核心概念和高级功能
  2. 学习 Laravel 生态系统:学习 Laravel 生态系统中的其他组件,如 Laravel Nova、Laravel Horizon、Laravel Echo 等
  3. 学习前端框架:学习 Vue.js、React 或 Angular 等前端框架,与 Laravel 结合使用
  4. 学习 API 开发:学习使用 Laravel 开发 RESTful API
  5. 学习测试:学习使用 PHPUnit 和 Laravel Dusk 进行测试
  6. 参与开源项目:参与 Laravel 或其他开源项目,提高自己的编程技能
  7. 构建实际项目:构建实际项目,将所学知识应用到实践中

资源推荐

祝你在 Laravel 开发之旅中取得成功!