Back to Course |
Laravel 11 Multi-Tenancy: All You Need To Know

Sending Invitation Email and Accept Route

Next, we need to send the invitation via email.


Laravel Notification Class

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);
}
}

Sending the Notification Email

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.


Accepting the Invitation

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.

  1. If the user is already logged in to another tenant, we must assign that user to the current tenant and redirect them to the correct domain.
  2. Otherwise, we need to redirect them to the registration form, where they will fill in details and will be assigned to the current tenant.
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
}
}
}