完成賬戶信息修改功能
- 利用 resource 路由為我們提供的路由: 'user.edit'載入修改資料的界面 和 'user.update'完成修改的邏輯
- 編輯對(duì)應(yīng)方法 UserController@edit 載入視圖
public function edit(User $user) //這里通過(guò)依賴注入實(shí)例化了 $user
{
return view('user.edit', compact('user')); //這里將 $user 傳給視圖
}
- 編輯視圖處理 : 記得偽造為 method="PATCH" 的表單
@extends('layouts.master')
@section('title', '修改資料')
@section('content')
<div class="container">
<h1 class="text-center">修改資料</h1>
@include('components._error', ['errors' => $errors])
<form method="POST" action=" {{ route('user.update', $user->id) }} ">
{{-- 重點(diǎn): 因?yàn)?user.update 路由要求method="PUT/PATCH" 但是普通form并不支持穴翩,所以需要我們自己添加一個(gè) input.hidden 表單項(xiàng)來(lái)偽造 --}}
@method('PATCH')
@csrf
<div class="form-group">
<label for="name">用戶名</label>
<input type="text" class="form-control" id="name" placeholder="請(qǐng)輸入用戶名" name="name" value="{{ old('name') ? old('name') : $user->name }}">
{{-- 這里是判斷 old('name') 是否有值叼风,如果沒(méi)有扼菠,就用我們傳過(guò)來(lái)的$user --}}
</div>
<div class="form-group">
<label for="password">新密碼</label>
<input type="password" class="form-control" id="password" placeholder="請(qǐng)輸入密碼" name="password">
<input type="password" class="form-control" placeholder="請(qǐng)?jiān)诖溯斎朊艽a以確認(rèn)" name="password_confirmation">
</div>
<button type="rest" class="btn btn-secondary">重置</button>
<button type="submit" class="btn btn-primary">修改</button>
</form>
</div>
@endsection
- 在 _navbar.blade.php 綁定一個(gè)跳轉(zhuǎn)到編輯頁(yè)面的超鏈接
Auth::id()
獲取當(dāng)前登陸用戶的主鍵id
<a href="{{ route('user.edit', Auth::id()) }}" class="btn btn-outline-success"> {{ Auth::user()->name }} </a>
- 完善 UserController@update 更新數(shù)據(jù)庫(kù)中的用戶信息
public function update(UserRequest $request, User $user)
{
// ...
}
- 上面有兩個(gè)坑: 第一個(gè)是
User $user
=> 路由要求我們得傳修改數(shù)據(jù)的主鍵進(jìn)來(lái)坦刀! => 因此表單上<form ... action=" {{ route('user.update', $user->id) }} ">
- 第二個(gè)就是使用
UserRequest $request
驗(yàn)證數(shù)據(jù)的時(shí)候洽议,會(huì)報(bào)錯(cuò)“郵箱沒(méi)填虏肾,用戶名重復(fù)等...”
return [
'name' => 'required|min:8|max:32|unique:users,name,' . Auth::id(), // unique:表,字段,排除校驗(yàn)自己
'email' => 'sometimes|required|email|unique:users', // sometimes 只有在表單提交的數(shù)據(jù)中履恩,存在 email 字段時(shí)校驗(yàn)
'password' => 'required|min:8|confirmed'
];
- 最后我還是覺(jué)得這種太麻煩了甸祭,我不如自己再新建一個(gè) Request 來(lái)完成關(guān)于 “更新邏輯” 的驗(yàn)證
php artisan make:request UserEditRequest
編輯 UserEditRequest@rule 并將 UserRequest 還原
public function rules()
{
return [
'name' => 'required|min:8|max:32|unique:users,name,' . Auth::id(),
'password' => 'nullable|min:8|confirmed'
];
}
- 完善功能 UserController@update
public function update(UserEditRequest $request, User $user) // 這里通過(guò) UserEditRequest 驗(yàn)證
{
// 判斷是否修改過(guò)用戶名或者密碼
if($request->name == $user->name || Hash::check($request->password, $user->password)) { // Hash::check(v1, v2) 判斷v1加密后是否等于v2
session()->flash('danger', '您未修改任何內(nèi)容');
return redirect()->back();
}
// 處理數(shù)據(jù)
$data['name'] = $request->name;
if($request->password) {
$data['password'] = Hash::make($request->password);
}
// 執(zhí)行更新和跳轉(zhuǎn)
$user->update($data);
session()->flash('success', '編輯資料成功!');
return redirect()->route('user.show', [$user]);
}
用戶資料編輯總結(jié)
- UserController@edit 展示編輯頁(yè)面善已, 在編輯頁(yè)面中需要使用
@method('PATCH')
來(lái)偽造表單的 action="PATCH" (因?yàn)槠胀?form 不支持patch)灼捂, 同時(shí)別忘了使用input.value={{ old('name') ? old('name') : $user->name }}
來(lái)判斷是否 修改出錯(cuò),出錯(cuò)則用 old() 調(diào)出原來(lái)的值换团,沒(méi)出錯(cuò)就用 $user->name 顯示用戶名悉稠。 - UserController@update 完成 “接受和驗(yàn)證數(shù)據(jù)->數(shù)據(jù)入庫(kù)->完成跳轉(zhuǎn)” 邏輯。
- 接受和驗(yàn)證數(shù)據(jù)艘包,我們先開始通過(guò)編輯 UserRequest@rule 來(lái)重新設(shè)定驗(yàn)證規(guī)則的猛,但是非常繁瑣:
-
'name' => '..|unique:users,name,'.Auth::id()
=> 排除唯一驗(yàn)證時(shí)沒(méi)修改name導(dǎo)致驗(yàn)證自己和自己重名 -
'email' => 'sometimes|...'
=> 當(dāng)表單中有 email 時(shí)再驗(yàn)證...
-
- 所以我們新建了一個(gè) Request 進(jìn)行驗(yàn)證
php artisan make:request UserEditRequest
耀盗, 只驗(yàn)證用戶名和密碼。 nullable 可以為空卦尊。
public function rules() { return [ 'name' => 'required|min:8|max:32|unique:users,name,' . Auth::id(), 'password' => 'nullable|min:8|confirmed' ]; }
- update() 方法需要2個(gè)參數(shù)叛拷,第一個(gè)就是我們上面驗(yàn)證的,第二個(gè)則是修改用戶的主鍵id猫牡,我們可以通過(guò)
Auth::id()
或者傳遞過(guò)去的$user$user->id
來(lái)獲取胡诗。因此form.action="{{ route('user.update', $user->id) }}"
- update() 方法中,我們需要判斷用戶是否修改了用戶名或者密碼之一淌友,然后需要處理要更新的數(shù)據(jù) $data 煌恢,判斷密碼是否為空,為空則不更新密碼震庭。最后使用 $user->update($data) 更新數(shù)據(jù)
// 唯一需要注意的就是 Hash::check(用戶輸入的密碼, 經(jīng)過(guò)Hash加密的舊密碼) => 是將用戶輸入的密碼加密后 對(duì)比舊密碼瑰抵,如果兩者相同,則返回true
- 接受和驗(yàn)證數(shù)據(jù)艘包,我們先開始通過(guò)編輯 UserRequest@rule 來(lái)重新設(shè)定驗(yàn)證規(guī)則的猛,但是非常繁瑣:
使用中間件 middleware 判斷用戶是否登陸
- 定義構(gòu)造函數(shù): UserController@__contrusct
public function __construct()
{
$this->middleware('auth'); //調(diào)用 中間件auth 判斷用戶是否登陸
}
- 這樣一來(lái)器联,用戶不登陸二汛,訪問(wèn)控制器內(nèi)的任何方法,都會(huì)被強(qiáng)制跳轉(zhuǎn)到登陸頁(yè)面拨拓,但是有一個(gè)問(wèn)題肴颊,注冊(cè)和新增用戶也會(huì)強(qiáng)制跳轉(zhuǎn)到登陸頁(yè)面,所以我們應(yīng)該使用
...->except(['方法名1', '方法名2'])
排除那些不需要登陸就可以訪問(wèn)的方法
/**
* 定義可以被公開訪問(wèn)(不需要登陸)的方法
*/
private $public = ['create', 'store'];
/**
* 通過(guò)構(gòu)造函數(shù)調(diào)用中間件判斷用戶是否登陸
*/
public function __construct()
{
// 調(diào)用中間件 “auth” 判斷用戶是否登陸 -> 排除(可以被公開訪問(wèn)的方法)
$this->middleware('auth')->except($this->public);
}
使用 Policy 來(lái)控制權(quán)限
上面實(shí)現(xiàn)的編輯用戶資料的功能其實(shí)有很嚴(yán)重的權(quán)限錯(cuò)誤:應(yīng)該設(shè)置只能自己編輯自己渣磷!但是其實(shí)通過(guò)修改瀏覽器地址指向的url婿着,我們可以編輯所有其他的用戶。
- 創(chuàng)建 Policy 策略
php artisan make:policy UserPolicy
=> 會(huì)在 /app/policies/ 下創(chuàng)建一個(gè) UserPplicy 文件 - 但是這樣創(chuàng)建在接下來(lái)我們依然要去綁定模型和自己編寫方法醋界,太麻煩了竟宋,我們創(chuàng)建一個(gè)針對(duì) User 模型的策略。(即修改 users表的權(quán)限控制策略)
php artisan make:policy UserPolicy --model=User
形纺,查看它的內(nèi)部代碼
<?php
namespace App\Policies;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization; //引用trait
public function view(User $user, User $model) //view策略
{
//
}
public function create(User $user) //create策略
{
//
}
public function update(User $user, User $model) //update策略
{
//
}
public function delete(User $user, User $model) //delete策略
{
//
}
}
// 只要對(duì)應(yīng)的策略返回 true丘侠,則表明可以執(zhí)行接下來(lái)的操作。
- 當(dāng)然現(xiàn)在這個(gè)東西是不可以直接使用的逐样!需要在 “服務(wù)提供商Providers” /app/Providers/ 下的 AuthServiceProvider 中注冊(cè)
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
'App\User' => 'App\policies\UserPolicy', //這里填寫UserPolicy的命名空間
];
- 在控制器中調(diào)用策略進(jìn)行權(quán)限認(rèn)證 UserController@update
authorize('驗(yàn)證方法名', 用戶)
public function update(UserEditRequest $request, User $user) // 這里通過(guò) UserEditRequest 驗(yàn)證
{
// 判斷用戶是否在權(quán)限策略內(nèi)進(jìn)行操作(自己正在更新自己蜗字,而不是越權(quán)更新他人)
$this->authorize('update', $user); //這里的 'update' 對(duì)應(yīng) UserPolicy@update, $user 對(duì)應(yīng) 當(dāng)前用戶
...
}
- 完善 UserPolicy@update 方法返回值為 true 則通過(guò)驗(yàn)證
public function update(User $user, User $model) //第一個(gè)參數(shù)表示當(dāng)前登陸的用戶,第二個(gè)參數(shù)表示被修改的用戶
{
// 判斷當(dāng)前登陸的用戶是否為當(dāng)前被修改的用戶
return $user == $model;
}
總結(jié) Policy 的使用
- Policy 策略定義文件都在 /app/Policies/ 下
- 創(chuàng)建 Policy 并指定該策略針對(duì)的模型
php artisan make:policy XxPolicy --model=Xx
- 創(chuàng)建好的 Policy 里面的方法就是不同的權(quán)限認(rèn)證場(chǎng)景脂新,在控制器中使用
$this->authorize('方法名', 當(dāng)前用戶)
進(jìn)行調(diào)用秽澳。 一旦 方法() 返回true 則通過(guò)驗(yàn)證,因此在方法內(nèi)戏羽,我們需要進(jìn)行條件判斷 - 創(chuàng)建好的 Policy 不可以直接使用,需要在 /app/Providers/ 內(nèi)注冊(cè)楼吃,這里我們是針對(duì)用戶權(quán)限的策略始花,因此需要在AuthServiceProvider 中注冊(cè)
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
'App\User' => 'App\policies\UserPolicy', // 模型命名空間 => 策略命名空間
];