支持Laravel.io的持续发展 →

你们还记不记起一加首款产品的发布?它名叫一加一,被宣称为 旗舰杀手。这个名字确实是名副其实:它的配置与当时的旗舰手机相同,但价格却低得多。我记得我花350美元买了一部!

但是,在一加推出首款手机时,他们面临了一些问题:作为一个新公司,他们没有像竞争对手那样有品牌知名度,而且他们的生产规模在他们推出手机时还没有跟上。因此,他们创建了一个邀请系统:他们会向等待名单上的首批人员发送邀请,并在手机生产并准备出货时激活邀请。当客户收到他们的手机时,他们也收到一张邀请函,可以给朋友或家庭成员。这使得一加一的用户群体变成了一个仅限邀请才能加入的特权俱乐部。他们创造了炒作,并在此期间分阶段释放,以应对生产爬坡。真棒!

如果我们想创建一个类似邀请系统的应用程序,比如一个社交网络,或者一个基于订阅的应用程序呢?今天,我们将探讨如何在Laravel应用程序中实现这种基于邀请的注册。我们将从用户等级、策略和枚举文章结束的地方开始,这里是github仓库

目标

我们希望能够阻止用户以默认方式注册,而是发送给他们一份邀请。邀请将会是一封包含链接的电子邮件,用户可以通过该链接进行注册,并填写额外信息。

我们将继续使用我们已有的用户级别:只有管理员可以发送邀请,管理员将为受邀用户选择用户级别。我们还将确保使用电子邮件收件箱中的链接注册的用户不需要验证电子邮件。

在这个过程中,我们将了解这些Laravel主题:

  • 中间件
  • 抛出异常
  • 通知
  • 用户反馈
  • 签名URL

设置它

让我们设置应用程序:从github仓库获取代码,运行它并运行迁移。注册一个账户,然后转到用户页面。注意到了什么?唯一的用户,就是我们刚才创建的那个,并不是管理员!我们希望所有用户都通过邀请注册,但谁将发送第一个邀请呢?

让我们让第一个用户可以以默认方式注册,并被分配admin用户级别。一旦第一个用户注册,我们应该在他们之后关闭这扇门。这个门当然是register路由。

确保第一个用户获得'admin'用户级别

让我们先解决第一个问题:使第一个用户始终是管理员。由于他们是唯一使用register路由的用户,我们可以这样认为:通过这种方式注册的用户可以是管理员。让我们在web.php中查找这条路由,看看它使用的是哪个控制器!你能找到吗?因为我肯定找不到。那是因为它在同一目录下的auth.php中,你可以在web.php的底部看到对它的导入

require __DIR__.'/auth.php';

auth.php中,我们可以看到register路由使用的是RegisteredUserController,并且POST请求将调用store方法。这合理,对吧?所以,我们来确保新注册的用户总是注册为管理员。

// app/Http/Controllers/Auth/RegisteredUserController.php

+ use App\Enums\UserLevel;

...

public function store(Request $request)
  {
    $request->validate([
      'name' => ['required', 'string', 'max:255'],
      'email' => ['required', 'string', 'email',
                  'max:255', 'unique:'.User::class],
      'password' => ['required', 'confirmed', Rules\Password::defaults()],
    ]);

    $user = User::create([
      'name' => $request->name,
      'email' => $request->email,
      'password' => Hash::make($request->password),
+     'level' => UserLevel::Administrator,
    ]);

    event(new Registered($user));

    Auth::login($user);

    return redirect(RouteServiceProvider::HOME);
  }

那么,让我们看看现在是否可行,好吗?使用php artisan migrate:fresh刷新你的数据库,或者如果你使用Sail,则使用sail artisan migrate:fresh。然后重新注册,现在你应该看到你是管理员了!

在第一个用户之后关闭门

第二个问题是,我们需要在已注册用户存在时禁用register路由。基于条件关闭应用程序的一部分正是中间件的标志,所以这就是我们将添加的!

使用php artisan make:middleware FirstUser来创建一个新的中间件。在里面,将handle方法改为这样

+ use App\Models\User;
  use Closure;
  use Illuminate\Http\Request;

class FirstUser
{
  /**
   * Handle an incoming request.
   *
   * @param  \Illuminate\Http\Request  $request
   * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
   * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
   */
  public function handle(Request $request, Closure $next)
  {
-   return $next($request)
+   if (User::all()->count() == 0) return $next($request);
+   else return redirect('/');
  }
}

我们将检查没有用户,如果有,我们将允许请求通过。如果有用户存在,我们将使用RouteServiceProvider::HOME将请求重定向到主页。现在我们可以将中间件添加到routes/auth.php中的register路由,为创建和存储方法

Route::middleware('guest')->group(function () {
  Route::get('register', [RegisteredUserController::class, 'create'])
-         ->name('register');
+         ->name('register')
+         ->middleware('firstUser');

- Route::post('register', [RegisteredUserController::class, 'store']);
+ Route::post('register', [RegisteredUserController::class, 'store'])
+         ->middleware('firstUser');

现在尝试一下,看看你是否可以注册另一个用户。如果你收到下面的错误,不要担心!

我们还需要让Laravel知道如何将middleware('firstUser')链接到FirstUser中间件类。这是在app/http/Kernel.php中完成的,更具体地是在$routemiddleware数组中。在那里添加新的中间件

protected $routeMiddleware = [
    ... Other middlewares here
    'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+   'firstUser' => \App\Http\Middleware\FirstUser::class
];

现在,如果我们再次尝试,我们会看到我们被重定向到了默认的'/'页面。

现在,我可以看到用户因为不知道为什么被重定向到其他页面而变得沮丧。这正是Laravel的HTTP异常发挥作用的地方。我们可以在应用程序中的任何地方使用abort()方法轻松抛出HTTP异常。如果我们查看HTTP消息列表,403似乎是最合适的。所以,让我们将中间件类的else子句改为这样

public function handle(Request $request, Closure $next)
  {
    if (User::all()->count() == 0) return $next($request);
-   else return redirect('/');
+   else abort(403, "There already is a registered user for this domain. Ask them for an invite to create your account.");
  }

方法 abort() 将使用状态码(在我们的例子中为 403)来显示视图 vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/403.blade.php。它还会显示我们传递给 abort() 方法第二个参数的消息,如下所示

This 至少解释了用户看到的内容。我同意这可以添加一些 ✨设计亮点✨,但我把这留给你。现在,让我们看看我们如何邀请用户二及以后的用户,怎么样?

创建邀请

为了将更多用户纳入其中,我们将创建一个新的视图,让管理员可以发送包含电子邮件字段和用户层级下拉菜单的邀请表单。我们将尽可能使一切 粗略设计,所以你可以这样理解:我们正在创建和存储一个 ‘userinvite’。我发现这里不需要自定义模型,但控制器和视图可以设置成好像有线。如果你还不理解,就去看看上面的 Adam 的视频,他会详细解释。

为此,创建一个新的目录 resources/views/userinvites,并在其中创建一个 create.blade.php 文件。代码如下所示

@php use App\Enums\UserLevel; @endphp
<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Invite Users') }}
        </h2>
    </x-slot>

    <div class="flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">

        <div class=" w-full sm:max-w-md mt-24 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg">
            <form method="POST" action="{{ url('/users/invite') }}">
                @csrf

                <!-- Email Address -->
                <div class="mt-4">
                    <x-input-label for="email" :value="__('Email')"/>

                    <x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus/>

                    <x-input-error :messages="$errors->get('email')" class="mt-2"/>
                </div>

                <!-- User Level -->
                <div class="mt-4">
                    <x-input-label for="level" :value="__('User Level')"/>

                    <select name="level" id="level" class="w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
                        @foreach(UserLevel::cases() as $levelOption)
                            <option value="{{$levelOption}}" @if ($levelOption == old('level')) selected="selected" @endif>
                                {{$levelOption->name}}
                            </option>
                        @endforeach
                    </select>

                    <x-input-error :messages="$errors->get('level')" class="mt-2"/>
                </div>

                <div class="flex items-center justify-end mt-6">
                    {{-- back button --}}
                    <a href="{{url()->previous()}}" class="inline-flex items-center px-4 py-2 bg-gray-300 border border-transparent rounded-md font-semibold text-xs text-gray-800 uppercase
                        tracking-widest hover:bg-gray-400 active:bg-gray-100 focus:outline-none focus:border-gray-100 focus:ring ring-gray-900 disabled:opacity-25 transition ease-in-out
                        duration-150">
                        Go Back
                    </a>

                    <x-primary-button class="ml-4">
                        {{ __('Invite') }}
                    </x-primary-button>
                </div>
            </form>
        </div>
    </div>
</x-app-layout>

现在,要显示这个视图,我们需要两样东西:一个控制器来显示它(并且稍后处理表单提交),以及一个在用户列表中的按钮来导航到该页面。让我们先处理控制器:使用 php artisan make:controller UserInviteController 创建控制器,并在其中添加两个方法:createstore。现在留空 store 方法,但是我们可以为 create 方法设置如下

+ use App\Models\User;

class UserInviteController extends Controller
{
+ public function create()
+ {
+   $this->authorize('invite', User::class);
+   return view('userinvites.create');
+ }

+ public function store()
+ {
+
+ }
}

如图所示,我添加了 authorize 方法来检查当前用户是否有权邀请新用户,这部分需要我们在 userPolicy.php 类中添加。在类中添加一个方法 invite,像这样

class UserPolicy
{
  ... other methods here

+  /**
+  * Determine whether the user can invite new users.
+  *
+  * @param  \App\Models\User  $loggedInUser the user that's trying to invite a new user
+  * @return \Illuminate\Auth\Access\Response|bool
+  */
+ public function invite(User $loggedInUser)
+ {
+   // only administrators are allowed to invite new users
+   return (UserLevel::Administrator == $loggedInUser->level);
+ }
}

现在,剩下的只是通过在 web.php 中添加两个路由来将控制器和这些方法链接起来,如下所示

Route::get('/users/invite', [UserInviteController::class, 'create'])
    ->middleware(['auth'])
    ->name('userinvites.create');

Route::post('/users/invite', [UserInviteController::class, 'store'])
    ->middleware(['auth'])
    ->name('userinvites.store');

然后在用户视图中添加一个按钮,如下

<div class="py-12">
  <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
+   <div class="flex items-center">
+     <h1 class="text-2xl font-extrabold flex-1">Users</h1>
+     @can('invite', App\Models\User::class)
+       <a href="{{route('userinvites.create')}}"
+          class="inline-flex items-center m-4 px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase
+       tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out
+       duration-150">
+         + Invite New User
+       </a>
+     @endcan
+   </div>

    <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
      <table class="w-full table-auto">
        <thead class="font-bold bg-gray-50 border-b-2">
        <tr>

我们正在利用与之前的博客文章中相同的 @can blade 指令。你可以在那里免费更新你的知识。😉

现在,返回到 UserInviteController.php。记得那里有一个空的 store 方法吗?这正是我们向管理员指定的电子邮件地址发送电子邮件的地方。但是,首先,小休息一下喝杯咖啡/茶!

Fly.io 是运行你的 Laravel 应用程序靠近用户的好方法。在 Fly 上全球部署只需几分钟! 部署你的 Laravel 应用程序!

通知新用户

在管理员邀请了用户后,会发生什么?用户应该收到一封带有关注链接的电子邮件,点击链接后,他们的电子邮件和用户层级应该已经填写(因为管理员已经为他们填写了)并且他们可以设置账户。常见的做法是设置 URL 的有效时间限制,以便用户在一定时间内无法使用。

通常,通知是作为对事件的响应而发送的,比如收到推特上的赞会发送出去 多巴胺击中 通知。但也可以在没有事件的情况下发送通知,如果没有这个必要。这里正是这种情况。

所以我们可以忽略事件,直接从控制器发送通知。创建一个通知,使用 php artisan make:notification UserInvited。你会看到默认情况下,通知已经设置为通过邮件发送,因为有返回值为 ['mail']via 方法和 toMail 方法。让我们快速更改 toMail 一下

public function toMail($notifiable)
  {
    $appName = env('APP_NAME');

    return (new MailMessage)
        ->subject('Personal Invitation')
        ->greeting('Hello!')
        ->line("You have been invited to join the {$appName} application!")
        ->action('Click here to register your account', url('/'))
        ->line('Note: this link expires after 24 hours.');
  }

这里我们只是在获取从 .env 变量中的 appName,然后创建一个带有关键操作的简单邮件。现在,我们需要发送那个邀请。还记得 UserInviteController 中的 store() 方法吗?我们在这里使用 FormRequest 进行验证,如果一切顺利,我们将在此处发送 UserInvited 通知。使用 php artisan make:request StoreUserInviteRequest 命令创建请求,并添加以下内容:

+ use App\Enums\UserLevel;
+ use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
+ use Illuminate\Validation\Rules\Enum;

class StoreUserInviteRequest extends FormRequest
{
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
-   return false;
+   return $this->user()->can('invite', User::class);
  }

  /**
   * Get the validation rules that apply to the request.
   *
   * @return array<string, mixed>
   */
  public function rules()
  {
    return [
+       'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
+       'level' => ['required', new Enum(UserLevel::class)]
    ];
  }
}

现在,将这个表单请求用于 UserInviteController

- public function store()
+ public function store(StoreUserInviteRequest $request)
  {
+   $validated = $request->validated();

+   // The enum validation will check if the chosen level can be cast to a UserLevel, but won't do the casting itself. So we do it here
+   $userLevel = UserLevel::from($validated['level']);
+   $email = $validated['email'];

+   // send mail to invite the new user with the given user level.
+   Notification::route('mail', $email)->notify(new UserInvited());

+   return redirect('/users');
  }

确保有一个电子邮件测试工具正在运行(Laravel Sail 自带 Mailhog),邀请一个新的用户。检查电子邮件测试工具,你应该会看到一个这样的电子邮件

让用户知道事情是否成功或失败的一个简单的闪存消息

// send mail to invite the new user with the given user level.
- Notification::route('mail', $email)->notify(new UserInvited());
- return redirect('/users');
+ try
+   {
+     Notification::route('mail', $email)->notify(new UserInvited());
+   }
+   catch( \Exception $e)
+   {
+     return redirect('/users')->with('error', "<b>Oh no!</b> Something went wrong. Please try again later.");
+   }
+   return redirect('/users')->with('success', "<b>Success!</b> An invite with level $userLevel->name has been sent to $email.");

并将这些消息构建到 users/index.blade.php 视图

<div class="py-12">
  <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">

+   {{-- MESSAGES --}}
+   @if(session('success'))
+     <div class="flex items-center bg-green-50 p-6 mb-6 w-full sm:rounded-lg sm:px-10 transition duration-700 ease-in-out"
+          x-data="{show: true}"
+          x-show="show"
+          x-init="setTimeout(() => show = false, 30000)"
+          x-transition>
+       <div class="flex mx-auto">
+         <svg class="h-6 w-6 flex-none fill-green-800 stroke-green-50 stroke-2" stroke-linecap="round" stroke-linejoin="round">
+             <circle cx="12" cy="12" r="11" />
+             <path d="m8 13 2.165 2.165a1 1 0 0 0 1.521-.126L16 9" fill="none" />
+         </svg>
+         <p class="ml-2 text-green-800">
+             {!! session('success') !!}
+         </p>
+       </div>
+     </div>
+   @endif
+
+   @if(session('error'))
+     <div class="flex items-center bg-red-100 p-6 mb-6 w-full sm:rounded-lg sm:px-10 transition duration-700 ease-in-out"
+          x-data="{show: true}"
+          x-show="show"
+          x-init="setTimeout(() => show = false, 30000)"
+          x-transition>
+       <div class="flex mx-auto">
+         <svg class="w-6 h-6 flex-none fill-red-800 stroke-red-100 stroke-2" fill="none" viewBox="0 0 24 24" stroke-width="1.5"  >
+             <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" />
+         </svg>
+         <p class="ml-2 text-red-800">
+             {!! session('error') !!}
+         </p>
+     </div>
+     </div>
+   @endif

    <div class="flex items-center">
      <h1 class="text-2xl font-extrabold flex-1">Users</h1>

它看起来像这样

太好了!现在,我们的用户已经了解发生了什么。最后一步是提供一个合适的 URL,因为现在我们发送的 URL 只链接到 '/'。为了确保用户不会篡改 URL,我们将使用一个 签名 URL。进入 UserInvited 通知并添加此方法

/**
 * Generates a unique signed URL that the mail receiver can user to register.
 * The URL contains the UserLevel and the receiver's email address, and will be valid for 1 day.
 *
 * @param $notifiable
 * @return string
 */
public function generateInvitationUrl(string $email)
{
  // ! Don't forget to import the URL Facade !
  return URL::temporarySignedRoute('users.create', now()->addDay(), [
    'level' => $this->userLevel,
    'email' => $email
  ]);
}

现在,你可能想知道 $userLevel$email 从何而来。我们将把 userLevel 添加到通知构造函数中,就像这样

/**
 * Create a new notification instance.
 *
 * @param UserLevel $userLevel
 * @param User $sender
 * @return void
 */
public function __construct(public UserLevel $userLevel, public User $sender)
{
}

别忘了导入 UserUserLevel 呢!

我还添加了发送邀请的用户以显示在电子邮件中。我们只需要在我们使用构造函数的地方反映这些更改,即在 UserInviteController

- Notification::route('mail', $email)->notify(new UserInvited());
+ Notification::route('mail', $email)->notify(new UserInvited($userLevel, $request->user()));

现在,让我们看看在签名 URL 中我们使用的 $email。这将是在邀请表单中管理员输入的电子邮件地址,也是发送此通知的电子邮件地址。还记得我们是如何在控制器中发送通知的吗?

Notification::route('mail', $email)->notify(new UserInvited());

'mail' 是我们使用的通道,而 $email 是路由。我们可以在通知的 toMail() 方法中像这样访问这个路由

$notifiable->routes['mail']

这将获取所有对 mail 通道的路由(就像目的地,在我们的情况下是电子邮件地址)。正是我们需要的东西!

所以,让我们生成我们的 URL,并随电子邮件一起传递。在 UserInvitedtoMail() 方法中做出这些更改

public function toMail($notifiable)
{
  $appName = env('APP_NAME');

+ $url = $this->generateInvitationUrl($notifiable->routes['mail']);

  return (new MailMessage)
              ->subject('Personal Invitation')
              ->greeting('Hello!')
-             ->line("You have been invited to join the {$appName} application!")
+             ->line("You have been invited by {$this->sender->name} to join the {$appName} application!")
-             ->action('Click here to register your account', url('/'))
+             ->action('Click here to register your account', url($url))
              ->line('Note: this link expires after 24 hours.');
}

就这样:一个带有签名 URL 的通知。它包含我们所需的所有信息,因为它已签名,所以防篡改,并且在 24 小时后过期。很好!

注册新用户

我们现在进入了最后阶段。最后一关是这样的:当新用户点击邀请电子邮件中的 URL 时,他们需要能够注册他们的账户。

我不知道你是否已经注意到了,但当我们尝试发送邀请时,你会看到一个错误而不是成功消息!你可以调试它,看看抛出了什么异常,或者我可以直接告诉你:你会得到一个 RouteNotFoundException,因为我们正在尝试创建签名 URL 的 users.create 路由还不存在。让我们在 web.php 中添加它

// show the 'create new user' screen
Route::get('/users/create', [UserController::class, 'create'])
    ->middleware('signed')
    ->name('users.create');

// create the new user
Route::post('/users/store',[UserController::class, 'store'])
    ->name('users.store');

你会注意到我们正在使用 'signed' 中间件。这将检查 URL 是否仍然有效(URL 中的信息未被篡改)以及 URL 是否已过期。感谢 Laravel 包括这个!我还包括了 store 路由,因为我们将稍后需要它。

我会加快一点节奏,因为我们做的是同样的事情:我将在 UserController 中添加 create() 方法,为它创建一个 formRequest,并为邀请用户显示注册表单创建一个视图。见你现在的一面!

// Usercontroller create method
public function create(CreateUserRequest $request)
{
    $validated = $request->validated();
    return view('users.create', ['email' => $validated['email'], 'level' => $validated['level']]);
}

创建用户请求: php artisan make:request CreateUserRequest

use App\Enums\UserLevel;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;

class CreateUserRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        // this should always be authorized, even without a logged-in user.
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'level' => ['required', new enum(UserLevel::class)]
        ];
    }
}

别忘了在 UserController 中导入 formRequest!让我们快速添加 users.create 视图,这个视图将主要基于 Breeze 的默认 register 视图

<x-guest-layout>
    <form method="POST" action="{{ route('users.store') }}">
        @csrf

        <!-- Name -->
        <div>
            <x-input-label for="name" :value="__('Name')" />

            <x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus />

            <x-input-error :messages="$errors->get('name')" class="mt-2" />
        </div>

        <!-- Email Address -->
        <input type="hidden" id="email" name="email" value="{{$email}}">

        <!-- User Level -->
        <input type="hidden" id="level" name="level" value="{{$level}}">

        <!-- Password -->
        <div class="mt-4">
            <x-input-label for="password" :value="__('Password')" />

            <x-text-input id="password" class="block mt-1 w-full"
                          type="password"
                          name="password"
                          required autocomplete="new-password" />

            <x-input-error :messages="$errors->get('password')" class="mt-2" />
        </div>

        <!-- Confirm Password -->
        <div class="mt-4">
            <x-input-label for="password_confirmation" :value="__('Confirm Password')" />

            <x-text-input id="password_confirmation" class="block mt-1 w-full"
                          type="password"
                          name="password_confirmation" required />

            <x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
        </div>

        <div class="flex items-center justify-end mt-4">
            <a class="underline text-sm text-gray-600 hover:text-gray-900" href="{{ route('login') }}">
                {{ __('Already registered?') }}
            </a>

            <x-primary-button class="ml-4">
                {{ __('Register') }}
            </x-primary-button>
        </div>
    </form>
</x-guest-layout>

哇,到处都是代码!现在你应该可以点击电子邮件中的链接,看到熟悉的屏幕

由于 store 方法尚未连接,注册功能目前无法使用。我们再次使用 formRequest 进行验证,因此运行 php artisan make:request StoreUserRequest 并添加以下内容

use App\Enums\UserLevel;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\Password;

class StoreUserRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        // this should always be authorized, even without a logged-in user.
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'confirmed', Password::defaults()],
            'level' => ['required', new enum(Userlevel::class)]
        ];
    }
}

来自已签名 URL 的 emaillevel 输入在 create 表单中是隐藏的。现在,我们可以填写 UserController 中的 store 方法

/**
 * Handle an incoming registration request. This will be called when an invited User accepts the invitation and registers.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\RedirectResponse
 *
 * @throws \Illuminate\Validation\ValidationException
 */
public function store(StoreUserRequest $request)
{
    $validated = $request->validated();

    $user = User::create($validated);

    event(new Registered($user));

    $user->markEmailAsVerified();

    Auth::login($user);
    //don't forget to import the RouteServiceProvider!
    return redirect(RouteServiceProvider::HOME);
}

我们将验证数据,如果验证成功,则创建一个用户。然后,我们通过派发事件、登录用户并将用户重定向到主页,遵循 Breeze 的默认逻辑。我增加的唯一一个功能是立即将用户的电子邮件标记为已验证,因为他们来自他们的电子邮件收件箱中的邀请。

让我们试试看!发送一个邀请,点击链接并注册新用户。如果一切顺利,你现在应该以新用户身份登录。你应该能够删除自己的帐户,如果你把新用户设置为管理员,你还可以发送邀请和编辑/删除其他用户。做得好!

最后更新 1 年前。

driesvints, krakowiak 赞同了这篇文章

2
喜欢这篇文章? 让作者知道并给予他们掌声!

你可能还喜欢以下文章

March 11th 2024

如何使用 Larastan 将你的 Laravel 应用从 0 到 9 进行更新

使用 Larastan 在 Laravel 应用执行之前找到错误是可能的,Larastan...

阅读文章
July 19th 2024

无需 traits 标准化 API 响应

我注意到大多数用于 API 响应的库都是使用 traits 实现的,并且...

阅读文章
July 17th 2024

在 Laravel 项目中通过 Discord 通知收集反馈

如何在 Laravel 项目中创建一个反馈模块,并在收到消息时收到 Discord 通知...

阅读文章

我们想对这些 令人惊叹的公司 表示感谢,因为他们支持我们

这里放您的标志吗?

Laravel.io

Laravel问题解决、知识分享和社区建设的门户。

© 2024 Laravel.io - 版权所有。