这一篇主要是注册功能的实现
创建注册路由
routes/web.php
// 用户注册表单视图页面路由
Route::get('/signup', 'UsersController@create')->name('signup');
添加控制器
php artisan make:controller UsersController
// 用户注册表单页面
public function create() {
return view('users.create');
}
修改home视图文件中的注册链接
<a class="btn btn-lg btn-success" href="{{ route('signup') }}" role="button">现在注册</a>
添加注册视图文件
resources/views/users/create.blade.php
@extends('layouts.default')
@section('title', '注册')
@section('content')
<h1>注册</h1>
@stop
数据迁移
不必再手动去数据库建表,建字段了,或者创建.sql文件了,而是通过laravel本身提供的数据迁移文件,来创建表,创建字段,或者删除表,删除数据。
迁移文件放在 database/migrations ,默认已经有了四个,分别是:
database/migrations/2014_10_12_000000_create_users_table.php 构建用户表
database/migrations/2014_10_12_100000_create_password_resets_table.php 构建密码重置表
database/migrations/2019_08_19_000000_create_failed_jobs_table.php 构建任务失败表
database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php 构建访问令牌的表
我们来看看用户表的迁移文件中的内容:
定义了一个匿名类,并继承 Migration
,这个类中,有两个方法 up,down
执行迁移时,up会被调用(创建表),执行回滚时,down会被调用(删除表)
创建表
Schema::create('users', function (Blueprint $table) { // 参数1就是表名,参数2是一个接收Blueprint 实例的闭包
// 字段
});
定义字段
用Blueprint实例来定义,也就是上面的 $table
$table->id(); // bigint unsigned 自增 id
$table->string('name'); // 昵称
$table->string('email')->unique(); // 邮箱,具有唯一性
$table->timestamp('email_verified_at')->nullable(); // 邮箱的验证时间,允许空值
$table->string('password'); // 密码
$table->rememberToken(); // 记住我token
$table->timestamps(); // created_at,updated_at -> 创建时间和更新时间
更多说明见文档:https://learnku.com/docs/laravel/9.x/migrations/12248#creating-tables
执行迁移
php artisan migrate
之前在 .env 中配置了数据库,此时可以去看看有没有创建表。
执行迁移回滚
php artisan migrate:rollback
用户模型
app/Models/User.php 这个是默认带的,我们来看看里面的内容
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory; // HasFactory 是模型工厂相关功能的引用
use Illuminate\Foundation\Auth\User as Authenticatable; // Authenticatable 是授权相关功能的引用
use Illuminate\Notifications\Notifiable; // Notifiable 是消息通知相关功能引用
use Laravel\Sanctum\HasApiTokens; // HasApiTokens API 令牌修改功能
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* 在这里面的字段才能被更新
* 防止批量字段更新时,被恶意更改了不应该变更的字段
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* 将表中查询到的某些字段隐藏
* 比如我们返回json格式给客户端,但是password是不需要给的,就可以在这里隐藏
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* 字段在代码中的数据类型
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime', // email_verified_at在表中为时间戳,在项目代码中,变为datetime类型
];
}
演示创建一个Article模型文件
创建model文件
php artisan make:model Article // 注意:按约定是单数命名
// 如果需要同时创建其对应的迁移文件
php artisan make:model Article -m
模型文件讲解
一个最小的模型文件
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasFactory;
}
类名称 -> 表名称
比如: Article -> articles
, User -> users
, BlogPost -> blog_posts
如果你想自定义表名称?
protected $table = 'my_articles';
体验创建,查找,更新 用户
我们已经有了User模型和users表,因此,可以先来体验一下操作用户数据。
可以在tinker交互环境中运行,我这里就简单的使用路由闭包来完成,因为我觉得tinker中写代码不方便。
创建一个用户
Routes/web.php
Route::get('/createUser', function () {
$rs = \App\Models\User::create([
'name' => 'junwind',
'email' => 'junwind@qq.com',
'password' => bcrypt('123123')
]);
var_dump($rs);
});
请求 http://192.168.0.101:8000/createUser ,然后检查表。
查询用户对象
// 引入 User 模型类
use App\Models\User // 提前引入后,该文件后续使用User,就不必都带上完整的命名空间了
User::find(1) // 查找id为1的,如果不存在,则返回null
User::findOrFail(2) // 查询id为2的,如果不存在,则会报错。
Illuminate\Database\Eloquent\ModelNotFoundException with message 'No query results for model [App\Models\User] 2'
User::first() // 表中的第一个
User::all() // 所有
// 为了方便我测试,我直接在web.php路由文件中写model了
Route::get('queryUser', function () {
$user = \App\Models\User::find(1);
dd($user, $user->name);
});
变更用户对象数据
1、先对User对象的属性赋值,再save保存
$user = User::find(1);
$user->name = 'kevin';
$user->save();
2、直接update
$user->update([
'name' => 'junwind'
]);
添加users资源路由
// users资源路由 php artisan route:list
Route::resource('users', 'UsersController');
添加个人信息展示页面
我们知道,这个路由实际是 Route::get(‘/users/{user}’, ‘UsersController@show’)
当我们访问 http://192.168.0.101:8000/users/1 时,其实就是显示id为1的用户信息。
当然,此时我们访问,会报错,因为还没有对应的show()方法。
添加show方法:app/Http/Controllers/UsersController.php
public function show(User $user) { // 这里具有 『隐性路由模型绑定』 ,当然需要符合某种约定才行。
return view('users.show', compact('user')); // 参数1是视图页面,参数2是将$user数据传递给视图页面
}
$user 为 User模型对象,这个对象的查询条件为id,id的值就是路由中的 {user} 值,这里laravel会自动注入{user}的值给$user的。能这么用,是要按一种约定:
- {user} 和 User模型名称一致,只不过是小写的。当然了,资源路由本身就是这种。
- 控制器方法中,必须有模型类的声明。 show(User $user)
- 请求的方式是 http://xx/users/1 这种
创建用户的个人信息页面:
resources/views/users/show.blade.php
@extends('layouts.default')
@section('title', $user->name)
@section('content')
{{ $user->name }} - {{ $user->email }}
@stop
再次访问 http://192.168.0.101/users/1 页面试试看。
生成用户头像
我们先来创建一个自己的助手函数文件,然后在里面添加生成图片的方法。
注意:里面都是静态方法,这是为了方便调用,并且性能也更好。
// 生成首字母base64图片
public static function letter_avatar($text)
{
$total = unpack('L', hash('adler32', $text, true))[1];
$hue = $total % 360;
list($r, $g, $b) = self::hsv2rgb($hue / 360, 0.3, 0.9);
$bg = "rgb({$r},{$g},{$b})";
$color = "#ffffff";
$first = mb_strtoupper(mb_substr($text, 0, 1));
$src = base64_encode('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="100" width="100"><rect fill="' . $bg . '" x="0" y="0" width="100" height="100"></rect><text x="50" y="50" font-size="50" text-copy="fast" fill="' . $color . '" text-anchor="middle" text-rights="admin" alignment-baseline="central">' . $first . '</text></svg>');
return 'data:image/svg+xml;base64,' . $src;
}
// 生成字母对应的RGB颜色码
public static function hsv2rgb($h, $s, $v)
{
$r = $g = $b = 0;
$i = floor($h * 6);
$f = $h * 6 - $i;
$p = $v * (1 - $s);
$q = $v * (1 - $f * $s);
$t = $v * (1 - (1 - $f) * $s);
switch ($i % 6) {
case 0:
$r = $v;
$g = $t;
$b = $p;
break;
case 1:
$r = $q;
$g = $v;
$b = $p;
break;
case 2:
$r = $p;
$g = $v;
$b = $t;
break;
case 3:
$r = $p;
$g = $q;
$b = $v;
break;
case 4:
$r = $t;
$g = $p;
$b = $v;
break;
case 5:
$r = $v;
$g = $p;
$b = $q;
break;
}
return [
floor($r * 255),
floor($g * 255),
floor($b * 255)
];
}
调用时,和其它类一样
use App\Helpers\Helpers;
...
print_r( Helpers::letter_avatar("xqw") );
添加用户头像局部视图文件
resources/views/layouts/_user_info.blade.php
<a href="{{ route('users.show', $user->id) }}">
<img src="{{ $gravatar }}" alt="{{ $user->name }}" class="gravatar"/>
</a>
<h1>{{ $user->name }}</h1>
在用户信息页面中引入这个局部视图 : resources/views/users/show.blade.php
@extends('layouts.default')
@section('title', $user->name)
@section('content')
<div class="row">
<div class="offset-md-2 col-md-8">
<div class="col-md-12">
<div class="offset-md-2 col-md-8">
<section class="user_info">
@include('layouts._user_info', ['user' => $user, 'gravatar' => $gravatar])
</section>
</div>
</div>
</div>
</div>
@stop
控制器中 app/Http/Controllers/UsersController.php :
// 用户信息页面
public function show(User $user) { // 这里具有 『隐性路由模型绑定』 ,当然需要符合某种约定才行。
$gravatar = Helpers::letter_avatar($user->name);
return view('users.show', compact('user', 'gravatar'));
}
最后,再次访问 http://192.168.0.101:8000/users/1、查看效果。
构建注册视图页面
清理数据库数据,其实就是删除users表中的旧数据
php artisan migrate:refresh // 会清空所有数据库数据
注册页面:resources/views/users/create.blade.php
@extends('layouts.default')
@section('title', '注册')
@section('content')
<div class="offset-md-2 col-md-8">
<div class="card ">
<div class="card-header">
<h5>注册</h5>
</div>
<div class="card-body">
<form method="POST" action="{{ route('users.store') }}">
<div class="mb-3">
<label for="name">名称:</label>
<input type="text" name="name" class="form-control" value="{{ old('name') }}">
</div>
<div class="mb-3">
<label for="email">邮箱:</label>
<input type="text" name="email" class="form-control" value="{{ old('email') }}">
</div>
<div class="mb-3">
<label for="password">密码:</label>
<input type="password" name="password" class="form-control" value="{{ old('password') }}">
</div>
<div class="mb-3">
<label for="password_confirmation">确认密码:</label>
<input type="password" name="password_confirmation" class="form-control" value="{{ old('password_confirmation') }}">
</div>
<button type="submit" class="btn btn-primary">注册</button>
</form>
</div>
</div>
</div>
@stop
// 用户注册表单页面
public function create() {
return view('users.create');
}
验证注册表单提交的数据
根据资源路由规则,我们知道,处理新增用户业务的是 UserController@store ,并且是Post方式
因此是在store方法里,接收表单提交的参数,先验证,然后新增到库中。
public function store(Request $request)
{
$this->validate($request, [ // 参数1为用户输入的数据,参数2为对应数据的验证规则
'name' => 'required|unique:users|max:50', // required必填项 unique:users字段唯一性,并且针对的是users表 max:50字段最大长度
'email' => 'required|email|unique:users|max:255', // email邮箱验证
'password' => 'required|confirmed|min:6' // confirmed两次输入的密码必须一致
]);
return;
}
给表单添加csrf_field
为了防止网站被 CSRF(跨站请求伪造)的攻击 ,在表单中,我们应该添加
{{ csrf_field() }}
// 等同于
<input type="hidden" name="_token" value="fhcxqT67dNowMoWsAHGGPJOAWJn8x5R5ctSwZrAq">
这个token值,是基于session的,在config/session.php中的lifetime中配置了过期时间,默认2小时。
显示注册表单提交失败信息
注册提交后,可能会失败,此时应该在页面给出提示。
创建一个失败消息的局部视图文件 : resources/views/layouts/_errors.blade.php
@if (count($errors) > 0)
<div class="alert alert-danger">
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
laravel本身会将验证中的错误闪存到$errors变量中,当有错误存在时,laravel还会自动将错误 $errors 绑定到视图中,所以在视图文件中,可以直接使用 $errors。
在注册表单视图文件中引用错误提示局部视图:resources/views/users/create.blade.php
...
@include('layouts._errors')
<form method="POST" action="{{ route('users.store') }}">
...
使用语言包
我们的错误消息提示默认是英文的,我们使用语言包,将其改为中文的。
方式1:可以自定义语言包。
创建文件 : lang/zh_CN/validation.php
'custom' => [
'email' => [
'required' => '邮箱地址不能为空!',
],
],
方式2:使用第三方库:
composer require overtrue/laravel-lang:~6.0
设置本地化:
config/app.php
'locale' => 'zh_CN',
如果字段名称还是英文的,我们创建语言包文件来解决:lang/zh_CN/validation.php
<?php
return [
'attributes' => [
'name' => '名称',
'email' => '邮箱',
'password' => '密码',
],
];
注册成功的处理
也就是经过了参数验证后,我们还需要处理:
1、用户数据落地,并重定向到个人页面。
public function store(Request $request)
{
$this->validate($request, [ // 参数1为用户输入的数据,参数2为对应数据的验证规则
'name' => 'required|unique:users|max:50', // required必填项 unique:users字段唯一性,并且针对的是users表 max:50字段最大长度
'email' => 'required|email|unique:users|max:255', // email邮箱验证
'password' => 'required|confirmed|min:6' // confirmed两次输入的密码必须一致
]);
$user = User::create([ // 创建成功后返回一个User对象实例
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
return redirect()->route('users.show', [$user]); // route() 方法会自动获取 Model 的主键,也就是数据表 users 的主键 id, 所以 [$user] 等于 [$user->id]
}
2、跳转到个人页面后,显示注册成功的提示信息。
我们可以在注册成功后,加一个闪存,跳转页面后,读取这个闪存。
public function store(Request $request)
{
...
session()->flash('success', '注册成功'); // 参数1是key,参数2是值,读取一次后,数据就失效了 , 读数据 session()->get('success') , session()->has(key)是否存在key
return redirect()->route('users.show', [$user]);
}
因为http协议是无状态的,我们可以用session来保存客户端上一次的状态,当然现在主流是用 Redis
添加消息提示局部视图: resources/views/layouts/_messages.blade.php
@foreach (['danger', 'warning', 'success', 'info'] as $msg)
@if(session()->has($msg))
<div class="flash-message">
<p class="alert alert-{{ $msg }}">
{{ session()->get($msg) }}
</p>
</div>
@endif
@endforeach
在全局默认视图中引入消息视图:resources/views/layouts/default.blade.php
<!DOCTYPE html>
<html>
<head>
<title>@yield('title', 'Weibo App') - Laravel 入门教程</title>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
@include('layouts._header')
<div class="container">
<div class="offset-md-1 col-md-10">
@include('layouts._messages')
@yield('content')
@include('layouts._footer')
</div>
</div>
</body>
</html>
现在来测试一下注册功能,可以创建用户成功了,可以跳转到用户个人信息页面了,并且也有注册成功的提示消息了。