whereRaw('lower(email) = ?', [strtolower($email)]) ->exists(); } /** * @throws UserIsAlreadyMemberOfOrganizationApiException|InvitationForTheEmailAlreadyExistsApiException */ public function inviteUser(Organization $organization, string $email, Role $role, User $inviter): OrganizationInvitation { // Normalize the email so it matches how user emails are stored (see UserService::createUser), // otherwise a mixed-case invite silently fails to link on registration. $email = strtolower($email); if (app(MemberService::class)->isEmailAlreadyMember($organization, $email)) { throw new UserIsAlreadyMemberOfOrganizationApiException; } if (OrganizationInvitation::query() ->where('email', $email) ->whereBelongsTo($organization, 'organization') ->exists()) { throw new InvitationForTheEmailAlreadyExistsApiException; } OrganizationInvitationAdding::dispatch($organization, $email, $role, $inviter); $invitation = new OrganizationInvitation; $invitation->email = $email; $invitation->role = $role->value; $invitation->organization()->associate($organization); $invitation->save(); Mail::to($email)->queue(new OrganizationInvitationMail($invitation)); return $invitation; } /** * @return Collection */ public function processAcceptedInvitations(User $user): Collection { $organizations = new Collection; $invitations = OrganizationInvitation::query() ->whereRaw('lower(email) = ?', [strtolower($user->email)]) ->whereNotNull('accepted_at') ->get(); foreach ($invitations as $invitation) { $organization = $invitation->organization; $role = Role::tryFrom($invitation->role); if ($role === null) { Log::error('Invalid role in invitation', [ 'invitation' => $invitation->getKey(), 'role' => $invitation->role, ]); continue; } app(MemberService::class)->addMember($user, $organization, $role); $invitation->delete(); $organizations->push($organization); } return $organizations; } }