id) ->where('email', $email) ->pending() ->first(); if ($existing) { throw ValidationException::withMessages([ 'email' => ['An invitation is already pending for this email.'], ]); } // Check if user is already a member $existingUser = User::where('email', $email)->first(); if ($existingUser && $existingUser->hasCompany($company->id)) { throw ValidationException::withMessages([ 'email' => ['This user is already a member of this company.'], ]); } $invitation = CompanyInvitation::create([ 'company_id' => $company->id, 'user_id' => $existingUser?->id, 'email' => $email, 'role_id' => $roleId, 'token' => Str::random(64), 'status' => CompanyInvitation::STATUS_PENDING, 'invited_by' => $invitedBy->id, 'expires_at' => Carbon::now()->addDays(7), ]); Mail::to($email)->send(new CompanyInvitationMail($invitation)); return $invitation; } /** * Accept a pending invitation and add the user to the company. */ public function accept(CompanyInvitation $invitation, User $user): void { if (! $invitation->isPending()) { throw ValidationException::withMessages([ 'invitation' => ['This invitation is no longer valid.'], ]); } // Add user to company $user->companies()->attach($invitation->company_id); // Assign role scoped to the invitation's company $role = Role::withoutGlobalScopes()->find($invitation->role_id); BouncerFacade::scope()->to($invitation->company_id); $user->assign($role->name); // Update invitation $invitation->update([ 'status' => CompanyInvitation::STATUS_ACCEPTED, 'user_id' => $user->id, ]); } /** * Decline a pending invitation. */ public function decline(CompanyInvitation $invitation, User $user): void { if (! $invitation->isPending()) { throw ValidationException::withMessages([ 'invitation' => ['This invitation is no longer valid.'], ]); } $invitation->update([ 'status' => CompanyInvitation::STATUS_DECLINED, 'user_id' => $user->id, ]); } /** * Get all pending invitations for a user (by user_id or email). */ public function getPendingForUser(User $user): Collection { return CompanyInvitation::forUser($user) ->pending() ->with(['company', 'role', 'invitedBy']) ->get(); } }