Skip to content

第5.5章:网页路由

学习时间: 40分钟


1. routes/web.phproutes/api.php:两种不同的控制台

再次强调它们之间的根本区别至关重要:

特性 routes/web.php (Web 控制台) routes/api.php (API 控制台)
主要任务 显示 HTML 页面,处理表单 以 JSON 格式为其他应用程序提供数据
状态 (State) 有状态 (Stateful) — 使用会话和 Cookie 无状态 (Stateless) — 每个请求独立
默认中间件 web (包括会话、CSRF 保护、Cookie 加密) api (包括“节流”——请求频率限制)
URL 前缀 无 (您的网站根目录) /api/ (在 RouteServiceProvider 中配置)
身份验证 通常通过会话 (登录/密码) 通常通过令牌 (Sanctum, Passport)

我们使用 routes/web.php 来构建供真人使用的界面。


2. 网页资源路由

类似于 Route::apiResource,网页也有 Route::resource。它为完整的 CRUD 周期创建路由,包括显示创建和编辑表单的页面。

让我们创建一个完整的路由集,通过 Web 界面管理我们的行星。

步骤 1:在 routes/web.php 中创建路由

注释或删除 /planets 的旧路由,并用一行代码替换它们:

use App\Http\Controllers\Web\PlanetPageController;

// Route::get('/planets', [PlanetPageController::class, 'index']);
// Route::get('/planets/{planet}', [PlanetPageController::class, 'show']);

Route::resource('planets', PlanetPageController::class);

步骤 2:查看已创建的内容 在终端中执行命令 php artisan route:list --except-vendor

+--------+-----------+------------------------+------------------+-------------------------------------------------+------------+
| Method | URI       | Name                   | Action           | Middleware                                      |
+--------+-----------+------------------------+------------------+-------------------------------------------------+------------+
| GET|HEAD | planets                | planets.index          | ...\PlanetPageController@index                    | web        |
| POST   | planets                | planets.store          | ...\PlanetPageController@store                    | web        |
| GET|HEAD | planets/create         | planets.create         | ...\PlanetPageController@create                   | web        |
| GET|HEAD | planets/{planet}       | planets.show           | ...\PlanetPageController@show                     | web        |
| PUT|PATCH | planets/{planet}       | planets.update         | ...\PlanetPageController@update                   | web        |
| DELETE | planets/{planet}       | planets.destroy        | ...\PlanetPageController@destroy                  | web        |
| GET|HEAD | planets/{planet}/edit  | planets.edit           | ...\PlanetPageController@edit                     | web        |
+--------+-----------+------------------------+------------------+-------------------------------------------------+------------+

Route::resource 为我们创建了 7 条路由,包括:

  • planets.create (GET /planets/create): 带有创建表单的页面。
  • planets.store (POST /planets): 处理此表单。
  • planets.edit (GET /planets/{planet}/edit): 带有编辑表单的页面。
  • planets.update (PUT/PATCH /planets/{planet}): 处理编辑表单。
  • planets.destroy (DELETE /planets/{planet}): 删除资源。

3. 命名路由:便捷的“宇宙坐标”

请注意 Name 列。Laravel 自动为每条路由分配了一个唯一的名称(例如 planets.index)。使用名称而不是硬编码的 URL 是一种最佳实践

为什么? 如果您决定将 URL 从 /planets 更改为 /worlds,您无需在所有模板中查找并更改所有链接。您只需在一个地方——路由文件中进行更改,而名称保持不变。

在 Blade 中的使用示例:

以前我们这样写:

<a href="/planets/{{ $planet->id }}">Узнать больше &rarr;</a>

现在我们将使用 route() 助手函数这样写:

<a href="{{ route('planets.show', ['planet' => $planet->id]) }}">Узнать больше &rarr;</a>

  • route('planets.show', ...) — 为名为 planets.show 的路由生成 URL。
  • ['planet' => $planet->id] — 将所需的参数传递到 URL 中。Laravel 会自动将 ID 替换到 {planet} 中。甚至可以传递整个模型:['planet' => $planet]

4. 实现控制器中缺失的方法

Route::resource 创建了路由,但 PlanetPageController 中相应的方法需要我们自己创建。

打开 app/Http/Controllers/Web/PlanetPageController.php 并添加它们。

<?php
use Illuminate\Http\Request; // <-- 添加

class PlanetPageController extends Controller
{
    // 我们已经有了 index() 和 show()

    /**
     * 显示创建新行星的表单。
     */
    public function create()
    {
        return view('planets.create'); // 只需返回带有表单的视图
    }

    /**
     * 将新行星保存到数据库中。
     */
    public function store(Request $request)
    {
        // 验证表单数据
        $validated = $request->validate([
            'name' => 'required|string|max:255|unique:planets',
            'solar_system' => 'required|string|max:100',
            // ... 其他规则
        ]);

        Planet::create($validated);

        // 将用户重定向到带有成功消息的列表页面
        return redirect()->route('planets.index')->with('success', 'Планета успешно создана!');
    }

    /**
     * 显示编辑行星的表单。
     */
    public function edit(Planet $planet)
    {
        return view('planets.edit', ['planet' => $planet]);
    }

    /**
     * 更新数据库中的行星数据。
     */
    public function update(Request $request, Planet $planet)
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255|unique:planets,name,' . $planet->id,
            'solar_system' => 'required|string|max:100',
        ]);

        $planet->update($validated);

        return redirect()->route('planets.show', $planet)->with('success', 'Данные о планете обновлены!');
    }

    /**
     * 删除行星。
     */
    public function destroy(Planet $planet)
    {
        $planet->delete();

        return redirect()->route('planets.index')->with('success', 'Планета списана.');
    }
}
  • redirect()->route(...) — 将用户重定向到另一个命名路由。
  • ->with('success', '...') — 在会话中添加“闪存消息”,该消息将在下一页上仅可用一次。我们可以在 Blade 模板中显示它。

5. 路由分组

如果您有许多具有共同特征的路由(例如,它们都用于管理面板,并且应具有 /admin 前缀和特殊的中间件),则可以对它们进行分组。

<?php
Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(function () {
    // 此组内的所有路由将具有:
    // 1. 'auth' 和 'admin' 中间件
    // 2. URL 前缀 '/admin' (例如, /admin/planets)
    // 3. 名称前缀 'admin.' (例如, admin.planets.index)

    Route::resource('planets', PlanetPageController::class);
    // Route::get('/dashboard', ...)->name('dashboard'); // -> admin.dashboard
});

巩固知识小测验

1. 在 `routes/web.php` 中,哪个命令将为 Web 界面创建一套完整的 CRUD 路由?

2. 使用命名路由的主要优点是什么?

3. 对于 `Route::resource('articles', ...)` 中的 `create()` 方法,将生成哪个路由?

4. 代码 `redirect()->route('home')->with('status', 'OK')` 的作用是什么?

5. `Route::prefix('dashboard')` 的用途是什么?

for (const [question, correctAnswer] of Object.entries(correctAnswers)) { const questionDiv = form.querySelector(input[name="${question}"]).closest('.question'); const labels = questionDiv.querySelectorAll('label'); labels.forEach(l => { l.style.color = 'inherit'; l.style.fontWeight = 'normal'; l.style.border = 'none'; });

  const userAnswer = form.elements[question] ? form.elements[question].value : undefined;

  if (userAnswer) {
    const selectedLabel = form.querySelector(`input[name="${question}"][value="${userAnswer}"]`).parentElement;
    if (userAnswer === correctAnswer) {
      score++;
      selectedLabel.style.fontWeight = 'bold';
      resultsHTML += `<li>问题 ${question.slice(1)}: <span style="color:green;">正确!</span></li>`;
    } else {
      selectedLabel.style.fontWeight = 'bold';
      const correctLabel = form.querySelector(`input[name="${question}"][value="${correctAnswer}"]`).parentElement;
      correctLabel.style.fontWeight = 'bold';
      resultsHTML += `<li>问题 ${question.slice(1)}: <span style="color:red;">错误。</span> 正确答案: <b>${correctAnswer.toUpperCase()}</b></li>`;
    }
  } else {
    resultsHTML += `<li>问题 ${question.slice(1)}: <span style="color:orange;">未作答。</span></li>`;
  }
}

resultsHTML += `</ul><p><b>您的分数:${score} / ${Object.keys(correctAnswers).length}</b></p>`;
resultsContainer.innerHTML = resultsHTML;
resultsContainer.style.display = 'block';

}


🚀 本章总结:

您已掌握在 Laravel 中组织 Web 路由的结构化和专业方法。现在您能够:

  • 区分 webapi 路由及其用途。
  • 使用 Route::resource 快速生成标准 CRUD 路由。
  • 应用命名路由来创建灵活且可维护的代码。
  • 在控制器中创建完整的 CRUD 操作,包括验证和重定向。
  • 分组路由以应用通用规则。

您“飞船”的导航系统现在已具备容错性,并准备好进行扩展。 在本节的最后一章中,我们将整合所有获得的知识,并在我们的 Blade 页面上显示通过 Fetch 获取的行星数据。