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 目录,里面有很多内容。你应该拥有类似下面截图所示的目录结构:

image 2023 11 03 21 37 36 507

让我们来设置数据库。首先要做的是用正确的数据库凭据更新 .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 表:

image 2023 11 04 08 51 50 785

最后,我们需要为博客表创建一个模型。这个模型将从 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 注解,后面跟上区块的名称。在我们的布局中,我们定义了两个区块:csscontent

  • 有一个功能可以让你在模板中建立 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

语法非常直观。该模板从布局模板扩展而来,定义了两个部分或块:csscontent。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 或你选择的端口号。你应该会看到与下面截图类似的内容:

image 2023 11 04 09 16 35 507

管理用户

如前所述,用户认证和授权是大多数框架都包含的功能之一。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 类使用的两个特性的一部分: RegistersUsersAuthenticatesUsers。检查这些方法可以让你了解幕后的工作。

每个 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 相同,唯一不同的是 @ 前缀。

现在,我们已经准备就绪。启动应用程序,点击菜单右侧的注册链接。尝试提交表单,但某些字段留空,这样我们就能注意到错误是如何显示的。结果应该与下面类似:

image 2023 11 04 09 36 59 274

我们应该自定义的一点是,一旦注册成功,用户将被重定向到哪里。在这种情况下,我们可以将用户重定向到登录页面。为此,需要更改 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 中,只需添加 auth 中间件,就能以这种方式定义受保护的路由。用下面的代码更新新的 post 路由:

Route::get('/new', ['middleware' => 'auth', function () {
    return view('new');
}]);

一切准备就绪!请尝试在注销后访问新帖页面;您将被自动重定向到登录页面。你能感受到框架有多么强大吗?

在模型中设置关系

正如我们之前提到的,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>
{{--...--}}

至此,我们已经可以测试当帖子不符合所需的验证条件时控制器的行为。如果漏掉任何参数或参数长度不正确,就会出现类似下图的错误页面:

image 2023 11 04 10 21 31 697

现在让我们添加逻辑,以便在帖子有效的情况下保存帖子。如果您还记得上一个应用程序中与模型的交互,那么您一定会惊讶地发现,在这里与模型的交互是如此简单。请看下面的内容:

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

帖子列表最终会显示出来。结果应该与下面的截图类似:

image 2023 11 04 10 35 03 729

添加测试

在很短的时间内,我们就创建了一个应用程序,让您可以从头开始注册、登录、创建并列出帖子。在本节的最后,我们将讨论如何使用 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 表(即第一个参数)中是否存在实体,以及数组(即第二个参数)中提供的数据。

虽然断言很多,但正如你所注意到的,它们非常有用,可以将冗长的测试缩减为寥寥几行。我们建议您访问官方文档,查看完整列表。