Skip to content

第5.2章:创建简单的API视图

学习时间: 45分钟


1. 任务:可视化数据

我们在第2章中的API能够以JSON格式返回数据。这对于机器来说很棒,但人们更喜欢在美观的网页上查看信息。我们的目标是创建两个这样的页面:

  1. 所有行星列表 (/planets)
  2. 单个行星页面 (/planets/{id})

为此,我们将使用“路由 → 控制器 → 视图”组合。

💡 太空类比:

想象一下,API返回的JSON是原始遥测数据,只是一串数字流。我们今天的任务是在任务控制中心创建两个屏幕(两个“视图”):

  • 概览屏幕: 显示系统中所有对象的状态(行星列表)。
  • 详细屏幕: 点击对象时显示其所有信息(单个行星页面)。

2. 步骤1:创建网页控制器

为了架构的清晰,不应将API逻辑和网页逻辑混合在同一个控制器中。我们将创建一个新的控制器,专门用于显示我们的Blade视图。

在终端中执行:

php artisan make:controller Web/PlanetPageController
我们将其创建在 Web 子文件夹中,以便与API控制器区分开来。

打开创建的文件 app/Http/Controllers/Web/PlanetPageController.php


步骤2:所有行星列表页面

1. 在控制器中创建方法:PlanetPageController 中添加 index 方法,该方法将从数据库中获取所有行星并将其传递给视图。

<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Planet; // 别忘了导入模型

class PlanetPageController extends Controller
{
    /**
     * 显示所有行星列表页面。
     */
    public function index()
    {
        // 1. 从数据库中获取所有行星
        $planets = Planet::all();

        // 2. 返回视图并传递数据
        return view('planets.index', ['planets' => $planets]);
    }
}

2. 创建Blade视图: 创建文件 resources/views/planets/index.blade.php。我们将使用上一章中创建的布局。

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>行星</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f4f4f9;
            color: #333;
            margin: 0;
            padding: 2em;
        }
        .container {
            max-width: 960px;
            margin: 0 auto;
        }
        h2 {
            color: #1a202c;
        }
        hr {
            border: none;
            border-top: 1px solid #e2e8f0;
            margin: 1.5em 0;
        }
        .planet-list {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
            gap: 1.5em;
        }
        .planet-card {
            background-color: #fff;
            border: 1px solid #e2e8f0;
            border-radius: 0.5em;
            padding: 1.5em;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
            transition: transform 0.2s;
        }
        .planet-card:hover {
            transform: translateY(-5px);
        }
        .planet-card h3 {
            margin-top: 0;
            color: #2d3748;
        }
        .planet-card p {
            margin-bottom: 0.5em;
            color: #4a5568;
        }
        .planet-card a {
            color: #4299e1;
            text-decoration: none;
            font-weight: bold;
        }
        .planet-card a:hover {
            text-decoration: underline;
        }
        .no-planets {
            color: #718096;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>所有已知行星列表</h2>
        <hr>
        <div class="planet-list">
            @forelse($planets as $planet)
                <div class="planet-card">
                    <h3>{{ $planet->name }}</h3>
                    <p>太阳系:{{ $planet->solar_system }}</p>
                    <p>直径:{{ number_format($planet->size_km, 0, '.', ' ') }} 公里</p>
                    <a href="/planets/{{ $planet->id }}">了解更多 &rarr;</a>
                </div>
            @empty
                <p class="no-planets">数据库中没有行星。请运行数据填充器。</p>
            @endforelse
        </div>
    </div>
</body>
</html>
  • number_format(...) 是一个普通的PHP函数,用于美观地格式化数字。它可以在Blade中直接使用。

3. 在 routes/web.php 中创建路由:

use App\Http\Controllers\Web\PlanetPageController;

// ...

Route::get('/planets', [PlanetPageController::class, 'index']);
现在,如果您在浏览器中访问 /planets 地址,您将看到行星列表页面!


4. 步骤3:单个行星页面

1. 在控制器中创建方法:

PlanetPageController 中添加 show 方法。通过路由模型绑定(Route Model Binding),Laravel将根据ID自动找到行星并将其传递给方法。

<?php
// 在 PlanetPageController 类内部
/**
 * 显示单个具体行星的页面。
 */
public function show(Planet $planet)
{
    // Laravel 已经为我们找到了行星。
    // 如果未找到,它将自动返回 404 错误。

    return view('planets.show', ['planet' => $planet]);
}

2. 创建Blade视图:

创建文件 resources/views/planets/show.blade.php

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ $planet->name }}</title>
    <style>
        body {
            font-family: sans-serif;
            background-color: #f4f4f9;
            color: #333;
            margin: 0;
            padding: 2em;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
        }
        .container {
            max-width: 600px;
            width: 100%;
        }
        .planet-detail {
            background-color: #fff;
            border: 1px solid #e2e8f0;
            border-radius: 0.5em;
            padding: 2em;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
        }
        .planet-detail h1 {
            margin-top: 0;
            color: #2d3748;
        }
        .planet-detail p {
            margin-bottom: 1em;
            color: #4a5568;
            font-size: 1.1em;
        }
        .back-link {
            display: inline-block;
            margin-top: 1.5em;
            color: #4299e1;
            text-decoration: none;
            font-weight: bold;
        }
        .back-link:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="planet-detail">
            @if($planet->image_url)
                <img src="{{ $planet->image_url }}" alt="{{ $planet->name }}的图片" style="max-width: 100%; height: auto; border-radius: 0.5em; margin-bottom: 1em;">
            @endif
            <h1>{{ $planet->name }}</h1>
            @if($planet->description)
                <p>{{ $planet->description }}</p>
            @endif
            <p><strong>太阳系:</strong> {{ $planet->solar_system }}</p>
            <p><strong>直径:</strong> {{ number_format($planet->size_km, 0, '.', ' ') }} 公里</p>
            <a href="/planets" class="back-link">&larr; 返回行星列表</a>
        </div>
    </div>
</body>
</html>

3. 在 routes/web.php 中创建路由:

// 在 /planets 路由之后添加此路由
Route::get('/planets/{planet}', [PlanetPageController::class, 'show']);
- 参数名称 {planet} 必须与控制器方法中的变量名称 (show(Planet $planet)) 匹配,以便路由模型绑定正常工作。

现在,点击列表页面上的“了解更多”链接,您将进入特定行星的详细页面。


巩固练习小测验

1. 分离API和网页逻辑的最佳实践方法是什么?

2. return view('planets.index', ['planets' => $planets]); 的作用是什么?

3. 在 `show(Planet $planet)` 的上下文中,路由模型绑定(Route Model Binding)是什么?

4. 如何在Blade中格式化 `created_at` 字段的日期?

5. 如果路由中指定了 `/posts/{post}`,控制器中方法的签名应该如何编写才能使路由模型绑定(Route Model Binding)工作?


🚀 本章总结:

您已成功使用 Laravel MVC 架构为您的 API 创建了“前端展示”。现在您拥有:

  • 用于网页逻辑的独立控制器。
  • 一个动态页面,列出所有行星,并从数据库获取数据。
  • 每个行星的详细页面,使用路由模型绑定(Route Model Binding)。
  • routes/web.php 中定义了两个 Web 路由,用于访问这些页面。

您已将原始数据转换为用户可理解且有用的信息。 在下一章中,我们将通过将 JavaScript 嵌入到我们的 Blade 视图中来增加交互性,以实现在不重新加载页面的情况下与 API 进行交互。