= self::MAX_DEPTH) { Log::warning('TriggerEngine: max depth reached, aborting further evaluation', [ 'ticket_id' => $ticket->id, 'event' => $event, ]); return; } self::$depth++; try { $triggers = Trigger::query()->where('enabled', true)->where('event', $event)->orderBy('sort_order')->get(); foreach ($triggers as $trigger) { $current = $ticket->fresh(); if ($current && $this->matches($trigger, $current)) { $this->applyActions($trigger, $current); } } } finally { self::$depth--; } } protected function matches(Trigger $trigger, Ticket $ticket): bool { foreach ($trigger->conditions as $condition) { $field = $condition['field'] ?? null; if (! in_array($field, Trigger::CONDITION_FIELDS, true)) { return false; } if (! $this->conditionMatches($condition, $ticket->{$field})) { return false; } } return true; } protected function conditionMatches(array $condition, mixed $actual): bool { $value = $condition['value'] ?? null; return match ($condition['operator'] ?? null) { 'equals' => (string) $actual === (string) $value, 'not_equals' => (string) $actual !== (string) $value, 'is_empty' => $actual === null || $actual === '', 'is_not_empty' => $actual !== null && $actual !== '', 'contains' => is_string($actual) && $value !== null && str_contains(mb_strtolower($actual), mb_strtolower((string) $value)), default => false, }; } protected function applyActions(Trigger $trigger, Ticket $ticket): void { foreach ($trigger->actions as $action) { match ($action['type'] ?? null) { 'set_status' => $this->applySetStatus($ticket, $action['value'] ?? null), 'set_priority' => $this->applySetPriority($ticket, $action['value'] ?? null), 'set_team' => $this->applySetTeam($ticket, $action['value'] ?? null), 'set_assignee' => $this->applySetAssignee($ticket, $action['value'] ?? null), 'send_notification' => $this->applySendNotification($ticket, $action), default => null, }; } } protected function applySetStatus(Ticket $ticket, ?string $value): void { if (! $value || ! Status::query()->where('key', $value)->exists() || $ticket->status_key === $value) { return; } $this->tickets->setStatus($ticket, $value); } protected function applySetPriority(Ticket $ticket, ?string $value): void { if (! $value || ! Priority::query()->where('key', $value)->exists() || $ticket->priority_key === $value) { return; } $this->tickets->setPriority($ticket, $value); } protected function applySetTeam(Ticket $ticket, null|string|int $value): void { if ($value === null || (int) $ticket->team_id === (int) $value) { return; } $team = Team::query()->find($value); if ($team) { $this->tickets->setTeam($ticket, $team); } } protected function applySetAssignee(Ticket $ticket, null|string|int $value): void { if ($value === null || (int) $ticket->assignee_id === (int) $value) { return; } $assignee = User::query()->find($value); if ($assignee) { $this->tickets->setAssignee($ticket, $assignee); } } protected function applySendNotification(Ticket $ticket, array $action): void { $templateId = $action['email_template_id'] ?? null; if (! $templateId) { return; } $this->tickets->sendCustomNotification($ticket, $action['recipient'] ?? 'client', (int) $templateId); } }