基于 Laravel 搭建用户邀请系统

在前段时间发布的 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 表将创建一条新记录。

扩展

你已经成功实现了用户邀请系统。

虽然这是一个简单的例子,但是它奠定了良好的基础。

可基于该例子扩展当用户接收邀请时捕获用户信息以及拒绝和重新发送邀请等功能。

另外,你也可修改代码从而支持多租户或团队/用户关系——虽然这实现起来有点复杂。但是相信没有什么是不能通过你的智慧去实现的。

参考:https://laravel-news.com/user-invitation-system

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容