fixed N+1 issues
This commit is contained in:
@@ -25,7 +25,7 @@ final class TicketControlsController extends Controller
|
||||
*/
|
||||
public function updateAgent(Request $request, Ticket $ticket, WorkflowNotificationService $workflow): RedirectResponse
|
||||
{
|
||||
$ticket->loadMissing($this->ticketActionRelations());
|
||||
$this->hydrateUserRelations($ticket);
|
||||
|
||||
Gate::authorize('update', $ticket);
|
||||
|
||||
@@ -51,7 +51,7 @@ final class TicketControlsController extends Controller
|
||||
'last_activity_at' => now(),
|
||||
]);
|
||||
|
||||
$fresh = $ticket->fresh(['user.company', 'owner.company', 'agent.company', 'status', 'priority', 'category', 'project', 'team', 'product']) ?? $ticket;
|
||||
$fresh = $this->freshTicketForAction($ticket);
|
||||
$workflow->dispatch($fresh, 'ticket.updated', $this->actor());
|
||||
|
||||
return redirect()
|
||||
@@ -66,7 +66,7 @@ final class TicketControlsController extends Controller
|
||||
*/
|
||||
public function updateStatus(Request $request, Ticket $ticket, WorkflowNotificationService $workflow, SlaService $sla): RedirectResponse
|
||||
{
|
||||
$ticket->loadMissing($this->ticketActionRelations());
|
||||
$this->hydrateUserRelations($ticket);
|
||||
|
||||
Gate::authorize('update', $ticket);
|
||||
|
||||
@@ -84,7 +84,7 @@ final class TicketControlsController extends Controller
|
||||
|
||||
$this->ensureBoardContainsStatus($ticket, (int) $status->id);
|
||||
|
||||
$fresh = $ticket->fresh(['user.company', 'owner.company', 'agent.company', 'status', 'priority', 'category', 'project', 'team', 'product']) ?? $ticket;
|
||||
$fresh = $this->freshTicketForAction($ticket);
|
||||
|
||||
if ($status->is_closed) {
|
||||
$sla->recordClosure($fresh);
|
||||
@@ -102,7 +102,7 @@ final class TicketControlsController extends Controller
|
||||
*/
|
||||
public function updatePriority(Request $request, Ticket $ticket, WorkflowNotificationService $workflow): RedirectResponse
|
||||
{
|
||||
$ticket->loadMissing($this->ticketActionRelations());
|
||||
$this->hydrateUserRelations($ticket);
|
||||
|
||||
Gate::authorize('update', $ticket);
|
||||
|
||||
@@ -115,7 +115,7 @@ final class TicketControlsController extends Controller
|
||||
'last_activity_at' => now(),
|
||||
]);
|
||||
|
||||
$fresh = $ticket->fresh(['user.company', 'owner.company', 'agent.company', 'status', 'priority', 'category', 'project', 'team', 'product']) ?? $ticket;
|
||||
$fresh = $this->freshTicketForAction($ticket);
|
||||
$workflow->dispatch($fresh, 'ticket.updated', $this->actor());
|
||||
|
||||
return redirect()
|
||||
@@ -123,25 +123,6 @@ final class TicketControlsController extends Controller
|
||||
->with('ticket-action-updated', 'Priority updated to '.($fresh->priority?->name ?? 'selected priority').'.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Relations needed by policies, redirect messages, and workflow templates.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
private function ticketActionRelations(): array
|
||||
{
|
||||
return [
|
||||
'owner.company',
|
||||
'agent.company',
|
||||
'status',
|
||||
'priority',
|
||||
'category',
|
||||
'project',
|
||||
'team',
|
||||
'product',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a direct guarded ticket update without relying on Livewire hydration.
|
||||
*
|
||||
@@ -158,6 +139,58 @@ final class TicketControlsController extends Controller
|
||||
->update($updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the ticket after a control update without issuing separate owner/agent relation queries.
|
||||
*/
|
||||
private function freshTicketForAction(Ticket $ticket): Ticket
|
||||
{
|
||||
$fresh = $ticket->fresh([
|
||||
'status',
|
||||
'priority',
|
||||
'category',
|
||||
'project',
|
||||
'team',
|
||||
'product',
|
||||
]) ?? $ticket;
|
||||
|
||||
$this->hydrateUserRelations($fresh);
|
||||
|
||||
return $fresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load requester and assigned agent in one user query and attach all aliases used downstream.
|
||||
*/
|
||||
private function hydrateUserRelations(Ticket $ticket): void
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the active Kanban board usable when a ticket is moved to a status not yet assigned to the board.
|
||||
*/
|
||||
@@ -173,9 +206,8 @@ final class TicketControlsController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the authenticated user with company eager-loaded for workflow rendering/N+1 safety.
|
||||
* Return the authenticated user with company loaded for workflow rendering.
|
||||
*/
|
||||
private function actor(): User
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user