> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Session

> 如何使用 Laravel 的 HTTP Session 在多次请求之间保存数据。

## 什么是 Session

HTTP 是无状态协议，其自身并没有跨请求保存用户信息的机制。
Laravel 的 Session 功能提供了一种在多次请求之间保存用户数据的方式。

登录状态、购物车中的商品、表单尚未提交的输入等，需要在多次请求间保持的信息，都可以放到 Session 中。

## Session 驱动配置

Session 相关配置写在 `config/session.php` 中。
可以通过 `SESSION_DRIVER` 环境变量（在 `.env` 中）切换驱动。

```ini theme={null}
SESSION_DRIVER=database
```

Laravel 默认支持的驱动如下：

| 驱动         | 说明                                        |
| ---------- | ----------------------------------------- |
| `file`     | 以文件形式保存到 `storage/framework/sessions`（默认） |
| `cookie`   | 以加密 Cookie 保存到浏览器                         |
| `database` | 保存到数据库表                                   |
| `redis`    | 保存到 Redis（高速）                             |
| `array`    | 保存到 PHP 数组中（仅测试用，无法跨请求保持）                 |

<Info>
  Laravel 默认项目使用 `database` 驱动。使用 `database` 需要 `sessions` 表，默认迁移中已包含，可直接使用。
</Info>

## 访问 Session

访问 Session 有两种方式：通过 `Request` 实例的方法，或使用全局辅助函数 `session()`。

### 使用 Request 实例

在控制器方法中类型提示 `Request`，然后通过 `$request->session()` 访问 Session。

```php theme={null}
<?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()` 辅助函数可以在控制器、视图等任何地方调用。

```php theme={null}
// 取值
$value = session('key');

// 带默认值取值
$value = session('key', '默认值');

// 保存值
session(['key' => 'value']);
```

<Tip>
  两种方式都可以在测试中通过 `assertSessionHas` 校验。控制器内使用 `$request->session()` 可以让依赖关系更明确。
</Tip>

## Session 操作

### 获取数据

```php theme={null}
// 按键取值
$value = $request->session()->get('key');

// 指定默认值
$value = $request->session()->get('key', 'default');

// 使用闭包提供默认值
$value = $request->session()->get('key', function () {
    return 'default';
});

// 获取全部 Session 数据
$data = $request->session()->all();
```

### 判断是否存在

```php theme={null}
// 值存在且不是 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`   |

### 保存数据

```php theme={null}
// 通过 Request 实例
$request->session()->put('user_name', '田中');

// 通过全局辅助函数
session(['user_name' => '田中']);

// 追加到数组
$request->session()->push('cart.items', ['id' => 1, 'name' => '商品 A']);
```

### 删除数据

```php theme={null}
// 删除特定键
$request->session()->forget('user_name');

// 一次删除多个键
$request->session()->forget(['user_name', 'cart']);

// 删除所有 Session 数据
$request->session()->flush();
```

## 闪存数据（Flash Data）

闪存数据是**只在下一次请求中有效**的 Session 数据。
用于表单提交完成后的一次性提示、错误消息等信息。

### 使用 `flash()` 保存

```php theme={null}
$request->session()->flash('status', '文章已保存。');
```

该数据可以在下一次请求中读取，之后会自动删除。

### 延长闪存数据

```php theme={null}
// 将所有闪存数据延长一次请求
$request->session()->reflash();

// 仅延长特定键
$request->session()->keep(['status', 'message']);
```

### 在 Blade 中显示闪存消息

```blade theme={null}
{{-- 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
```

<Tip>
  将闪存消息的展示写在布局文件（如 `layouts/app.blade.php`）中，可以在所有页面统一展示。
</Tip>

## 实践示例：表单提交后的消息展示

在表单提交后，将成功消息保存到 Session 并重定向，然后在目标页展示——这是 Web 应用中的经典模式。

<Steps>
  <Step title="定义路由">
    ```php theme={null}
    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');
    ```
  </Step>

  <Step title="实现控制器">
    在处理表单提交的 `store` 方法中，保存后携带成功消息进行重定向。

    ```php theme={null}
    <?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', '...')` 是把闪存数据传给重定向目标的快捷方式。
  </Step>

  <Step title="在布局中展示消息">
    在 `resources/views/layouts/app.blade.php` 中加入闪存消息展示部分。

    ```blade theme={null}
    <!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>
    ```
  </Step>

  <Step title="创建列表页视图">
    ```blade theme={null}
    {{-- 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
    ```

    保存并重定向后，列表页顶部会显示一次「文章已创建。」的提示。
  </Step>
</Steps>

### 与校验错误结合的模式

校验失败时使用 `back()->withErrors()`。
Laravel 在校验失败时会自动将错误保存为闪存数据，因此在 Blade 中可以直接使用 `$errors` 变量。

```php theme={null}
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()
}
```

```blade theme={null}
{{-- 表单页 --}}

@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>
```

<Warning>
  `flush()` 会删除 Session 中的所有数据，登录状态等也会一并丢失。若只想删除用户相关数据，请使用 `forget()` 指定特定键。
</Warning>

## 下一步

<Card title="校验" icon="check-circle" href="/zh/validation">
  了解如何通过校验来验证表单输入。
</Card>


## Related topics

- [Cloud Sessions](/zh/packages/laravel-copilot-sdk/cloud-sessions.md)
- [Remote Sessions](/zh/packages/laravel-copilot-sdk/remote-sessions.md)
- [SessionConfig](/zh/packages/laravel-copilot-sdk/session-config.md)
- [SessionEvent](/zh/packages/laravel-copilot-sdk/session-event.md)
- [HTTP 测试](/zh/http-tests.md)
