Next, we need to send the invitation via email.
Let's create an Laravel Notification class for invitations.
php artisan make:notification SendInvitationNotification
The SendInvitationNotification
class will accept the Notification
Model as a parameter. Then, we will be able to add the tenant's name to the email.
app/Http/Notifications/:
use App\Models\Invitation; class SendInvitationNotification extends Notification{ use Queueable; public function __construct(private readonly Invitation $invitation) { // } public function via(object $notifiable): array { return ['mail']; } public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->line("You are invited to the team {$this->invitation->tenant->name}") ->action('Notification Action', url('/')) ->line('Thank you for using our application!'); } // ...}
To call $this->invitation->tenant
, we need to add one more thing: we haven't yet added the tenant relationship to the Invitation
Model.
app/Models/Invitation.php:
use Illuminate\Database\Eloquent\Relations\BelongsTo; class Invitation extends Model{ // ... public function tenant(): BelongsTo { return $this->belongsTo(Tenant::class); }}
Now, we can send the Notification.
app/Http/Controllers/UserController.php:
use Illuminate\Support\Facades\Notification;use App\Notifications\SendInvitationNotification; class UserController extends Controller{ // ... public function store(StoreUserRequest $request): RedirectResponse { $invitation = Invitation::create([ 'tenant_id' => auth()->user()->current_tenant_id, 'email' => $request->input('email'), 'token' => Str::random(32), ]); Notification::route('mail', $request->input('email'))->notify(new SendInvitationNotification($invitation)); return redirect()->route('users.index'); }}
We can send a notification. After providing some email and sending notification, you should see the email where the tenant name is correct.
Now, let's build the link to accept the invitation. We need a method in the Controller and Route.
app/Http/Controllers/UserController.php:
class UserController extends Controller{ // ... public function acceptInvitation(string $token) { }}
routes/web.php:
// ... Route::get('invitations/{token}', [\App\Http\Controllers\UserController::class, 'acceptInvitation'])->name('invitations.accept'); require __DIR__.'/auth.php';
In the Notification, we will generate a temporary Signed URL that expires after 30 minutes.
app/Http/Notifications/:
use Illuminate\Support\Facades\URL; class SendInvitationNotification extends Notification{ // ... public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->line("You are invited to the team {$this->invitation->tenant->name}") ->action('Accept Invitation', URL::temporarySignedRoute('invitations.accept', now()->addMinutes(30), ['token' => $this->invitation->token])) ->line('Thank you for using our application!'); } // ...}
In the Controller, what do we need to check? First, if the token is valid and has yet to be accepted.
app/Http/Controllers/UserController.php:
class UserController extends Controller{ // ... public function acceptInvitation(string $token) { $invitation = Invitation::where('token', $token) ->whereNull('accepted_at') ->firstOrFail(); }}
Next, we have two cases.
class UserController extends Controller{ // ... public function acceptInvitation(string $token) { $invitation = Invitation::where('token', $token) ->whereNull('accepted_at') ->firstOrFail(); if (auth()->check()) { // assign a user } else { // redirect to register } }}