hydrateTicketUsers($ticket); $ticket->loadMissing(['status', 'priority', 'category', 'project', 'team', 'product']); $actor?->loadMissing('company'); WorkflowTemplate::query() ->where('event', $event) ->where('is_active', true) ->get() ->each(function (WorkflowTemplate $template) use ($ticket, $event, $actor): void { $recipient = $this->recipient($template->to_role, $ticket, $actor); if ($recipient === null || blank($recipient->email ?? null)) { $this->log($template, $ticket, $event, null, 'skipped', 'No recipient email was available.'); return; } $trackingToken = Str::random(48); $subject = $this->renderText($template->subject, $ticket, $actor); $html = $this->wrapHtml($this->renderText($template->html_body, $ticket, $actor), $ticket, $trackingToken); $text = $this->renderText($template->text_body ?: strip_tags($template->html_body), $ticket, $actor); try { Mail::html($html, function ($message) use ($recipient, $template, $subject): void { $message->to($recipient->email, $recipient->name ?? null)->subject($subject); if (filled($template->cc)) { $message->cc(array_map('trim', explode(',', (string) $template->cc))); } if (filled($template->bcc)) { $message->bcc(array_map('trim', explode(',', (string) $template->bcc))); } }); $this->log($template, $ticket, $event, (string) $recipient->email, 'sent', null, $subject, $trackingToken); } catch (Throwable $exception) { Log::warning('Helpdesk workflow email failed.', ['event' => $event, 'ticket_id' => $ticket->id, 'error' => $exception->getMessage()]); $this->log($template, $ticket, $event, (string) $recipient->email, 'failed', $exception->getMessage(), $subject); } }); } /** * Load requester/agent with one query if the caller has not already attached them. */ private function hydrateTicketUsers(Ticket $ticket): void { if ($ticket->relationLoaded('owner') && $ticket->relationLoaded('agent')) { return; } $userIds = collect([$ticket->user_id, $ticket->agent_id]) ->filter() ->map(static fn (mixed $id): int => (int) $id) ->unique() ->values(); if ($userIds->isEmpty()) { $ticket->setRelation('user', null); $ticket->setRelation('owner', null); $ticket->setRelation('agent', null); return; } $users = User::query() ->with('company') ->whereIn('id', $userIds) ->get() ->keyBy('id'); $owner = $users->get((int) $ticket->user_id); $agent = $ticket->agent_id === null ? null : $users->get((int) $ticket->agent_id); $ticket->setRelation('user', $owner); $ticket->setRelation('owner', $owner); $ticket->setRelation('agent', $agent); } private function recipient(string $role, Ticket $ticket, ?User $actor): ?User { return match ($role) { 'agent' => $ticket->agent, 'actor' => $actor, default => $ticket->owner, }; } private function renderText(string $template, Ticket $ticket, ?User $actor): string { $tokens = [ '{{ ticket.reference }}' => $ticket->reference, '{{ ticket.subject }}' => $ticket->subject, '{{ ticket.status }}' => (string) ($ticket->status?->name ?? ''), '{{ ticket.priority }}' => (string) ($ticket->priority?->name ?? ''), '{{ ticket.category }}' => (string) ($ticket->category?->name ?? ''), '{{ ticket.project }}' => (string) ($ticket->project?->title ?? ''), '{{ ticket.team }}' => (string) ($ticket->team?->name ?? ''), '{{ requester.name }}' => (string) ($ticket->owner?->name ?? ''), '{{ agent.name }}' => (string) ($ticket->agent?->name ?? ''), '{{ actor.name }}' => (string) ($actor?->name ?? ''), '{{ ticket.url }}' => route('helpdesk.tickets.show', ['ticket' => $ticket->getKey()]), ]; return strtr($template, $tokens); } private function wrapHtml(string $body, Ticket $ticket, ?string $trackingToken = null): string { return '
' .'
' .'
' .'
Helpdesk notification
' .'

'.e($ticket->reference).'

' .'

'.e($ticket->subject).'

' .'
' .$body .'

Open ticket

' .$this->trackingPixel($trackingToken) .'
'; } private function trackingPixel(?string $trackingToken): string { if ($trackingToken === null) { return ''; } $url = Route::has('helpdesk.workflow.open') ? route('helpdesk.workflow.open', ['token' => $trackingToken]) : URL::to('/'.trim((string) config('helpdesk.route_prefix', 'helpdesk'), '/').'/workflow/open/'.$trackingToken); return ''; } private function log(WorkflowTemplate $template, Ticket $ticket, string $event, ?string $recipient, string $status, ?string $error = null, ?string $subject = null, ?string $trackingToken = null): void { WorkflowLog::query()->create([ 'template_id' => $template->id, 'ticket_id' => $ticket->id, 'event' => $event, 'recipient' => $recipient, 'subject' => $subject, 'status' => $status, 'tracking_token' => $trackingToken, 'error' => $error, ]); } }