where('slug', TicketStatusEnum::Open->value) ->firstOrFail(); /** @var Ticket $ticket */ $ticket = Ticket::query()->create([ 'user_id' => $request->userId, 'connected_app_id' => $request->connectedAppId, 'issue_category' => $request->issueCategory, 'description' => $request->description, 'status_id' => $defaultStatus->id, ]); if (! empty($request->notifiedRoleIds)) { $ticket->notifiedRoles()->sync($request->notifiedRoleIds); } if (! empty($request->notifiedUserIds)) { $ticket->notifiedUsers()->sync($request->notifiedUserIds); } if ($request->attachment instanceof UploadedFile) { $this->attachmentService->store($ticket->id, $request->attachment); } // Load relations to build complete DTO and trigger notifications $ticket->load(['user', 'connectedApp', 'status', 'notifiedRoles', 'notifiedUsers', 'attachments']); $this->sendNotifications($ticket); return TicketData::fromModel($ticket); }); } /** * Get all tickets raised by a specific user. * * @return Collection */ public function getAllForUser(int $userId): Collection { if ($userId <= 0) { throw new InvalidArgumentException('Invalid user ID'); } $tickets = Ticket::with(['user', 'connectedApp', 'status', 'notifiedRoles', 'notifiedUsers', 'attachments']) ->where('user_id', $userId) ->latest() ->get(); return TicketData::collect($tickets); } /** * Get all tickets in the system (for admins/support). * * @return Collection */ public function getAll(): Collection { $tickets = Ticket::with(['user', 'connectedApp', 'status', 'notifiedRoles', 'notifiedUsers', 'attachments']) ->latest() ->get(); return TicketData::collect($tickets); } /** * Get a specific ticket. */ public function getTicket(int $id): TicketData { if ($id <= 0) { throw new InvalidArgumentException('Invalid ticket ID'); } $ticket = Ticket::with(['user', 'connectedApp', 'status', 'notifiedRoles', 'notifiedUsers', 'attachments']) ->findOrFail($id); return TicketData::fromModel($ticket); } /** * Update the status of a ticket. * * @throws Throwable */ public function updateStatus(int $ticketId, string $statusSlug): void { if ($ticketId <= 0) { throw new InvalidArgumentException('Invalid ticket ID'); } DB::transaction(function () use ($ticketId, $statusSlug): void { $ticket = Ticket::findOrFail($ticketId); $status = TicketStatus::where('slug', $statusSlug)->firstOrFail(); $ticket->update(['status_id' => $status->id]); }); } /** * Send notifications to all routed recipients. */ private function sendNotifications(Ticket $ticket): void { $notification = new TicketRaisedNotification($ticket); $notifiedUserIds = []; // Notify specific users foreach ($ticket->notifiedUsers as $user) { $user->notify($notification); $notifiedUserIds[] = $user->id; } // Notify role users foreach ($ticket->notifiedRoles as $role) { $users = User::role($role->name)->get(); foreach ($users as $user) { // Avoid double notifications if (! in_array($user->id, $notifiedUserIds, true)) { $user->notify($notification); $notifiedUserIds[] = $user->id; } } } } }