Skip to content

用户注册

介绍

该章节我们将学习如何在 Laravel 中实现用户注册功能。用户注册功能是 Laravel 中非常重要的一个功能,它可以帮助用户注册账号,登录系统,以及进行其他一些操作。对于网站整体的 UI 风格,我们会复用之前章节的布局。

要实现用户注册的功能,我们首先要创建用户的表结构。Laravel 框架自带了用户表结构。这个用户表完全满足了我们对于该项目的用户要求,所以我们不需要再去创建新的表结构。找到 database/migrations/0001_01_01_000000_create_users_table.php 文件,查看内容如下, 主要看 up 方法。这个 migration 执行之后主要生成三张表结构

  • 用户表
  • 密码重置表
  • 会话存储表,会话使用 database 驱动的时候,会使用这个表
php
// 用户表
Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

// 密码重置表
Schema::create('password_reset_tokens', function (Blueprint $table) {
    $table->string('email')->primary();
    $table->string('token');
    $table->timestamp('created_at')->nullable();
});

// session 存储表。session 使用 database 驱动的时候,会使用这个表
Schema::create('sessions', function (Blueprint $table) {
    $table->string('id')->primary();
    $table->foreignId('user_id')->nullable()->index();
    $table->string('ip_address', 45)->nullable();
    $table->text('user_agent')->nullable();
    $table->longText('payload');
    $table->integer('last_activity')->index();
});

根据之前的migration章节,这里需要使用下面的命令

shell
php artisan migrate

INFO

如果之前已经执行过了。那么就不需要重复执行了

打开mysql客户端,这里我使用的是navicat。如果没有也没关系,这个命令是生产级别的,只要没有出现错误,都不会有问题。如图所示 Laravel入门学习-用户注册

创建模型

表结构创建好了,要创建对应的模型。但是对于用户模型,我们不需要创建新的模型,因为 Laravel 已经为我们创建了一个用户模型 User,找到 app/Models/User.php 文件,查看内容如下

php
<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

创建控制器

准备好了基本的数据结构之后,我们开始进行下面的步骤。第一步,创建控制器,使用下面的命令

shell
php artisan make:controller RegisterController

# 将会生成 app\Http\Controllers\RegisterController.php

找到该文件,然后添加一个 index 方法

php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class RegisterController extends Controller
{
    //
    public function index()
    {
        // 这里渲染一个视图,对应的就是 register.blade.php
        return view('register');
    }
}

创建视图

目前还没有 register 视图,我们使用下面的命令来创建视图。

shell
php artisan make:view register

# 将会生成 resources\views\register.blade.php

找到该文件,然后添加一些基本的 HTML 结构

html
<div>
  <h1>这是注册页面</h1>
</div>

添加路由

这些都创建好了之后,我们需要在 routes/web.php 文件中添加路由之后才可以访问,如下

php
Route::get('/register', [\App\Http\Controllers\RegisterController::class, 'index']);

通过浏览器访问 域名/register,你会看到一个如下内容,就代表用户注册页面可以正常访问了 Laravel入门学习-用户注册

创建注册页面

目前只是一个空白页面,现在我们要用添加一些基本的 html 结构。打开 resources/views/register.blade.php 文件,然后添加一些基本的 HTML 结构。 这里为了更加统一,我们选用目前 tailwindcss 社区最火的开源 UI daisyui 来构建页面。首先,我们需要安装 daisyui。找到layout 组件

html
<!-- resources/views/components/layout.blade.php -->
<!-- 添加下面的 CDN -->
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <!-- 添加下面的 CDN 代码 -->
  <link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.14/dist/full.min.css" rel="stylesheet" type="text/css" />
  <script src="https://cdn.tailwindcss.com"></script>
  <title>Laravel</title>
</head>

然后再回到 register.blade.php 文件,用下面的代码覆盖掉原来的

html
<x-layout>
  <x-slot:header>用户注册</x-slot:header>
  <div class="flex flex-col justify-center min-h-full px-6 py-12 lg:px-8">
    <div class="sm:mx-auto sm:w-full sm:max-w-sm">
      <img class="w-auto h-10 mx-auto" src="https://tailwindui.com/plus/img/logos/mark.svg?color=indigo&shade=600" alt="Your Company" />
      <h2 class="mt-10 font-bold tracking-tight text-center text-gray-900 text-2xl/9">注 册 账 户</h2>
    </div>

    <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
      <form class="space-y-6" action="/register" method="POST">
        <label class="flex items-center gap-2 input input-bordered">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70">
            <path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" />
            <path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" />
          </svg>
          <input type="text" class="grow" placeholder="邮箱" name="email" />
        </label>
        <label class="flex items-center gap-2 input input-bordered">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70">
            <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
          </svg>
          <input type="text" class="grow" placeholder="用户名" name="name" />
        </label>
        <label class="flex items-center gap-2 input input-bordered">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70">
            <path
              fill-rule="evenodd"
              d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
              clip-rule="evenodd" />
          </svg>
          <input type="password" class="grow" value="密码" name="password" />
        </label>

        <div>
          <button
            type="submit"
            class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
            注 册
          </button>
        </div>
      </form>
    </div>
  </div>
</x-layout>

如下图所示 Laravel入门教程-用户注册 然后我们点击 注册 按钮,就会出现下面的错误。 Laravel入门教程-用户注册 这个错误说明 register 路由不支持 POST,所以需要我们回到 routes/web.php 文件,新增一个 POST 路由

php
Route::post('/register', [\App\Http\Controllers\RegisterController::class, 'store']); // 新增这个路由

添加路由之后,还需要在 RegisterController.php 文件中添加 store 方法

php
public function store(Request $request)
{

}

然后再点击注册按钮,会出现下面的如图的 419 Page Expired 错误。这是为什么呢? Laravel入门教程- Laravel  419 Page Expired 刚开始使用 Laravel 的开发者,一般都会被这个错误搞的有点稀里糊涂的。这是由于框架从安全方面考虑,称为CSRF 跨站攻击。要解决这个问题,框架提供了非常简单的方式。

在表单添加 CSRF

找到 register.blade.php 文件,添加一个隐藏的 CSRF 字段

html
<!-- 在注册按钮上面添加 csrf, 如下代就行-->
@csrf
<div>
  <button
    type="submit"
    class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
    注 册
  </button>
</div>

添加之后,再点击注册,即可成功提交表单了。但是是空白页面,我们再回到 ReisterController.phpstore 方法,使用 dd 方法打印请求的参数

php
public function store(Request $request)
{
    // 这个 dd 方法是自带的,用于打印变量
    dd($request->all());
}
  • dd 方法打印变量
  • $request->all() 可以获取请求参数

然后在点击注册按钮,在浏览器会看到下面如图的输出 Laravel入门教程-用户注册

创建验证器

INFO

开发中流行这样一句话,用户的任何输入都是不可靠和可信的

基于这样的观点,对于用户的输入我们都需要在后端进行验证,以确保用户输入的数据是合法的。让我们回到注册表单提交的store方法

php
public function store(Request $request)
{
     $request->validate([
            'name' => 'required',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6|max:16|alpha_num',
        ], [
            'name.required' => '请输入姓名',
            'email.required' => '请输入邮箱',
            'email.email' => '请输入正确的邮箱格式',
            'email.unique' => '邮箱已被注册',
            'password.required' => '请输入密码',
            'password.min' => '密码长度不能小于6位',
            'password.max' => '密码长度不能大于16位',
            'password.alpha_num' => '密码只能由字母和数字组成',
        ]);
    // 后续业务逻辑
}

这里要着重说一下 uniques:users,这个验证规则验证 users表 里的 email 字段是否唯一 unique 规则文档。关于其他的规则,可以参考 可用 validation 规则文档

添加完验证规则之后,再点击注册按钮,你会发现一个奇怪的现象,似乎页面没有任何变化,也没有跳转。这是因为 Laravel 的验证功能内部处理导致的,实际上如果通过了验证会继续后面的业务,如果没有通过,那么将会跳转到原页面,所以才没有看到页面变化。

处理验证失败

实际上提交表单之后,没有验证通过的话,在跳转到注册页面的时候会讲错误信息携带过去。你可以通过 @error 指令获取错误。我们回到 register.blade.php 文件,添加一个 @error 指令

php
// 这里使用 email 这个 input 组件
<label class="flex items-center gap-2 input input-bordered @error('email') input-error @enderror">
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70">
    <path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" />
    <path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" />
  </svg>
  <input type="text" class="grow " placeholder="邮箱" name="email" />
</label>

再点击注册按钮,会看到如下的页面,你会看到邮箱验证错误,添加了红色的边框

Laravel入门教程-用户注册-错误

这里虽然显示input的错误,但是还没有显示具体的错误信息。这里还是需要用到 @error 指令,如下

php
// 这里使用 email 这个 input 组件
<label class="flex items-center gap-2 input input-bordered @error('email') input-error @enderror">
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70">
    <path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" />
    <path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" />
  </svg>
  <input type="text" class="grow " placeholder="邮箱" name="email" />
</label>

@error('email')
  <div class="text-white alert alert-error">{{ $message }}</div>
@enderror

代码写好之后,再次点击注册,会出现如下页面,就说明成功了。 Laravel入门教程-用户注册-错误

完整的表单验证代码

完整的代码如下

html
<x-layout>
  <x-slot:header>用户注册</x-slot:header>
  <div class="flex flex-col justify-center min-h-full px-6 py-12 lg:px-8">
    <div class="sm:mx-auto sm:w-full sm:max-w-sm">
      <img class="w-auto h-10 mx-auto" src="https://tailwindui.com/plus/img/logos/mark.svg?color=indigo&shade=600" alt="Your Company" />
      <h2 class="mt-10 font-bold tracking-tight text-center text-gray-900 text-2xl/9">注 册 账 户</h2>
    </div>

    <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
      <form class="space-y-6" action="/register" method="POST">
        <label class="input input-bordered flex items-center gap-2 @error('email') input-error @enderror">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70">
            <path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" />
            <path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" />
          </svg>
          <input type="text" class="grow " placeholder="邮箱" name="email" />
        </label>
        @error('email')
        <div class="text-white alert alert-error">{{ $message }}</div>
        @enderror
        <label class="input input-bordered flex items-center gap-2 @error('name') input-error @enderror">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70">
            <path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
          </svg>
          <input type="text" class="grow" placeholder="用户名" name="name" />
        </label>
        @error('name')
        <div class="text-white alert alert-error">{{ $message }}</div>
        @enderror
        <label class="input input-bordered flex items-center gap-2 @error('password') input-error @enderror">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70">
            <path
              fill-rule="evenodd"
              d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z"
              clip-rule="evenodd" />
          </svg>
          <input type="password" class="grow" name="password" placeholder="密码" />
        </label>
        @error('password')
        <div class="text-white alert alert-error">{{ $message }}</div>
        @enderror

        <div>
          <button
            type="submit"
            class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
            注 册
          </button>
        </div>
        @csrf
      </form>
    </div>
  </div>
</x-layout>

Laravel 入门教程-用户注册-错误

保留输入数据

但是在这里还有一个问题。你发现了没有呢?就是每次提交之后表单的值都被清空了。没有保留表单输入的值。那么有没有什么办法可以保留表单输入的值呢?答案是:当然有。Laravel 很贴心的想到了这一点。我们只需要做一丢丢的改动,就可以实现这个功能。还是用邮箱在做说明

html
<!-- 主要用在表单的 value 添加 old 方法即可 -->
<input type="text" class="grow " placeholder="邮箱" name="email" value='{{ old('email') }}' />

现在我们为表单每个 input的 value 添加 old('表单的name'),这样就可以保留表单输入的值了。再次点击注册,你会看到邮箱输入框的值没有被清空,而是保留了之前的值。

这里就不贴完整的代码,找到对应的 input 框

html
<input type="text" class="grow " placeholder="邮箱" name="email" value='{{ old("email") }}' />
<input type="text" class="grow " placeholder="用户名" name="name" value='{{ old("name") }}' />

密码表单就不保存了,每次都要清空。我们再次回到注册页面,点击按钮试试看。记得故意输入错误。如图所示,即使提交错误,原表单值还是被保留了。 Laravel 入门教程-用户注册-保留输入数据

好了,对于表单提交的一些基本功能和需求目前都已经满足了,下面我们开始进入到用户注册的业务逻辑。一切就绪之后,用户注册就很简单了。我们只需要在 store 方法中完成注册逻辑即可。

php
public function store(Request $request)
{
    $request->validate([
        'name' => 'required',
        'email' => 'required|email|unique:users',
        'password' => 'required|min:6|max:16|alpha_num',
    ], [
        'name.required' => '请输入姓名',
        'email.required' => '请输入邮箱',
        'email.email' => '请输入正确的邮箱格式',
        'email.unique' => '邮箱已被注册',
        'password.required' => '请输入密码',
        'password.min' => '密码长度不能小于6位',
        'password.max' => '密码长度不能大于16位',
        'password.alpha_num' => '密码只能由字母和数字组成',
    ]);

    // 添加数据我们使用用户模型,首先 new User()
    // 得到 $userModel
    $userModel = new User();
    // 填充 name 字段
    $userModel->name = $request->get('name');
    // 填充 email 字段
    $userModel->email = $request->get('email');
    // 填充 password 字段
    // Laravel 框架自带了密码加密功能,使用 bcrpt 方法加密密码
    $userModel->password = bcrypt($request->get('password'));
    // 调用 save 方法保存数据。如果保存成功,则跳转到 login 登录页面,目前还没有
    if ($userModel->save()) {
        return redirect('/login');
    }

    // 如果失败,则还跳转到注册页面
    // withErrors 返回的时候保留错误信息,错误信息显示在密码框下面
    // withInput 会将表单数据重新填充到表单中
     return redirect('/register')->withErrors([
        'password' => '注册失败',
    ])->withInput($request->all());
}

业务逻辑很简单,如果用户保存成功,则跳转到登录页面。目前没有,所以注册成功之后会显示 404 页面。如果失败,则会跳转到注册页面。但是这里有个小细节,注册失败之后有个错误信息需要显示,这里我们就显示在密码框下面了。

来尝试一下,输入符合验证器的表单数据。然后再次点击注册按钮。你会看到如图所示 Laravel 入门教程-用户注册 由于登录页面不存在,所以跳转出现了 404 页面。

趁热打铁,下一节内容,我们快速的实现登录功能。Laravel 自带 Auth 认证非常强大,只需要几行代码代码便可以实现安全的登录功能