在前段時間釋出的 ubisend 中,急需一個方案解決使用者可以邀請其他人員管理其賬號的問題。
想想當下的 CMS 系統,如果只有一個使用者擁有釋出內容的許可權將不能使得系統物盡其用。當然你也不希望通過共享密碼的方式讓其他使用者可釋出內容。
同樣地,假設當你在搭建一個多租戶應用程式,同時想實現讓你的使用者擁有邀請其他使用者加入他們團隊的功能。該怎麼做呢?
最直接的當然是使用各種增刪改查來管理使用者,但相比於郵件邀請,允許這些使用者設定自己的密碼安全性較低。
基石
在多種實現方法中,最簡單的是——建立一個新使用者記錄(未啟用的),然後儲存跟啟用操作相關聯的 token 值。
然而,這裡我們將介紹另一種方法。通過新增額外的 invite 表——儲存使用者郵箱地址和用於啟用的 token 值來解決這一問題。啟用後,將使用該表中的資料來建立新使用者。
全新安裝 laravel5.4 後,先配置好資料庫和郵箱等設定。
遷移
框架安裝時自動新增了 user 表的遷移檔案,現在只要將其中的 name 和 password 欄位刪除便可,刪除完檔案內容如下所示。
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('email')->unique();
$table->rememberToken();
$table->timestamps();
});
}
在控制檯專案的根目錄下執行命令(接下來簡稱為執行命令):php artisan make:migration create_invites_table
,建立 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');
}
執行命令:php artisan migrate
。隨之,資料庫將完成遷移。
模型
我們需要建立 Eloquent 模型來管理團隊和使用者。但 laravel 預設含 user 表的 Eloquent 模型,所以這裡不需要再建立了。
執行命令:php artisan make:model Invite
,建立 invite 表的模型。
按照下述在 app
目錄下的 invite.php
中編輯白名單,從而實現批量建立和更新資料表資料。
protected $fillable = [
'email', 'token',
];
路由
本次教程中,將為接下來的情況定義三個路由。
- 顯示邀請新使用者的表單頁面
- 處理所提交的表單
- 接受邀請
在 app/routes/web.php
檔案中,新增如下路由:
Route::get('invite', 'InviteController@invite')->name('invite');
Route::post('invite', 'InviteController@process')->name('process');
// {token} 必須傳遞給控制器中方法的引數
Route::get('accept/{token}', 'InviteController@accept')->name('accept');
控制器
眼尖的讀者會發現,上述的路由中都是通過 InviteController
來處理所有請求。
執行命令:php artisan make:controller InviteController
,建立控制器。
在 app/Http/Controllers/InviteController.php
檔案中定義這些方法:
public function invite()
{
// 為使用者展示填寫邀請郵箱的表單
}
public function process()
{
// 處理使用者所提交的表單和傳送郵件到邀請郵箱
}
public function accept($token)
{
// 可通過 URl 中提供的 token 值來查詢使用者
}
這樣一來,萬事齊全。只要我們理清邏輯填寫上述方法便可實現使用者邀請系統了!
業務邏輯
接下來將逐一闡述每個方法的實現邏輯。
invite()
這段程式碼比較簡單,只需要給使用者一個可以填寫被邀請者郵箱的表單即可。
類似於:
public function invite()
{
return view('invite');
}
新建 resources/views/invite.blade.php
,檢視中的表單通過 POST 方法將填入的
email 傳送到路由 invite
上。
// 使用命名的路由,以防 URL 發生變化
// 表單不會中斷
<form action="{{ route('invite') }}" method="post">
{{ csrf_field() }}
<input type="email" name="email" />
<button type="submit">Send invite</button>
</form>
process()
注意,前方高能。這是整個流程的核心方法!
前提:使用 Laravel 的 mailables 來給新使用者傳送邀請通知。
首先,執行命令 php artisan make:mail InviteCreated
,建立 maliable 類。
編輯 app/Mail
目錄下 InviteCreated
檔案。修改該類中的構造方法,依賴注入 Invite 模型並且將其設定為 public。
use App\Invite;
public function __construct(Invite $invite)
{
$this->invite = $invite;
}
接著設定郵件是誰傳送的,郵件傳送的內容。
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
,我們需要生成當新使用者接受邀請後用來識別其身份的唯一隨機數——token。然後,將該 token 和使用者輸入的 email 值將存到 invite 表中。
use App\Invite;
use App\Mail\InviteCreated;
use Illuminate\Support\Facades\Mail;
...
public function process(Request $request)
{
// 驗證請求中的資料
do {
// 使用 laravel 的 str_random() 輔助方法來生成token隨機數
$token = str_random();
} //校驗 token 值若已經存在,則重新生成
while (Invite::where('token', $token)->first());
// 在 Invite 表中建立新紀錄
$invite = Invite::create([
'email' => $request->get('email'),
'token' => $token
]);
// 傳送郵件
Mail::to($request->get('email'))->send(new InviteCreated($invite));
// 返回上一級
return redirect()
->back();
}
因此,新使用者不僅接收到通知,資料還被儲存在 invite 表中。
accept()
最後,還差一個方法來處理新使用者接收邀請的邏輯。
通常情況下,你想要獲取密碼和其他可能會用到的使用者資料。但是,現在只要把流程走通便可,怎麼簡單怎麼來吧。邏輯為——基於驗證 token 值來決定是否建立新使用者。
記住,URL 中的 token 必須作為引數傳回。
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 表中的資料建立新使用者
User::create(['email' => $invite->email]);
// 刪除 invite 表中的該條記錄
$invite->delete();
// 這裡你可能會實現讓使用者登入,然後讓其進入首頁的功能。但是現在我們只要確保它能順利執行
return 'Good job! Invite accepted!';
}
執行
在瀏覽器中點選邀請網址 ( 例如 http://example.com/invite ),輸入邀請物件郵箱並提交表單。
開啟 invite 表將看到一條含唯一 token 值的新紀錄將被建立。開啟郵箱將會發現含資料庫中已存在的 token 值的啟用連結。
當點選完啟用連結後將會彈出“乾的漂亮!邀請被接收!”的標誌。再校驗一下,是否按預期流程處理。資料庫中 invite 表的相應記錄被刪除,相反地,user 表將建立一條新記錄。
擴充套件
你已經成功實現了使用者邀請系統。
雖然這是一個簡單的例子,但是它奠定了良好的基礎。
可基於該例子擴充套件當使用者接收邀請時捕獲使用者資訊以及拒絕和重新傳送邀請等功能。
另外,你也可修改程式碼從而支援多租戶或團隊/使用者關係——雖然這實現起來有點複雜。但是相信沒有什麼是不能通過你的智慧去實現的。