第9章 9.2 快速上手 Laravel:让 PHP 写起来像 Python 一样优雅

上章回顾:上一章我们速览了 PHP 8 的新特性——构造函数属性提升、联合类型、match 表达式……你有没有发现,PHP 8 已经越来越像 Python 了?这一章我们要把「像 Python 一样优雅」这件事做到极致——用 Laravel 框架,写出跟 Python Flask 一样简洁好看的代码。

本章目标:30 分钟入门 Laravel 能跑起来,60 分钟写出你的第一个完整小项目。


🎯 开场 3 分钟:为什么需要框架?

场景切入:写一个「显示用户列表」的页面

不用框架,你会这样写 PHP:

// 原生 PHP:5 个文件起步
<?php
header('Content-Type: text/html; charset=utf-8');
$conn = new mysqli('localhost', 'root', 'password', 'test');
$result = $conn->quer\n\n![Simple tech illustration expla](https://blog.xxyye.com/wp-content/uploads/2026/06/6e12c6c921458b3.png)\n\n![AI comic creation scene, creat](https://blog.xxyye.com/wp-content/uploads/2026/06/83ca2dfce4adb3e.png)\n\ny("SELECT * FROM users");
echo "<table><tr><th>ID</th><th>姓名</th></tr>";
while ($row = $result->fetch_assoc()) {
echo "<tr><td>{$row['id']}</td><td>{$row['name']}</td></tr>";
}
echo "</table>";
?>

痛点来了
- 想改 URL 路由?改 Apache/Nginx 配置
- 想加个登录验证?从头写 Session 处理
- 想返回 JSON?手写 json_encode + 头信息
- 多人协作?每人写一套,代码像意大利面

Laravel 就是来解决这些问题的——给你一套约定好的结构,让代码自动变得有序


🧱 基础 25 分钟:核心概念

概念 1:路由——网站的「导航地图」

是什么:路由就是「URL 地址 → 处理函数」的映射表,像餐厅的座位号。

类比:你打电话给外卖平台,「我要点餐」这个请求被路由到「点餐组」,「我要投诉」被路由到「客服组」。

为什么用:不用改服务器配置,直接在代码里定义 URL 行为。

怎么用

// routes/web.php - Laravel 路由文件
use Illuminate\Support\Facades\Route;

// 访问 /hello 就执行这个函数
Route::get('/hello', function () {
return '你好,Laravel!';
});

// 带参数的路由
Route::get('/user/{id}', function (string $id) {
return "用户ID是:{$id}";
});

对比 Python Flask:

# Python Flask 等价写法
from flask import Flask
app = Flask(__name__)

@app.route('/hello')
def hello():
return '你好,Laravel!'

@app.route('/user/<id>')
def user(id):
return f"用户ID是:{id}"

看,Python Flask 简洁,但 Laravel 更适合大型项目。


概念 2:MVC 架构——分工明确的「流水线」

是什么:MVC = Model(模型) + View(视图) + Controller(控制器),一种代码组织模式。

类比:去医院看病:
- Model = 病历本(存数据)
- View = 医生的诊断书(展示结果)
- Controller = 医生(协调:看完病历、检查身体、写诊断书)

为什么用:不用 MVC 的话,所有代码混在一起,改需求时像在一锅粥里找芝麻。

怎么用

// Model:app/Models/User.php
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
protected $table = 'users';      // 对应哪张表
protected $fillable = ['name', 'email'];  // 允许批量写入的字段
}
// Controller:app/Http/Controllers/UserController.php
<?php
namespace App\Http\Controllers;

use App\Models\User;

class UserController extends Controller
{
// 展示所有用户
public function index()
{
    $users = User::all();  // 等价于 SELECT * FROM users
    return view('users.index', ['users' => $users]);
}

// 创建新用户(表单提交后)
public function store(Request $request)
{
    User::create($request->only(['name', 'email']));
    return redirect('/users');
}
}
// View:resources/views/users/index.blade.php
<h1>用户列表</h1>
<ul>
@foreach ($users as $user)
<li>{{ $user->name }} - {{ $user->email }}</li>
@endforeach
</ul>

Laravel 的 View 用 Blade 模板引擎,语法像写 HTML 但带「超能力」——{{ }} 会自动转义防 XSS,@foreach @if 让模板自己会「思考」。


概念 3:Artisan 命令——你的命令行瑞士军刀

是什么:Laravel 自带一系列命令行工具,叫 Artisan( artisan /ɑːrˈtɪsən/ ,手工艺人)。

为什么用:生成代码骨架、数据库迁移、启动开发服务器……不用手动创建文件。

怎么用

# 创建控制器
php artisan make:controller UserController

# 创建模型 + 迁移文件(一起搞定)
php artisan make:model User -m

# 启动开发服务器(相当于 python manage.py runserver)
php artisan serve

# 查看所有可用命令
php artisan list
# 实际运行效果
$ php artisan serve
NFO  Server running on http://127.0.0.1:8000.

$ php artisan make:controller ArticleController
NFO  Controller created successfully.

概念 4:Eloquent ORM——用「对象」操作数据库

是什么:Eloquent 是 Laravel 的 ORM(对象关系映射),让你用 PHP 对象操作数据库,不用写 SQL。

类比:ORM 就像翻译官——你说中文「给我所有用户」,翻译官帮你转成英文 SQL。

为什么用:不用记 SQL 语法,代码更安全(防 SQL 注入),还能跨数据库(MySQL/PostgreSQL/SQLite 换着用)。

怎么用

// 查数据 - 不用写 SELECT
$users = User::all();              // 查所有
$user = User::find(1);             // 按主键查
$users = User::where('age', '>', 18)->get();  // 条件查询

// 增数据 - 不用写 INSERT
User::create(['name' => '张三', 'email' => 'zhangsan@example.com']);

// 改数据 - 不用写 UPDATE
$user = User::find(1);
$user->name = '李四';
$user->save();

// 删数据 - 不用写 DELETE
$user->delete();

🔥 实战 35 分钟:3 个递进小项目

项目 1(5 分钟):Hello Laravel

目标:访问 /hello 返回「Hello World」,访问 /now 返回当前时间。

步骤

  1. 创建新项目(如果还没创建):
composer create-project laravel/laravel my-first-laravel
cd my-first-laravel
  1. 打开 routes/web.php,添加路由:
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
return view('welcome');
});

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

Route::get('/now', function () {
return now()->toDateTimeString();  // Laravel 自带的时间对象
});
  1. 启动服务器:
php artisan serve
  1. 打开浏览器访问:
    - http://127.0.0.1:8000/hello → 显示「Hello World」
    - http://127.0.0.1:8000/now → 显示「2024-01-15 14:30:00」

预期输出

# 浏览器访问 /now 显示:
2024-01-15 14:30:00

一句话解释:Route::get 定义了 URL 到函数的映射,now() 是 Laravel 封装的 Carbon 时间对象。


项目 2(15 分钟):用户管理 CRUD

目标:做一个完整的用户增删改查页面,数据存 SQLite(不用装 MySQL)。

步骤

  1. 生成脚手架(一条命令搞定):
php artisan make:model User -mcr
# -m = 创建迁移文件(数据库表结构)
# -c = 创建控制器
# -r = 生成 CRUD 完整方法
  1. 配置数据库(用 SQLite,文件数据库):
// .env 文件,找到 DB_CONNECTION,改成:
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database/database.sqlite
# 创建 SQLite 数据库文件
touch database/database.sqlite
  1. 写数据库迁移(database/migrations/xxxx_create_users_table.php):
<?php
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('created_at')->nullable();
    $table->timestamp('updated_at')->nullable();
});
}
# 执行迁移,创建数据表
php artisan migrate
  1. 写 Controller(app/Http/Controllers/UserController.php):
<?php
namespace App\Http\Controllers;

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

class UserController extends Controller
{
public function index()
{
    $users = User::orderBy('created_at', 'desc')->paginate(10);
    return view('users.index', compact('users'));
}

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

public function store(Request $request)
{
    User::create($request->validate([
        'name' => 'required|min:2',
        'email' => 'required|email|unique:users',
    ]));
    return redirect()->route('users.index');
}

public function edit(User $user)
{
    return view('users.edit', compact('user'));
}

public function update(Request $request, User $user)
{
    $user->update($request->validate([
        'name' => 'required|min:2',
        'email' => 'required|email|unique:users,email,' . $user->id,
    ]));
    return redirect()->route('users.index');
}

public function destroy(User $user)
{
    $user->delete();
    return redirect()->route('users.index');
}
}
  1. 添加路由(routes/web.php):
Route::resource('users', UserController::class);
  1. 创建视图文件(resources/views/users/index.blade.php):
<h1>用户管理</h1>
<a href="{{ route('users.create') }}">➕ 添加用户</a>

<table border="1" cellpadding="5">
<tr><th>ID</th><th>姓名</th><th>邮箱</th><th>操作</th></tr>
@foreach ($users as $user)
<tr>
    <td>{{ $user->id }}</td>
    <td>{{ $user->name }}</td>
    <td>{{ $user->email }}</td>
    <td>
        <a href="{{ route('users.edit', $user) }}">✏️ 编辑</a>
        <form action="{{ route('users.destroy', $user) }}" method="POST" style="display:inline">
            @csrf
            @method('DELETE')
            <button type="submit">🗑️ 删除</button>
        </form>
    </td>
</tr>
@endforeach
</table>

{{ $users->links() }}

预期输出:访问 http://127.0.0.1:8000/users 显示用户列表,有分页、编辑、删除按钮。

一句话解释:Route::resource 一行代码定义了 7 条 RESTful 路由,Eloquent 自动处理 SQL,Laravel 自动做 CSRF 防护。


项目 3(15 分钟):CSV 批量导入用户

目标:上传 CSV 文件,批量导入用户到数据库。

步骤

  1. 安装处理 CSV 的包:
composer require league/csv
  1. UserController 添加导入方法:
use League\Csv\Reader;

public function import(Request $request)
{
$request->validate([
    'csv_file' => 'required|mimes:csv,txt|max:10240',
]);

$csv = Reader::createFromPath($request->file('csv_file')->getRealPath());
$csv->setHeaderOffset(0);

$count = 0;
foreach ($csv->getRecords() as $record) {
    User::firstOrCreate(
        ['email' => $record['email']],
        ['name' => $record['name']]
    );
    $count++;
}

return redirect()->route('users.index')->with('success', "成功导入 {$count} 条数据");
}
  1. resources/views/users/index.blade.php 添加上传表单:
<hr>
<h3>📤 CSV 批量导入</h3>
<form action="{{ route('users.import') }}" method="POST" enctype="multipart/form-data">
@csrf
<input type="file" name="csv_file" accept=".csv,.txt">
<button type="submit">导入</button>
</form>
<p><strong>CSV 格式要求:</strong>第一行是表头(name,email),后续每行一条用户数据</p>
  1. 创建测试 CSV 文件 test_users.csv
name,email
王五,wangwu@example.com
赵六,zhaoliu@example.com

预期输出

# 上传 CSV 后页面显示:
成功导入 2 条数据

一句话解释:firstOrCreate 像个「智能秘书」——如果邮箱已存在就跳过,不存在才创建,避免重复数据。


💪 进阶 20 分钟:常见坑 + 性能小贴士

坑 1:路由顺序搞反——404 了!

// ❌ 错误:动态路由放在静态路由前面
Route::get('/user/{id}', ...);  // 这会匹配所有 /user/xxx,包括 /user/create
Route::get('/user/create', ...);  // 永远执行不到!

// ✅ 正确:静态路由放前面
Route::get('/user/create', ...);
Route::get('/user/{id}', ...);

坑 2:没加 @csrf——表单提交被拦截!

<!-- ❌ 错误:Laravel 默认开启 CSRF 防护,不加 token 会 419 错误 -->
<form action="/users" method="POST">
<input name="name">
</form>

<!-- ✅ 正确:加 @csrf 指令 -->
<form action="/users" method="POST">
@csrf
<input name="name">
</form>

坑 3:Eloquent 查不到就崩——用 findOrFail

// ❌ 错误:ID 不存在时返回 null,后面调用 $user->name 报错
$user = User::find($id);

// ✅ 正确:找不到就抛 404 页面
$user = User::findOrFail($id);

坑 4:数据库迁移——字段改了就忘了 --step

# ❌ 错误:修改迁移文件后直接 migrate,数据库不生效
php artisan migrate

# ✅ 正确:重置后重新迁移(开发环境)
php artisan migrate:fresh

坑 5:dd() 调试留在了生产环境

// ❌ 错误:dd() 会中断程序,上线后用户看到一堆调试信息
$users = User::all();
dd($users);

// ✅ 正确:用日志或只开发环境用的调试
\Log::info('Users count: ' . $users->count());
// 或用 PHP 内置的 debug_backtrace

性能小贴士:N+1 查询问题

// ❌ 错误:在循环里查关联数据,100 个用户 = 101 次查询
@foreach ($users as $user)
{{ $user->posts->count() }}  <!-- 每次都查一次 posts 表 -->
@endforeach

// ✅ 正确:预加载,一次查完
$users = User::with('posts')->get();
// 等价于 SELECT * FROM users 和 SELECT * FROM posts WHERE user_id IN (1,2,3...)

调试技巧:用 Laravel Tinker 实时交互

# 打开 Tinker,进入交互式调试环境
php artisan tinker

# 里面可以实时执行 PHP 代码
>>> User::count()
=> 15
>>> User::first()->name
=> "张三"
>>> exit

✏️ 练习题

练习 1(2 分钟):改路由参数

  • 输入:访问 /user/666
  • 预期输出:页面显示「用户ID是:666」
  • 提示:找到 routes/web.php,添加一条 Route::get('/user/{id}', ...)

练习 2(2 分钟):加个判断

  • 输入:在练习 1 基础上,如果 ID 是 1,显示「这是管理员」
  • 预期输出:访问 /user/1 显示管理员提示,否则显示用户 ID
  • 提示:在路由的闭包函数里加 if ($id === '1')

练习 3(3 分钟):换个数据源

  • 输入:把「用户列表」改成「文章列表」(articles 表,有 title 和 content 字段)
  • 预期输出:访问 /articles 显示文章列表
  • 提示:复制 User.php 改名为 Article.php,改 $fillable$table,然后 php artisan make:controller ArticleController -r

练习 4(5 分钟):串起项目 2 和 3

  • 输入:在项目 3 的 CSV 导入基础上,加一个「导出 CSV」功能
  • 预期输出:点击按钮下载 users.csv
  • 提示:用 League\Csv\Writer,把 $users 转成 CSV 格式返回

练习 5(3 分钟):读懂报错

  • 输入:用户提交表单后页面显示「419 Page Expired」
  • 预期输出:知道原因是 CSRF token 过期或缺失
  • 提示:检查表单里有没有 @csrf 指令

作业:做一个「通讯录管理工具」

需求描述:公司内部通讯录,支持:
- 查看所有同事
- 添加新同事(姓名、手机号、部门)
- 编辑同事信息
- 删除同事
- 搜索同事(按姓名或部门)

功能点
1. 用 Eloquent ORM 操作 SQLite 数据库
2. 用 Blade 模板展示数据
3. 支持搜索过滤

加分项
1. 分页显示(每页 5 条)
2. 手机号格式验证

验收标准
- 能跑起来(php artisan serve
- CRUD 操作正常
- 搜索能过滤结果
- 代码有注释

提交方式:评论区贴关键代码或 GitHub 链接


📚 总结 + 资源

本分钟学到的 3 个核心点
1. 路由是 URL 到函数的映射,用 Route::get() 定义,用 Route::resource 一行定义 CRUD 路由
2. MVC 架构让代码分工明确——Model 管数据、View 管展示、Controller 管逻辑
3. Eloquent ORM 让你用对象操作数据库,User::all() = SELECT,User::create() = INSERT

延伸学习资源
- Laravel 官方文档 —— 最权威的教材
- 《Laravel 之道》—— 国内写的实战派教程
- Laracasts —— 视频教程,老外做的,很细

互动钩子:你在项目里用过 Laravel 的哪个功能?遇到过什么坑?评论区聊聊,老粉优先回复!


下章预告:Laravel 很强大,但它还是「同步」的——一个请求进来,必须等上一个处理完才能处理下一个。下一章我们要认识一个能「同时处理 10 万连接」的大杀器——Swoole 协程与异步 PHP,让 PHP 跑得像 Node.js 一样快。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。