在前段時間發(fā)布的 ubisend 中,急需一個方案解決用戶可以邀請其他人員管理其賬號的問題。
想想當下的 CMS 系統(tǒng),如果只有一個用戶擁有發(fā)布內容的權限將不能使得系統(tǒng)物盡其用。當然你也不希望通過共享密碼的方式讓其他用戶可發(fā)布內容。
同樣地,假設當你在搭建一個多租戶應用程序,同時想實現(xiàn)讓你的用戶擁有邀請其他用戶加入他們團隊的功能。該怎么做呢?
最直接的當然是使用各種增刪改查來管理用戶,但相比于郵件邀請,允許這些用戶設置自己的密碼安全性較低。
基石
在多種實現(xiàn)方法中,最簡單的是——創(chuàng)建一個新用戶記錄(未激活的),然后存儲跟激活操作相關聯(lián)的 token 值。
然而,這里我們將介紹另一種方法。通過添加額外的 invite 表——存儲用戶郵箱地址和用于激活的 token 值來解決這一問題。激活后,將使用該表中的數(shù)據來創(chuàng)建新用戶。
全新安裝 laravel5.4 后,先配置好數(shù)據庫和郵箱等設置。
遷移
框架安裝時自動添加了 user 表的遷移文件,現(xiàn)在只要將其中的 name 和 password 字段刪除便可,刪除完文件內容如下所示。
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('email')->unique();
$table->rememberToken();
$table->timestamps();
});
}
在控制臺項目的根目錄下執(zhí)行命令(接下來簡稱為執(zhí)行命令):php artisan make:migration create_invites_table,創(chuàng)建 invite 表的遷移文件。
編輯 database/migrations 目錄下的 create_invite_table.php。添加自增 ID,用戶郵箱和用于唯一識別接受邀請用戶的 token 等字段。
public function up()
{
Schema::create('invites', function (Blueprint $table) {
$table->increments('id');
$table->string('email');
$table->string('token', 16)->unique();
$table->timestamps();
});
}
public function down()
{
Schema::drop('invites');
}
執(zhí)行命令:php artisan migrate。隨之,數(shù)據庫將完成遷移。
模型
我們需要創(chuàng)建 Eloquent 模型來管理團隊和用戶。但 laravel 默認含 user 表的 Eloquent 模型,所以這里不需要再創(chuàng)建了。
執(zhí)行命令:php artisan make:model Invite,創(chuàng)建 invite 表的模型。
按照下述在 app 目錄下的 invite.php 中編輯白名單,從而實現(xiàn)批量創(chuàng)建和更新數(shù)據表數(shù)據。
protected $fillable = [
'email', 'token',
];
路由
本次教程中,將為接下來的情況定義三個路由。
- 顯示邀請新用戶的表單頁面
- 處理所提交的表單
- 接受邀請
在 app/routes/web.php 文件中,添加如下路由:
Route::get('invite', 'InviteController@invite')->name('invite');
Route::post('invite', 'InviteController@process')->name('process');
// {token} 必須傳遞給控制器中方法的參數(shù)
Route::get('accept/{token}', 'InviteController@accept')->name('accept');
控制器
眼尖的讀者會發(fā)現(xiàn),上述的路由中都是通過 InviteController 來處理所有請求。
執(zhí)行命令:php artisan make:controller InviteController ,創(chuàng)建控制器。
在 app/Http/Controllers/InviteController.php 文件中定義這些方法:
public function invite()
{
// 為用戶展示填寫邀請郵箱的表單
}
public function process()
{
// 處理用戶所提交的表單和發(fā)送郵件到邀請郵箱
}
public function accept($token)
{
// 可通過 URl 中提供的 token 值來查找用戶
}
這樣一來,萬事齊全。只要我們理清邏輯填寫上述方法便可實現(xiàn)用戶邀請系統(tǒng)了!
業(yè)務邏輯
接下來將逐一闡述每個方法的實現(xiàn)邏輯。
invite()
這段代碼比較簡單,只需要給使用者一個可以填寫被邀請者郵箱的表單即可。
類似于:
public function invite()
{
return view('invite');
}
新建 resources/views/invite.blade.php,視圖中的表單通過 POST 方法將填入的
email 發(fā)送到路由 invite 上。
// 使用命名的路由,以防 URL 發(fā)生變化
// 表單不會中斷
<form action="{{ route('invite') }}" method="post">
{{ csrf_field() }}
<input type="email" name="email" />
<button type="submit">Send invite</button>
</form>
process()
注意,前方高能。這是整個流程的核心方法!
前提:使用 Laravel 的 mailables 來給新用戶發(fā)送邀請通知。
首先,執(zhí)行命令 php artisan make:mail InviteCreated,創(chuàng)建 maliable 類。
編輯 app/Mail 目錄下 InviteCreated 文件。修改該類中的構造方法,依賴注入 Invite 模型并且將其設置為 public。
use App\Invite;
public function __construct(Invite $invite)
{
$this->invite = $invite;
}
接著設置郵件是誰發(fā)送的,郵件發(fā)送的內容。
public function build()
{
return $this->from('you@example.com')
->view('emails.invite');
}
resources/views/invite.blade.php 視圖的簡單例子:
<p>Hi,</p>
<p>Someone has invited you to access their account.</p>
<a href="{{ route('accept', $invite->token) }}">Click here</a> to activate!
你可能會擔心在視圖中如何獲得 $invite 的值。放心吧,laravel 它會將 mailable 類中 public 屬性的值自動傳遞到視圖上。
回到 InviteController,我們需要生成當新用戶接受邀請后用來識別其身份的唯一隨機數(shù)——token。然后,將該 token 和用戶輸入的 email 值將存到 invite 表中。
use App\Invite;
use App\Mail\InviteCreated;
use Illuminate\Support\Facades\Mail;
...
public function process(Request $request)
{
// 驗證請求中的數(shù)據
do {
// 使用 laravel 的 str_random() 輔助方法來生成token隨機數(shù)
$token = str_random();
} //校驗 token 值若已經存在,則重新生成
while (Invite::where('token', $token)->first());
// 在 Invite 表中創(chuàng)建新紀錄
$invite = Invite::create([
'email' => $request->get('email'),
'token' => $token
]);
// 發(fā)送郵件
Mail::to($request->get('email'))->send(new InviteCreated($invite));
// 返回上一級
return redirect()
->back();
}
因此,新用戶不僅接收到通知,數(shù)據還被存儲在 invite 表中。
accept()
最后,還差一個方法來處理新用戶接收邀請的邏輯。
通常情況下,你想要獲取密碼和其他可能會用到的用戶數(shù)據。但是,現(xiàn)在只要把流程走通便可,怎么簡單怎么來吧。邏輯為——基于驗證 token 值來決定是否創(chuàng)建新用戶。
記住,URL 中的 token 必須作為參數(shù)傳回。
use App\User;
use App\Invite;
use App\Mail\InviteCreated;
use Illuminate\Support\Facades\Mail;
...
public function accept($token)
{
// 查找邀請記錄
if (!$invite = Invite::where('token', $token)->first()) {
//if the invite doesn't exist do something more graceful than this
abort(404);
}
// 根據 invite 表中的數(shù)據創(chuàng)建新用戶
User::create(['email' => $invite->email]);
// 刪除 invite 表中的該條記錄
$invite->delete();
// 這里你可能會實現(xiàn)讓用戶登錄,然后讓其進入首頁的功能。但是現(xiàn)在我們只要確保它能順利執(zhí)行
return 'Good job! Invite accepted!';
}
運行
在瀏覽器中點擊邀請網址 ( 例如 http://example.com/invite ),輸入邀請對象郵箱并提交表單。
打開 invite 表將看到一條含唯一 token 值的新紀錄將被創(chuàng)建。打開郵箱將會發(fā)現(xiàn)含數(shù)據庫中已存在的 token 值的激活鏈接。
當點擊完激活鏈接后將會彈出“干的漂亮!邀請被接收!”的標志。再校驗一下,是否按預期流程處理。數(shù)據庫中 invite 表的相應記錄被刪除,相反地,user 表將創(chuàng)建一條新記錄。
擴展
你已經成功實現(xiàn)了用戶邀請系統(tǒng)。
雖然這是一個簡單的例子,但是它奠定了良好的基礎。
可基于該例子擴展當用戶接收邀請時捕獲用戶信息以及拒絕和重新發(fā)送邀請等功能。
另外,你也可修改代碼從而支持多租戶或團隊/用戶關系——雖然這實現(xiàn)起來有點復雜。但是相信沒有什么是不能通過你的智慧去實現(xiàn)的。