Group::query()->orderBy('name')->get(['id', 'name']), // Resolved, not raw — see ClientsController::create()'s note on // the same prop: this is what will actually happen, and the // form's own field mirrors this resolution to draw its hint. 'default_storage_quota_mb' => $this->storageUsage->defaultQuotaMb(), ]); } public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', new AvailableEmailRule], 'name' => ['nullable', 'string', 'max:255'], 'group_id' => ['required', 'integer', Rule::in([0, ...Group::query()->pluck('id')->all()])], 'storage_quota_mb' => ['nullable', 'integer', 'min:0'], ]); $group = $validated['group_id'] > 0 ? Group::query()->whereKey($validated['group_id'])->first() : null; $invitation = Invitation::issue( email: $validated['email'], name: $validated['name'] ?? null, group: $group, invitedBy: $request->user(), expiresAt: now()->addHours((int) $this->settings->get(Setting::ClientInvitationExpiryHours)), // The `integer` rule above validates the shape but does not // cast it — this arrives as a numeric string from the request, // same as group_id, and issue() takes a real int. storageQuotaMb: (int) ($validated['storage_quota_mb'] ?? 0), ); Notification::route('mail', $invitation->email)->notify( new ClientInvitationNotification($invitation->name ?? $invitation->email, $invitation->token), ); $this->activity->log(Action::ClientInvited, context: ['email' => $invitation->email]); return redirect()->route('clients.index')->with('success', __('Invitation sent.')); } }