第5.5章:网页路由
学习时间: 40分钟
1. routes/web.php
与 routes/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 中的使用示例:
以前我们这样写:
现在我们将使用 route()
助手函数这样写:
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
});
巩固知识小测验
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 路由的结构化和专业方法。现在您能够:
- 区分
web
和api
路由及其用途。 - 使用
Route::resource
快速生成标准 CRUD 路由。 - 应用命名路由来创建灵活且可维护的代码。
- 在控制器中创建完整的 CRUD 操作,包括验证和重定向。
- 分组路由以应用通用规则。
您“飞船”的导航系统现在已具备容错性,并准备好进行扩展。 在本节的最后一章中,我们将整合所有获得的知识,并在我们的 Blade 页面上显示通过 Fetch 获取的行星数据。