新建一个分支:
git checkout master
git checkout -b sign-up
我们之前已使用 Tinker 成功创建了一个用户对象。
运行下面命令使用 Tinker 来查询用户的信息,确保该用户对象在数据库中确实存在:
$ php artisan tinker
从数据库中获取第一号用户的个人信息:
>>> App\Models\User::first()
=> App\Models\User {#2909
id: 1,
name: "Summer",
email: "summer@example.com",
email_verified_at: null,
created_at: "2018-12-12 09:09:53",
updated_at: "2018-12-12 09:21:53",
}
通过 Tinker 返回的信息可以看到,id 为 1,
用户名为 Summer 的用户信息被成功返回。
接下来让我们开始着手构建用户个人页面,
并在此页面上对用户信息进行显示。
新增的 resource 方法将遵从 RESTful 架构为用户资源生成路由。该方法接收两个参数,第一个参数为资源名称,第二个参数为控制器名称。
Route::resource('users', 'UsersController');
上面代码将等同于:
Route::get('/users', 'UsersController@index')->name('users.index');
Route::get('/users/create', 'UsersController@create')->name('users.create');
Route::get('/users/{user}', 'UsersController@show')->name('users.show');
Route::post('/users', 'UsersController@store')->name('users.store');
Route::get('/users/{user}/edit', 'UsersController@edit')->name('users.edit');
Route::patch('/users/{user}', 'UsersController@update')->name('users.update');
Route::delete('/users/{user}', 'UsersController@destroy')->name('users.destroy');
接下来我们定义方法:
app/Http/Controllers/UsersController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class UsersController extends Controller
{
//
public function create()
{
return view('users.create');
}
public function show(User $user)
{
return view('users.show', compact('user'));
}
}
~
/*
public function show(User $user)
{
return view('users.show', compact('user'));
return view('users.show', [
'user'=>$user
]);
}
//所以在视图里访问 user 变量的时候,实际访问的就是这个数组里 user 键所对应的内容
//创建一个包含变量名和它们的值的数组
*/
Laravel 会自动解析定义在控制器方法(变量名匹配路由片段)中的 Eloquent 模型类型声明。
在上面代码中,由于 show() 方法传参时声明了类型 —— Eloquent 模型 User,对应的变量名 $user 会匹配路由片段中的 {user},这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。
此功能称为 『隐性路由模型绑定』,是『约定优于配置』设计范式的体现,同时满足以下两种情况,此功能即会自动启用:
路由声明时必须使用 Eloquent 模型的单数小写格式来作为路由片段参数,User 对应 {user}:
Route::get('/users/{user}', 'UsersController@show')->name('users.show');
在使用资源路由 Route::resource('users', 'UsersController'); 时,默认已经包含了上面的声明。
控制器方法传参中必须包含对应的 Eloquent 模型类型声明,并且是有序的:
public function show(User $user)
{
return view('users.show', compact('user'));
}
当请求 weibo.test/users/1 并且满足以上两个条件时,Laravel 将会自动查找 ID 为 1 的用户并赋值到变量 $user 中,如果数据库中找不到对应的模型实例,会自动生成 HTTP 404 响应。
return view('users.show', compact('user'));
我们将用户对象 $user 通过 compact 方法转化为一个关联数组,并作为第二个参数传递给 view 方法,将数据与视图进行绑定。
show 方法添加完成之后,我们便能在视图中使用 user 变量来访问通过 view 方法传递给视图的用户数据。
由于我们还没有创建用户个人页面,因此这时访问用户页面时会出现如下报错。
View [users.show] not found.
下面让我们来新建一个用户个人页面。
$ vi resources/views/users/show.blade.php
@extends('layouts.default')
@section('title', $user->name)
@section('content')
{{ $user->name }} - {{ $user->email }}
@stop
由于我们使用了 view('users.show', compact('user')) 将用户数据与视图进行绑定,因此在视图中可以直接使用 $user 来访问用户实例。
Gravatar 头像和侧边栏
$ vi app/Models/User.php
public function gravatar($size = '100')
{
$hash = md5(strtolower(trim($this->attributes['email'])));
return "http://www.gravatar.com/avatar/$hash?s=$size";
}
1 为 gravatar 方法传递的参数 size 指定了默认值 100;
2 通过 $this->attributes['email'] 获取到用户的邮箱;
3 使用 trim 方法剔除邮箱的前后空白内容;
4 用 strtolower 方法将邮箱转换为小写;
5 将小写的邮箱使用 md5 方法进行转码;
6 将转码后的邮箱与链接、尺寸拼接成完整的 URL 并返回;
接下来让我们来构建一个全局通用的局部视图,用于展示用户的头像和用户名等基本信息。
$ vi resources/views/shared/_user_info.blade.php
<a href="{{ route('users.show', $user->id) }}">
<img src="{{ $user->gravatar('140') }}" alt="{{ $user->name }}" class="gravatar"/>
</a>
<h1>{{ $user->name }}</h1>
该视图将被嵌套在用户个人页面中进行使用,
因此我们需要对用户个人页面进行更改,
加入我们新创建的用户信息局部视图。
$ vi 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('shared._user_info', ['user' => $user])
</section>
</div>
</div>
</div>
</div>
@stop
接下来对样式再进行优化。
$vi resources/sass/app.scss
/* User gravatar */
section.user_info {
padding-bottom: 10px;
margin-top: 20px;
text-align: center;
.gravatar {
float: none;
max-width: 70px;
}
h1 {
font-size: 1.4em;
letter-spacing: -1px;
margin-bottom: 3px;
margin-top: 15px;
}
}
.gravatar {
float: left;
max-width: 50px;
border-radius: 50%;
}
npm run dev
Git 代码版本控制
接着让我们将这些修改加入到版本控制中:
$ git add -A
$ git commit -m "用户显示页面"
在我们接下来要进行的用户注册功能开发中,第一个步骤即是构建注册表单。首先我们需要把之前在 Tinker 中创建的所有用户数据进行删除,将数据库重置。重置的方法很简单,只需借助 migrate:refresh 命令,即可完成数据库的重置操作。
$ php artisan migrate:refresh
refresh 的作用是重置数据库并重新运行所有迁移。
表单构建#
现在让我们更改之前已创建的用户注册页面,加入表单元素。
$ vi 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') }}">
{{ csrf_field() }}
<div class="form-group">
<label for="name">名称:</label>
<input type="text" name="name" class="form-control" value="{{ old('name') }}">
</div>
<div class="form-group">
<label for="email">邮箱:</label>
<input type="text" name="email" class="form-control" value="{{ old('email') }}">
</div>
<div class="form-group">
<label for="password">密码:</label>
<input type="password" name="password" class="form-control" value="{{ old('password') }}">
</div>
<div class="form-group">
<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
Laravel 提供了全局辅助函数 old 来帮助我们在 Blade 模板中显示旧输入数据。
这样当我们信息填写错误,
页面进行重定向访问时,
输入框将自动填写上最后一次输入过的数据。
{{ old('name') }}
Laravel 为了安全考虑,会让我们提供一个 token(令牌)来防止我们的应用受到 CSRF(跨站请求伪造)的攻击。
修复该异常的方法很简单,我们只需要在表单元素中添加 Blade 模板为我们提供的 csrf_field 方法即可。
该方法在 Blade 模板中调用如下:
{{ csrf_field() }}
上面这段代码转换为 HTML 如下所示:
<input type="hidden" name="_token" value="fhcxqT67dNowMoWsAHGGPJOAWJn8x5R5ctSwZrAq">
由于输入框为 hidden 类型,因此该 input 元素在页面上是不可见的。
现在让我们为注册表单添加 csrf_field 方法。
接着让我们将本次修改加入到版本控制中:
$ git add -A
$ git commit -m "用户注册表单"
用户数据验证#
如果你现在填写注册表单并进行提交,则会出现报错,因为表单还不能真正使用。我们还需要在用户控制器中添加一个用于处理表单数据提交后的 store 方法,用于处理用户创建的相关逻辑。
$ vi app/Http/Controllers/UsersController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class UsersController extends Controller
{
public function create()
{
return view('users.create');
}
public function show(User $user)
{
return view('users.show', compact('user'));
}
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|unique:users|max:50',
'email' => 'required|email|unique:users|max:255',
'password' => 'required|confirmed|min:6'
]);
return;
}
}
在实际开发中,我们经常需要对用户输入的数据进行 验证,在验证成功后再将数据存入数据库。在 Laravel 开发中,提供了多种数据验证方式,在本教程中,我们使用其中一种对新手较为友好的验证方式 - validator 来进行讲解。
validator 由 App\Http\Controllers\Controller 类中的 ValidatesRequests 进行定义,
因此我们可以在所有的控制器中使用 validate 方法来进行数据验证。
validate 方法接收两个参数,
第一个参数为用户的输入数据,
第二个参数为该输入数据的验证规则。
这时候如果你再次填写注册表单并进行提交,
你会发现表单依旧处于不可用状态,这是因为:
在表单信息验证失败时,在页面上没有给出错误提示;
在表单信息验证通过后,页面没有重定向跳转到其它页面,并给出注册成功的提示;
下面让我们针对上面两种情况继续完善注册表单。
git add -A
git commit -m "增加 CSRF 验证"
显示表单错误信息
$ vi resources/views/shared/_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
Blade 支持所有的循环语句和条件判断语句,
如 @if, @elseif, @else, @for, @foreach, @while 等等,
应用在 Blade 中的表达式都需要以 @ 开头
Laravel 默认会将所有的验证错误信息进行闪存。当检测到错误存在时,Laravel 会自动将这些错误消息绑定到视图上,因此我们可以在所有的视图上使用 errors 变量来显示错误信息。需要注意的是,在我们对 errors 进行使用时,要先使用 count($errors) 检查其值是否为空。
$ vi 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">
@include('shared._errors')
<form method="POST" action="{{ route('users.store') }}">
{{ csrf_field() }}
<div class="form-group">
<label for="name">名称:</label>
<input type="text" name="name" class="form-control" value="{{ old('name') }}">
</div>
<div class="form-group">
<label for="email">邮箱:</label>
<input type="text" name="email" class="form-control" value="{{ old('email') }}">
</div>
<div class="form-group">
<label for="password">密码:</label>
<input type="password" name="password" class="form-control" value="{{ old('password') }}">
</div>
<div class="form-group">
<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
添加语言包
composer require "overtrue/laravel-lang:~3.0"
$ vi config/app.php
<?php
return [
.
.
.
'locale' => 'zh-CN',
// Illuminate\Translation\TranslationServiceProvider::class,
Overtrue\LaravelLang\TranslationServiceProvider::class,
还可以手动设置,
具体操作方法,随后专门写一篇blog
git add -A
git commit -m "中文的错误提示信息"
注册成功
目前用户注册失败的逻辑已经处理完成,让我们接着完善用户注册成功后的处理逻辑。
当用户注册完成,且表单信息验证通过后,我们需要做以下两个操作:
将用户提交的信息存储到数据库,并重定向到其个人页面;
在网页顶部位置显示注册成功的提示信息;
让我们针对这两个操作来写具体的逻辑实现代码。
保存用户并重定向
$ vi app/Http/Controllers/UsersController.php
<?php
namespace App\Http\Controllers;
.
.
.
class UsersController extends Controller
{
.
.
//store 方法接受一个 Illuminate\Http\Request 实例参数
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|unique:users|max:50',
'email' => 'required|email|unique:users|max:255',
'password' => 'required|confirmed|min:6'
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
session()->flash('success', '欢迎,您将在这里开启一段新的旅程~');
return redirect()->route('users.show', [$user]);
}
}
redirect()->route('users.show', [$user]);
注意这里是一个『约定优于配置』的体现,
此时 $user 是 User 模型对象的实例。
route() 方法会自动获取 Model 的主键,也就是数据表 users 的主键 id,以上代码等同于:
redirect()->route('users.show', [$user->id]);
$name = $request->name;
如果需要获取用户输入的所有数据,可使用:
$data = $request->all();
用户模型 User::create() 创建成功后会返回一个用户对象,
并包含新注册用户的所有信息。
我们将新注册用户的所有信息赋值给变量 $user,并通过路由跳转来进行数据绑定
session()->flash('success', '欢迎,您将在这里开启一段新的旅程~');
现在验证通过的注册用户已经能够成功创建,并进行重定向跳转了。
接下来我们要做的就是,用户注册成功后,在页面顶部位置显示注册成功的提示信息。
使用 session() 方法来访问会话实例。而当我们想存入一条缓存的数据,
让它只在下一次的请求内有效时,则可以使用 flash 方法。
flash 方法接收两个参数,第一个为会话的键,第二个为会话的值,
我们可以通过下面的这行代码为会话赋值。
之后我们可以使用 session()->get('success') 通过键名来取出对应会话中的数据,
取出的结果为 欢迎,您将在这里开启一段新的旅程~。
接下来的消息提示我们会用会话进行闪存,并分别为其设定好指定的键。
danger, warning, success, info 这四个键名在 Bootstrap 分别具有不同样式展现效果,
因此后面我们将使用这几个键名作为消息提示的专有设定。
现在让我们加入消息提醒视图,让会话消息在视图上进行展示。
$ vi resources/views/shared/_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
session()->has($msg) 可用于判断会话中 $msg 键对应的值是否为空,
若为空则在页面上不进行显示。
最后,我们通过 session()->get($msg) 来取出对应的值并在页面上进行显示。
$ vi resources/views/layouts/default.blade.php
<!DOCTYPE html>
<html>
<head>
<title>@yield('title', 'Weibo App') - Laravel 入门教程</title>
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
@include('layouts._header')
<div class="container">
<div class="offset-md-1 col-md-10">
@include('shared._messages')
@yield('content')
@include('layouts._footer')
</div>
</div>
</body>
</html>
Git 代码版本控制
接下来让我们将改动的代码进行提交,并切回到主分支中进行合并。
开始之前,请确保 npm run watch-poll 窗口已经关闭,然后依次执行以下命令:
$ git add -A
$ git commit -m "完成用户注册功能"
$ git checkout master
$ git merge sign-up