projectPermissionService->userHasPermission($request->user(), $project, 'edit'), 403, ); $linkedTicket = Ticket::query() ->where('project_id', $project->id) ->findOrFail($request->linkedTicketId()); [$sourceTicketId, $targetTicketId] = $this->normalizeTicketPair($ticket->id, $linkedTicket->id); $ticketLink = DB::transaction(function () use ($linkedTicket, $request, $sourceTicketId, $targetTicketId, $ticket): TicketLink { $ticketLink = TicketLink::query()->updateOrCreate( [ 'source_ticket_id' => $sourceTicketId, 'target_ticket_id' => $targetTicketId, ], [ 'ticket_link_type_id' => $request->ticketLinkTypeId(), ], ); $ticketLink->load('ticketLinkType'); $this->createTicketWorklogEntry->handle( $ticket, $request->user(), 'link_updated', "Linked {$linkedTicket->ticket_key} as {$ticketLink->ticketLinkType->label}.", [ 'linked_ticket_key' => $linkedTicket->ticket_key, 'ticket_link_type' => $ticketLink->ticketLinkType->label, ], ); $this->createTicketWorklogEntry->handle( $linkedTicket, $request->user(), 'link_updated', "Linked {$ticket->ticket_key} as {$ticketLink->ticketLinkType->label}.", [ 'linked_ticket_key' => $ticket->ticket_key, 'ticket_link_type' => $ticketLink->ticketLinkType->label, ], ); return $ticketLink; }); return to_route('projects.tickets.show', [$project, $ticket]) ->with('status', "{$ticketLink->ticketLinkType->label} link was saved."); } public function destroy( Request $request, Project $project, Ticket $ticket, TicketLink $ticketLink, ): RedirectResponse { abort_unless( $this->projectPermissionService->userHasPermission($request->user(), $project, 'edit'), 403, ); $ticketLink->load(['sourceTicket', 'targetTicket', 'ticketLinkType']); abort_unless( $ticketLink->source_ticket_id === $ticket->id || $ticketLink->target_ticket_id === $ticket->id, 404, ); $linkedTicket = $ticketLink->source_ticket_id === $ticket->id ? $ticketLink->targetTicket : $ticketLink->sourceTicket; $label = $ticketLink->ticketLinkType->label; DB::transaction(function () use ($label, $linkedTicket, $request, $ticket, $ticketLink): void { $ticketLink->delete(); $this->createTicketWorklogEntry->handle( $ticket, $request->user(), 'link_removed', "Removed {$label} link to {$linkedTicket->ticket_key}.", [ 'linked_ticket_key' => $linkedTicket->ticket_key, 'ticket_link_type' => $label, ], ); $this->createTicketWorklogEntry->handle( $linkedTicket, $request->user(), 'link_removed', "Removed {$label} link to {$ticket->ticket_key}.", [ 'linked_ticket_key' => $ticket->ticket_key, 'ticket_link_type' => $label, ], ); }); return to_route('projects.tickets.show', [$project, $ticket]) ->with('status', "{$label} link was removed."); } /** * @return array{0: int, 1: int} */ private function normalizeTicketPair(int $firstTicketId, int $secondTicketId): array { return [ min($firstTicketId, $secondTicketId), max($firstTicketId, $secondTicketId), ]; } }