Laravel 框架
尽管 Symfony 和 Zend Framework 在相当长的时间里一直是大公司,但在最近几年里,第三个框架出现了,而且越来越受欢迎,如今已成为开发人员最喜爱的框架。简洁、优雅的代码和高速的开发速度是这个 "工匠框架" 的王牌。在本节中,你将了解 Laravel 的功能,并初步创建一个非常简单的应用程序。
安装
Laravel 自带一套命令行工具,能让你的生活更轻松。因此,我们建议在全局而不是每个项目中安装 Laravel,也就是说,把 Laravel 作为环境中的另一个程序来安装。你仍然可以使用 Composer 执行以下命令:
$ composer global require "laravel/installer"
该命令会将 Laravel 安装程序下载到 ~/.composer/vendor
。要在命令行中使用该可执行文件,需要运行类似下面的命令:
$ sudo ln -s ~/.composer/vendor/bin/laravel /usr/bin/laravel
现在,您可以使用 laravel
命令了。为确保一切顺利,只需运行以下命令:
$ laravel –version
如果一切顺利,这应该输出安装的版本。
Project 设置
是的,我们知道。每个教程都是从创建博客开始的。但是,我们正在构建网络应用程序,这是最简单的方法,也能为你带来一些价值。那么,让我们开始吧;在你想添加应用程序的地方执行以下命令:
$ laravel new php-blog
该命令的输出结果与 Composer 的输出结果类似,因为它使用 Composer 获取依赖项。几秒钟后,程序会告诉你一切都安装成功,可以开始了。
Laravel 创建了一个新的 php-blog 目录,里面有很多内容。你应该拥有类似下面截图所示的目录结构:

让我们来设置数据库。首先要做的是用正确的数据库凭据更新 .env
文件。将 DB_DATABASE
的值更新为你自己的值;下面是一个例子:
DB_HOST=localhost
DB_DATABASE=php_blog
DB_USERNAME=root
DB_PASSWORD=
您还需要创建 php_blog
数据库。只需一个命令即可完成此操作,如下所示:
$ mysql -u root -e "CREATE SCHEMA php_blog"
有了 Laravel,你就有了一个迁移系统;也就是说,你可以把所有的数据库模式变化都保存在 database/migrations 下,这样其他使用你代码的人就可以快速设置他们的数据库了。第一步是运行以下命令,为博客表创建迁移文件:
$ php artisan make:migration create_posts_table --create=posts
打开生成的文件,该文件应类似于 database/migrations/<date>_create_posts_table.php
。向上方法定义了带有自动递增 ID 和时间戳字段的表 blogs。我们希望添加标题、帖子内容和创建帖子的用户 ID。用以下方法替换 up 方法:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('title');
$table->text('content');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')
->references('id')->on('users');
});
}
在这里,标题是一个字符串,而内容是一个文本。区别在于这些字段的长度,字符串是简单的 VARCHAR,而文本是 TEXT 数据类型。对于用户 ID,我们定义了 INT UNSIGNED,它引用了用户表的 id 字段。Laravel 在创建项目时已经定义了用户表,所以你不必担心。如果你对它的外观感兴趣,可以查看 database/ migrations/2014_10_12_000000_create_users_table.php 文件。你会发现,一个用户由 ID、姓名、唯一电子邮件和密码组成。
到目前为止,我们只是编写了迁移文件。要应用这些文件,需要运行以下命令:
$ php artisan migrate
如果一切按预期进行,您现在应该有一个类似于以下内容的 blogs
表:

最后,我们需要为博客表创建一个模型。这个模型将从 Illuminate\Database\Eloquent\Model 扩展而来,它是 Laravel 使用的 ORM。要自动生成这个模型,运行下面的命令:
$ php artisan make:model Post
模型的名称应与数据库表的名称相同,但使用单数。运行此命令后,您可以在 app/Post.php
中找到空模型。
添加第一个端点
让我们添加一个快速端点,以了解路由是如何工作的,以及如何将控制器与模板连接起来。为了避免访问数据库,让我们创建添加新文章视图,该视图将显示一个表单,允许用户添加带有标题和文本的新文章。让我们从添加路由和控制器开始。打开 app/Http/routes.php
文件并添加以下内容:
Route::group(['middleware' => ['web']], function () {
Route::get('/new', function () {
return view('new');
});
});
这三行非常简单,说明对于 /new
端点,我们希望用 new 视图来回复。稍后,我们将在控制器中把事情复杂化,但现在,让我们把注意力集中在视图上。
Laravel 使用 Blade 而不是 Twig 作为模板引擎,但它们的工作方式非常相似。它们还可以定义布局,其他模板可以从布局中扩展。放置布局的位置在 resources/views/layouts
中。在该目录下创建一个 app.blade.php
文件,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<title>PHP Blog</title>
<link rel="stylesheet" href="{{ URL::asset('css/layout.css') }}"
type="text/css">
@yield('css')
</head>
<body>
<div class="navbar">
<ul>
<li><a href="/new">New article</a></li>
<li><a href="/">Articles</a></li>
</ul>
</div>
<div class="content">
@yield('content')
</div>
</body>
</html>
这只是一个普通的布局,在正文中包含一个标题、一些 CSS 和一个 ul 部分列表,这些部分将用作导航栏。除了 HTML 代码外,这里还有两个重要元素需要注意,它们听起来应该已经很熟悉了:
-
要定义一个区块,Blade 使用
@yield
注解,后面跟上区块的名称。在我们的布局中,我们定义了两个区块:css
和content
。 -
有一个功能可以让你在模板中建立 URL。我们希望将 CSS 文件包含在
public/css/layout.css
中,因此我们将使用URL::asset
来构建该 URL。包含 JS 文件也很有帮助。
如你所见,我们包含了一个 layout.css
文件。CSS 和 JS 文件存储在 public
目录下。使用以下代码在 public/css/layout.css
中创建你的 CSS 文件:
.content {
position: fixed;
top: 50px;
width: 100%
}
.navbar ul {
position: fixed;
top: 0;
width: 100%;
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
.navbar li {
float: left;
border-right: 1px solid #bbb;
}
.navbar li:last-child {
border-right: none;
}
.navbar li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
.navbar li a:hover {
background-color: #111;
}
现在,我们可以专注于我们的视图了。模板存储在 resources/views
中,和布局一样,它们需要 .blade.php
文件扩展名。在 resources/views/new.blade.php
中创建视图,内容如下:
@extends('layouts.app')
@section('css')
<link rel="stylesheet" href="{{ URL::asset('css/new.css') }}"
type="text/css">
@endsection
@section('content')
<h2>Add new post</h2>
<form method="post" action="/new">
<div class="component">
<label for="title">Title</label>
<input type="text" name="title"/>
</div>
<div class="component">
<label>Text</label>
<textarea rows="20" name="content"></textarea>
</div>
<div class="component">
<button type="submit">Save</button>
</div>
</form>
@endsection
语法非常直观。该模板从布局模板扩展而来,定义了两个部分或块:css
和 content
。CSS 文件的格式与前一个相同。您可以在 public/css/new.css
中创建该文件,内容类似于以下内容:
label {
display: block;
}
input {
width: 80%;
}
button {
font-size: 30px;
float: right;
margin-right: 20%;
}
textarea {
width: 80%;
}
.component {
padding: 10px;
}
模板的其余部分只是定义了指向同一 URL 的 POST 表单,并带有标题和文本字段。一切准备就绪,可以在浏览器中进行测试!尝试访问 http://localhost:8080/new 或你选择的端口号。你应该会看到与下面截图类似的内容:

管理用户
如前所述,用户认证和授权是大多数框架都包含的功能之一。Laravel 提供了用户模型、注册和认证控制器,使我们的工作变得非常简单。使用它们非常简单:只需添加指向已有控制器的路由,然后添加视图即可。让我们开始吧。
这里有五个路由需要考虑。其中两个属于注册步骤,一个用于获取表单,另一个用于表单提交用户提供的信息。另外三个与身份验证部分有关:一个用于获取表单,一个用于发布表单,还有一个用于注销。这五个函数都包含在 Auth\AuthController 类中。在 routes.php 文件中添加以下路由:
// Registration routes...
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');
// Authentication routes...
Route::get('/login', 'Auth\AuthController@getLogin');
Route::post('login', 'Auth\AuthController@postLogin');
Route::get('logout', 'Auth\AuthController@getLogout');
请注意我们是如何定义这些路由的。与之前创建的路由不同,路由的第二个参数是一个字符串,包含控制器的类名和方法。这是创建路由的更好方法,因为它将逻辑分离到不同的类中,以后可以重复使用和/或进行单元测试。
如果您感兴趣,可以浏览该控制器的代码。你会发现这是一个复杂的设计,路由将调用的函数实际上是 AuthController
类使用的两个特性的一部分: RegistersUsers
和 AuthenticatesUsers
。检查这些方法可以让你了解幕后的工作。
每个 get
路由都希望渲染一个视图。对于用户注册,我们需要在 resources/views/auth/register.blade.php
中创建一个模板;对于登录视图,我们需要在 resources/views/auth/login.blade.php
中创建一个模板。只要我们向正确的 URL 发送正确的 POST 参数,就可以添加任何我们认为必要的内容。
用户注册
让我们从注册表单开始;这个表单需要四个 POST 参数:姓名、电子邮件、密码和密码确认,正如路由所说,我们需要将其提交到 /auth/register
。模板可以如下所示:
@extends('layouts.app')
@section('css')
<link rel="stylesheet" href="{{ URL::asset('css/register.css') }}" type="text/css">
@endsection
@section('content')
<h2>Account registration</h2>
<form method="post" action="/auth/register">
{{ csrf_field() }}
<div class="component">
<label for="name">Name</label>
<input type="text" name="name" value="{{ old('name') }}" />
</div>
<div class="component">
<label>Email</label>
<input type="email" name="email" value="{{ old('email') }}"/>
</div>
<div class="component">
<label>Password</label>
<input type="password" name="password" />
</div>
<div class="component">
<label>Password confirmation</label>
<input type="password" name="password_confirmation" />
</div>
<div class="component">
<button type="submit">Create</button>
</div>
</form>
@endsection
该模板与新文章表单十分相似:它扩展了布局,添加了 CSS 文件,并在内容部分填充了一个表单。这里新添加的功能是,在表单无效的情况下,使用以前的函数检索上次请求中提交的值,并将其显示给用户。
在尝试之前,我们需要添加一个 register.css
文件,为表单添加样式。一个简单的文件如下:
div.content {
text-align: center;
}
label {
display: block;
}
input {
width: 250px;
}
button {
font-size: 20px;
}
.component {
padding: 10px;
}
最后,我们应编辑布局,以便在菜单上添加指向注册和登录页面的链接。只需在 ul
标签末尾添加以下 li
元素即可:
<li class="right"><a href="/auth/register">Sign up</a></li>
<li class="right"><a href="/login">Sign in</a></li>
在 layout.css
末尾也添加正确类别的样式:
div.alert {
color: red;
}
为了让事情变得更有用,我们可以在提交表单时添加出错信息。Laravel 会将错误信息显示在会话中,并可通过 errors
模板变量访问。由于错误信息不仅适用于注册表单,也适用于所有表单,我们可以将其添加到 app.blade.php
布局中,如下所示:
<div class="content">
@if (count($errors) > 0)
<div class="alert">
<strong>Whoops! Something went wrong!</strong>
@foreach ($errors->all() as $error)
<p>{{ $error }}</p>
@endforeach
</div>
@endif
@yield('content')
在这段代码中,我们将使用 Blade 的 @if
条件和 @foreach
循环。语法与 PHP 相同,唯一不同的是 @
前缀。
现在,我们已经准备就绪。启动应用程序,点击菜单右侧的注册链接。尝试提交表单,但某些字段留空,这样我们就能注意到错误是如何显示的。结果应该与下面类似:

我们应该自定义的一点是,一旦注册成功,用户将被重定向到哪里。在这种情况下,我们可以将用户重定向到登录页面。为此,需要更改 AuthController 的 $redirectTo 属性的值。到目前为止,我们只有新帖页面,但以后可以通过以下方式添加任意路径:
protected $redirectPath= '/new;
用户登录
除了注册外,用户登录还需要做一些更改。我们不仅要添加登录视图,还要修改布局中的菜单,以便确认已通过身份验证的用户,删除注册链接,并添加注销链接。如前所述,模板必须保存在 resources/views/auth/login.blade.php
中。该表单需要输入电子邮件和密码,还可以选择复选框来实现 "记住我" 功能。举例如下:
@extends('layouts.app')
@section('css')
<link rel="stylesheet" href="{{ URL::asset('css/register.css') }}" type="text/css">
@endsection
@section('content')
<h2>Login</h2>
<form method="POST" action="/login">
{!! csrf_field() !!}
<div class="component">
<label>Email</label>
<input type="email" name="email" value="{{ old('email') }}">
</div>
<div class="component">
<label>Password</label>
<input type="password" name="password">
</div>
<div class="component">
<input class="checkbox" type="checkbox" name="remember">
Remember Me
</div>
<div class="component">
<button type="submit">Login</button>
</div>
</form>
@endsection
布局需要稍作改动。以前我们显示的是注册和登录用户的链接,现在我们需要检查是否有用户已通过身份验证;如果有,则应显示注销链接。您甚至可以从视图中通过 Auth::user() 方法获取已通过身份验证的用户。如果结果不为空,则表示用户已成功通过身份验证。使用以下代码更改这两个链接:
<ul>
<li><a href="/new">New article</a></li>
<li><a href="/">Articles</a></li>
@if (Auth::user() !== null)
<li class="right">
<a href="/logout">Logout</a>
</li>
@else
<li class="right">
<a href="/auth/register">Sign up</a>
</li>
<li class="right">
<a href="/login">Sign in</a>
</li>
@endif
</ul>
在模型中设置关系
正如我们之前提到的,Laravel 自带 ORM,即 Eloquent ORM,这使得处理模型变得非常容易。在我们的简单数据库中,我们为帖子定义了一个表,为用户定义了另一个表。帖子包含拥有它的用户的 ID,即 user_id。好的做法是使用表名的单数,后面跟 _id,这样 Eloquent 就知道该去哪里找了。这就是我们在外键方面所做的全部工作。
我们还应该提及模型侧的这种关系。根据关系的类型(一对一、一对多或多对多),代码会略有不同。在我们的例子中,我们有一对多的关系,因为一个用户可以有许多帖子。在 Laravel 中,我们需要更新帖子和用户模型。用户模型需要指定它有许多帖子,因此需要添加一个 posts 方法,内容如下:
public function posts() {
return $this->hasMany('App\Post');
}
该方法表示用户模型有许多帖子。Post
中需要做的另一个改动与此类似:我们需要添加一个定义关系的用户方法。该方法应与此方法类似:
public function user() {
return $this->belongsTo('App\User');
}
看起来很少,但这就是我们需要的全部配置。在下一节中,你将看到使用这两个模型保存和查询是多么容易。
创建复杂控制器
尽管本节的标题提到了复杂的控制器,但你会发现我们只需很少的代码就能创建完整而强大的控制器。首先,让我们添加管理帖子创建的代码。该控制器需要链接到以下路由:
Route::post('/new', 'Post\PostController@createPost');
可以想象,现在我们需要创建包含 createPost
方法的 Post\PostController
类。控制器应存储在 app/Http/ Controllers
中,如果能以文件夹的形式组织,那就更好了。将以下类保存在 app/Http/Controllers/Post/PostController.php
中:
namespace App\Http\Controllers\Post;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use App\Post;
class PostController extends Controller {
public function createPost(Request $request) {
}
}
到目前为止,我们可以从这个类中注意到的唯一两件事是:
-
控制器从
App\Http\Controllers\Controller
类扩展而来,该类包含一些适用于所有控制器的通用助手。 -
控制器的方法可以获得作为用户请求的
Illuminate\Http\Request
参数。这个对象将包含发布的参数、cookie等元素。这与我们在自己的应用程序中创建的对象非常相似。
在这种控制器中,我们需要做的第一件事就是检查所发布的参数是否正确。为此,我们将使用以下代码:
public function createPost(Request $request) {
$validator = Validator::make($request->all(), [
'title' => 'required|max:255',
'content' => 'required|min:20',
]);
if ($validator->fails()) {
return redirect()->back()
->withInput()
->withErrors($validator);
}
}
我们首先要做的是创建一个验证器。为此,我们使用了 Validator::make
函数,并发送了两个参数:第一个参数包含请求中的所有参数,第二个参数是一个数组,包含预期字段及其约束条件。请注意,我们希望有两个必填字段:标题和内容。其中,第一个字段的长度最多为 255 个字符,第二个字段的长度至少为 20 个字符。
一旦创建了验证器对象,我们就可以使用 fails
方法检查用户发布的数据是否符合要求。如果返回 true
,即验证失败,我们就会使用 redirect()→back()
将用户重定向到前一页。为了执行这一调用,我们将增加两个方法调用:withInput
将发送已提交的值,以便我们再次显示它们;withErrors
将以与 AuthController
相同的方式发送错误。
此时,如果我们显示之前提交的标题和文本,以防帖子无效,会对用户有所帮助。为此,请使用视图中已知的旧方法:
{{--...--}}
<input type="text" name="title" value="{{ old('title') }}"/>
</div>
<div class="component">
<label>Text</label>
<textarea rows="20" name="content">
{{ old('content') }}
</textarea>
{{--...--}}
至此,我们已经可以测试当帖子不符合所需的验证条件时控制器的行为。如果漏掉任何参数或参数长度不正确,就会出现类似下图的错误页面:

现在让我们添加逻辑,以便在帖子有效的情况下保存帖子。如果您还记得上一个应用程序中与模型的交互,那么您一定会惊讶地发现,在这里与模型的交互是如此简单。请看下面的内容:
public function createPost(Request $request) {
$validator = Validator::make($request->all(), [
'title' => 'required|max:255',
'content' => 'required|min:20',
]);
if ($validator->fails()) {
return redirect()->back()
->withInput()
->withErrors($validator);
}
$post = new Post();
$post->title = $request->title;
$post->content = $request->content;
Auth::user()->posts()->save($post);
return redirect('/new');
}
我们要做的第一件事是创建一个帖子对象,根据请求值设置标题和内容。然后,根据 Auth::user()
的结果(该结果提供了当前已验证用户模型的实例),我们将通过 posts()->save($post)
保存刚刚创建的帖子。如果我们想在不包含用户信息的情况下保存帖子,可以使用 $post->save()
。其实,这就是全部。
让我们快速添加另一个端点来获取给定用户的帖子列表,这样我们就可以看看 Eloquent ORM 是如何让我们轻松获取数据的。添加以下路由:
Route::get('/', ['middleware' => 'auth', function () {
$posts = Auth::user()
->posts()
->orderBy('created_at')
->get();
return view('posts', ['posts' => $posts]);
}]);
我们检索数据的方式与保存数据的方式非常相似。我们需要一个模型的实例—本例中是已通过身份验证的用户—然后我们将添加一个方法调用的连接,在内部生成要执行的查询。在本例中,我们将请求按创建日期排序的帖子。为了向视图发送信息,我们需要传递第二个参数,它将是一个包含参数名称和值的数组。
添加以下模板作为 resources/views/posts.blade.php
,该模板将以表格形式显示认证用户的帖子列表。请注意我们将如何在下面的代码中使用作为模型实例的 $post
对象:
@extends('layouts.app')
@section('css')
<link rel="stylesheet" href="{{ URL::asset('css/posts.css') }}" type="text/css">
@endsection
@section('content')
<h2>Your posts</h2>
<table>
@foreach ($posts as $post)
<tr>
<td>{{ $post->title }}</td>
<td>{{ $post->created_at }}</td>
<td>{{ str_limit($post->content, 100) }}</td>
</tr>
@endforeach
</table>
@endsection
帖子列表最终会显示出来。结果应该与下面的截图类似:

添加测试
在很短的时间内,我们就创建了一个应用程序,让您可以从头开始注册、登录、创建并列出帖子。在本节的最后,我们将讨论如何使用 PHPUnit 测试 Laravel 应用程序。
在 Laravel 中编写测试非常简单,因为它与 PHPUnit 有很好的集成。已经有一个 phpunit.xml
文件、一个定制的 TestCase
类、定制的断言和大量的帮助工具来测试数据库。它还允许你测试路由,模拟 HTTP 请求,而不是测试控制器。我们将在测试创建新帖子时访问所有这些功能。
首先,我们需要删除 tests/ExampleTest.php
,因为它测试的是主页,而我们修改后,它就会失败。别担心,这是一个帮助开发人员开始测试的示例测试,使其失败完全不是问题。
现在,我们需要创建新的测试。为此,我们可以手动添加文件或使用命令行并运行以下命令:
$ php artisan make:test NewPostTest
该命令将创建从 TestCase
扩展而来的 tests/NewPostTest.php
文件。如果打开该文件,会发现其中已经有一个假测试,也可以将其删除。无论如何,您都可以运行 PHPUnit 来确保一切通过。方法与之前相同,如下所示:
$ ./vendor/bin/phpunit
我们可以添加的第一个测试是,我们尝试添加一个新的帖子,但 POST 参数传递的数据无效。在这种情况下,我们应该期望响应包含错误和旧数据,这样用户就可以编辑它,而不用重新编写所有内容。在 NewPostTest
类中添加以下测试:
class NewPostTest extends TestCase
{
public function testWrongParams() {
$user = factory(App\User::class)
->make(['email' => 'test@user.laravel']);
$this->be($user);
$this->call(
'POST',
'/new',
['title' => 'the title', 'content' => 'ojhkjhg']
);
$this->assertSessionHasErrors('content');
$this->assertHasOldInput();
}
}
在测试中,我们可以注意到的第一件事是使用工厂创建用户实例。你可以向 make
调用传递一个数组,其中包含任何你想设置的参数;否则,将使用默认值。得到 user
实例后,我们将把它发送到 be 方法,让 Laravel 知道我们希望该用户是本次测试的授权用户。
设置好测试条件后,我们将使用调用助手来模拟真实的 HTTP 请求。我们必须向该方法发送 HTTP 方法(本例中为 POST)、请求路径以及可选参数。请注意,调用方法会返回响应对象,以备不时之需。
我们将发送标题和内容,但第二项不够长,因此可能会出现一些错误。Laravel 自带多个自定义断言,尤其是在测试此类响应时。在这种情况下,我们可以使用其中的两个:assertSessionHasErrors(断言会话存在错误)和 assertHasOldInput(断言存在旧输入)。前者用于检查会话中是否存在 Flash 错误(尤其是内容参数的错误),后者用于检查响应是否包含旧数据,以便将其显示给用户。
我们要添加的第二个测试是用户发布有效数据的情况,这样我们就可以将发布的数据保存到数据库中。这个测试比较棘手,因为我们需要与数据库交互,这通常不是一个令人愉快的体验。不过,Laravel 提供了足够的工具来帮助我们完成这项任务。首先,也是最重要的一点,是让 PHPUnit 知道我们想在每个测试中使用数据库事务。然后,我们需要在数据库中持久化认证用户,因为帖子有一个外键指向该用户。最后,我们应断言帖子已正确保存在数据库中。在 NewPostTest 类中添加以下代码:
use DatabaseTransactions;
//...
public function testNewPost() {
$postParams = [
'title' => 'the title',
'content' => 'In a place far far away.'
];
$user = factory(App\User::class)
->make(['email' => 'test@user.laravel']);
$user->save();
$this->be($user);
$this->call('POST', '/new', $postParams);
$this->assertRedirectedTo('http://localhost/new');
$this->seeInDatabase('posts', $postParams);
}
DatabaseTransactions 特质将使测试在开始时启动一个事务,并在测试完成后将其回滚,因此我们不会在数据库中留下测试的数据。在数据库中保存经过验证的用户也很容易,因为工厂的结果是用户模型的实例,我们只需调用保存方法即可。
assertRedirectedTo 断言将确保响应包含将用户重定向到指定 URL 的有效头信息。更有趣的是,seeInDatabase 将验证 posts 表(即第一个参数)中是否存在实体,以及数组(即第二个参数)中提供的数据。
虽然断言很多,但正如你所注意到的,它们非常有用,可以将冗长的测试缩减为寥寥几行。我们建议您访问官方文档,查看完整列表。