主题
用户注册
介绍
该章节我们将学习如何在 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
已经为我们创建了一个用户模型 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
,你会看到一个如下内容,就代表用户注册页面可以正常访问了
创建注册页面
目前只是一个空白页面,现在我们要用添加一些基本的 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>
如下图所示 然后我们点击 注册 按钮,就会出现下面的错误。
这个错误说明
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 的开发者,一般都会被这个错误搞的有点稀里糊涂的。这是由于框架从安全方面考虑,称为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.php
的 store
方法,使用 dd
方法打印请求的参数
php
public function store(Request $request)
{
// 这个 dd 方法是自带的,用于打印变量
dd($request->all());
}
dd
方法打印变量$request->all()
可以获取请求参数
然后在点击注册按钮,在浏览器会看到下面如图的输出
创建验证器
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>
再点击注册按钮,会看到如下的页面,你会看到邮箱验证错误,添加了红色的边框
这里虽然显示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
代码写好之后,再次点击注册,会出现如下页面,就说明成功了。
完整的表单验证代码
完整的代码如下
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 很贴心的想到了这一点。我们只需要做一丢丢的改动,就可以实现这个功能。还是用邮箱在做说明
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") }}' />
密码表单就不保存了,每次都要清空。我们再次回到注册页面,点击按钮试试看。记得故意输入错误。如图所示,即使提交错误,原表单值还是被保留了。
好了,对于表单提交的一些基本功能和需求目前都已经满足了,下面我们开始进入到用户注册的业务逻辑。一切就绪之后,用户注册就很简单了。我们只需要在 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
页面。如果失败,则会跳转到注册页面。但是这里有个小细节,注册失败之后有个错误信息需要显示,这里我们就显示在密码框下面了。
来尝试一下,输入符合验证器的表单数据。然后再次点击注册按钮。你会看到如图所示 由于登录页面不存在,所以跳转出现了
404
页面。
趁热打铁,下一节内容,我们快速的实现登录功能。Laravel 自带 Auth 认证非常强大,只需要几行代码代码便可以实现安全的登录功能