什么是 Session
HTTP 是无状态协议,其自身并没有跨请求保存用户信息的机制。
Laravel 的 Session 功能提供了一种在多次请求之间保存用户数据的方式。
登录状态、购物车中的商品、表单尚未提交的输入等,需要在多次请求间保持的信息,都可以放到 Session 中。
Session 驱动配置
Session 相关配置写在 config/session.php 中。
可以通过 SESSION_DRIVER 环境变量(在 .env 中)切换驱动。
Laravel 默认支持的驱动如下:
| 驱动 | 说明 |
|---|
file | 以文件形式保存到 storage/framework/sessions(默认) |
cookie | 以加密 Cookie 保存到浏览器 |
database | 保存到数据库表 |
redis | 保存到 Redis(高速) |
array | 保存到 PHP 数组中(仅测试用,无法跨请求保持) |
Laravel 默认项目使用 database 驱动。使用 database 需要 sessions 表,默认迁移中已包含,可直接使用。
访问 Session
访问 Session 有两种方式:通过 Request 实例的方法,或使用全局辅助函数 session()。
使用 Request 实例
在控制器方法中类型提示 Request,然后通过 $request->session() 访问 Session。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\View\View;
class DashboardController extends Controller
{
public function index(Request $request): View
{
$username = $request->session()->get('username');
return view('dashboard', ['username' => $username]);
}
}
使用全局辅助函数 session()
session() 辅助函数可以在控制器、视图等任何地方调用。
// 取值
$value = session('key');
// 带默认值取值
$value = session('key', '默认值');
// 保存值
session(['key' => 'value']);
两种方式都可以在测试中通过 assertSessionHas 校验。控制器内使用 $request->session() 可以让依赖关系更明确。
Session 操作
获取数据
// 按键取值
$value = $request->session()->get('key');
// 指定默认值
$value = $request->session()->get('key', 'default');
// 使用闭包提供默认值
$value = $request->session()->get('key', function () {
return 'default';
});
// 获取全部 Session 数据
$data = $request->session()->all();
判断是否存在
// 值存在且不是 null 时返回 true
if ($request->session()->has('user_id')) {
// ...
}
// 即使值为 null,只要存在就返回 true
if ($request->session()->exists('user_id')) {
// ...
}
// 不存在时返回 true
if ($request->session()->missing('user_id')) {
// ...
}
请注意 has 与 exists 的差异:
| 方法 | 值为 null 时 |
|---|
has | 返回 false |
exists | 返回 true |
保存数据
// 通过 Request 实例
$request->session()->put('user_name', '田中');
// 通过全局辅助函数
session(['user_name' => '田中']);
// 追加到数组
$request->session()->push('cart.items', ['id' => 1, 'name' => '商品 A']);
删除数据
// 删除特定键
$request->session()->forget('user_name');
// 一次删除多个键
$request->session()->forget(['user_name', 'cart']);
// 删除所有 Session 数据
$request->session()->flush();
闪存数据(Flash Data)
闪存数据是只在下一次请求中有效的 Session 数据。
用于表单提交完成后的一次性提示、错误消息等信息。
使用 flash() 保存
$request->session()->flash('status', '文章已保存。');
该数据可以在下一次请求中读取,之后会自动删除。
延长闪存数据
// 将所有闪存数据延长一次请求
$request->session()->reflash();
// 仅延长特定键
$request->session()->keep(['status', 'message']);
在 Blade 中显示闪存消息
{{-- resources/views/layouts/app.blade.php --}}
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
将闪存消息的展示写在布局文件(如 layouts/app.blade.php)中,可以在所有页面统一展示。
实践示例:表单提交后的消息展示
在表单提交后,将成功消息保存到 Session 并重定向,然后在目标页展示——这是 Web 应用中的经典模式。
定义路由
use App\Http\Controllers\PostController;
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
实现控制器
在处理表单提交的 store 方法中,保存后携带成功消息进行重定向。<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class PostController extends Controller
{
public function index(): View
{
$posts = Post::latest()->get();
return view('posts.index', ['posts' => $posts]);
}
public function create(): View
{
return view('posts.create');
}
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required|string',
]);
Post::create($validated);
return redirect()
->route('posts.index')
->with('success', '文章已创建。');
}
}
redirect()->with('success', '...') 是把闪存数据传给重定向目标的快捷方式。 在布局中展示消息
在 resources/views/layouts/app.blade.php 中加入闪存消息展示部分。<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
{{-- 闪存消息 --}}
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
@yield('content')
</body>
</html>
创建列表页视图
{{-- resources/views/posts/index.blade.php --}}
@extends('layouts.app')
@section('content')
<h1>文章列表</h1>
@foreach ($posts as $post)
<div>
<h2>{{ $post->title }}</h2>
<p>{{ $post->body }}</p>
</div>
@endforeach
@endsection
保存并重定向后,列表页顶部会显示一次「文章已创建。」的提示。
与校验错误结合的模式
校验失败时使用 back()->withErrors()。
Laravel 在校验失败时会自动将错误保存为闪存数据,因此在 Blade 中可以直接使用 $errors 变量。
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'body' => 'required|string',
]);
Post::create($validated);
// 成功:带闪存消息重定向
return redirect()
->route('posts.index')
->with('success', '文章已创建。');
// 校验失败时 Laravel 会自动执行 back()->withErrors()->withInput()
}
{{-- 表单页 --}}
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form method="POST" action="{{ route('posts.store') }}">
@csrf
<div>
<label for="title">标题</label>
<input
id="title"
type="text"
name="title"
value="{{ old('title') }}"
/>
@error('title')
<span>{{ $message }}</span>
@enderror
</div>
<div>
<label for="body">正文</label>
<textarea id="body" name="body">{{ old('body') }}</textarea>
@error('body')
<span>{{ $message }}</span>
@enderror
</div>
<button type="submit">发表</button>
</form>
flush() 会删除 Session 中的所有数据,登录状态等也会一并丢失。若只想删除用户相关数据,请使用 forget() 指定特定键。
下一步