fixed N+1 issues
This commit is contained in:
@@ -55,11 +55,8 @@ final class TicketShow extends Component
|
||||
|
||||
public function mount(Ticket $ticket): void
|
||||
{
|
||||
$this->actor()->loadMissing('company');
|
||||
$ticket->loadMissing($this->ticketDetailRelations());
|
||||
|
||||
$this->authorize('view', $ticket);
|
||||
$this->ticket = $this->freshTicket($ticket);
|
||||
$this->authorize('view', $this->ticket);
|
||||
$this->status_id = $this->ticket->status_id;
|
||||
$this->priority_id = $this->ticket->priority_id;
|
||||
$this->agent_id = $this->ticket->agent_id;
|
||||
@@ -86,7 +83,7 @@ final class TicketShow extends Component
|
||||
'attachments.*' => 'file|max:'.config('helpdesk.max_upload_kb', 10240).'|mimes:'.implode(',', config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'log', 'zip'])),
|
||||
]);
|
||||
|
||||
$tickets->comment($this->ticket, $this->actor(), $this->commentBody, $this->internal, $this->attachments);
|
||||
$tickets->comment($this->ticket, Auth::user(), $this->commentBody, $this->internal, $this->attachments);
|
||||
|
||||
$this->reset(['commentBody', 'internal', 'attachments']);
|
||||
$this->ticket = $this->freshTicket($this->ticket);
|
||||
@@ -103,7 +100,7 @@ final class TicketShow extends Component
|
||||
'replyBody' => ['required', 'string', 'min:2'],
|
||||
]);
|
||||
|
||||
$tickets->comment($this->ticket, $this->actor(), $this->replyBody, $this->internal, [], $this->reply_parent_id);
|
||||
$tickets->comment($this->ticket, Auth::user(), $this->replyBody, $this->internal, [], $this->reply_parent_id);
|
||||
|
||||
$this->reset(['reply_parent_id', 'replyBody']);
|
||||
$this->ticket = $this->freshTicket($this->ticket);
|
||||
@@ -123,7 +120,7 @@ final class TicketShow extends Component
|
||||
->where('is_active', true)
|
||||
->findOrFail($this->canned_response_id);
|
||||
|
||||
$rendered = $renderer->render($response->body, $this->ticket, $this->actor());
|
||||
$rendered = $renderer->render($response->body, $this->ticket, Auth::user());
|
||||
$this->commentBody = trim($this->commentBody."\n\n".$rendered);
|
||||
$this->dispatch('helpdesk-canned-response-applied', body: $this->commentBody);
|
||||
}
|
||||
@@ -136,7 +133,7 @@ final class TicketShow extends Component
|
||||
->where('ticket_id', $this->ticket->id)
|
||||
->findOrFail($itemId);
|
||||
|
||||
$checklists->toggle($item, $this->actor());
|
||||
$checklists->toggle($item, Auth::user());
|
||||
$this->ticket = $this->freshTicket($this->ticket);
|
||||
}
|
||||
|
||||
@@ -177,7 +174,7 @@ final class TicketShow extends Component
|
||||
$sla->recordClosure($this->ticket);
|
||||
}
|
||||
|
||||
$workflow->dispatch($this->ticket, $status->is_closed ? 'ticket.closed' : 'ticket.updated', $this->actor());
|
||||
$workflow->dispatch($this->ticket, $status->is_closed ? 'ticket.closed' : 'ticket.updated', Auth::user());
|
||||
|
||||
session()->flash('ticket-action-updated', 'Status updated to '.$this->ticket->status?->name.'.');
|
||||
$this->dispatch('helpdesk-status-updated', statusId: $this->status_id);
|
||||
@@ -213,7 +210,7 @@ final class TicketShow extends Component
|
||||
$this->ticket = $this->freshTicket($this->ticket);
|
||||
$this->priority_id = (int) $this->ticket->priority_id;
|
||||
|
||||
$workflow->dispatch($this->ticket, 'ticket.updated', $this->actor());
|
||||
$workflow->dispatch($this->ticket, 'ticket.updated', Auth::user());
|
||||
|
||||
session()->flash('ticket-action-updated', 'Priority updated to '.$this->ticket->priority?->name.'.');
|
||||
$this->dispatch('helpdesk-priority-updated', priorityId: $this->priority_id);
|
||||
@@ -251,7 +248,7 @@ final class TicketShow extends Component
|
||||
$this->ticket = $this->freshTicket($this->ticket);
|
||||
$this->agent_id = $this->ticket->agent_id === null ? null : (int) $this->ticket->agent_id;
|
||||
|
||||
$workflow->dispatch($this->ticket, 'ticket.updated', $this->actor());
|
||||
$workflow->dispatch($this->ticket, 'ticket.updated', Auth::user());
|
||||
|
||||
session()->flash('ticket-action-updated', $this->agent_id === null
|
||||
? 'Ticket is now unassigned.'
|
||||
@@ -262,9 +259,6 @@ final class TicketShow extends Component
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$this->actor()->loadMissing('company');
|
||||
$this->ticket = $this->freshTicket($this->ticket);
|
||||
|
||||
return view('helpdesk::livewire.ticket-show', [
|
||||
'statuses' => Status::query()->orderBy('sort_order')->get(),
|
||||
'priorities' => Priority::query()->orderBy('sort_order')->get(),
|
||||
@@ -276,19 +270,6 @@ final class TicketShow extends Component
|
||||
/**
|
||||
* Convert Livewire select values into a nullable integer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Return the authenticated user with relations commonly used by policies and workflow templates.
|
||||
*/
|
||||
private function actor(): User
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
$user->loadMissing('company');
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function normaliseNullableInt(mixed $value): ?int
|
||||
{
|
||||
if ($value === null || $value === '' || $value === '0' || $value === 0) {
|
||||
@@ -298,17 +279,9 @@ final class TicketShow extends Component
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relations required by the ticket detail view and workflow renderers.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
private function ticketDetailRelations(): array
|
||||
private function freshTicket(Ticket $ticket): Ticket
|
||||
{
|
||||
return [
|
||||
'user.company',
|
||||
'owner.company',
|
||||
'agent.company',
|
||||
$ticket = $ticket->fresh([
|
||||
'status',
|
||||
'priority',
|
||||
'category',
|
||||
@@ -320,18 +293,15 @@ final class TicketShow extends Component
|
||||
'ratings',
|
||||
'tags',
|
||||
'checklistItems',
|
||||
'attachments.user.company',
|
||||
'attachments',
|
||||
'gitCommits.repository',
|
||||
];
|
||||
}
|
||||
]) ?? $ticket;
|
||||
|
||||
private function freshTicket(Ticket $ticket): Ticket
|
||||
{
|
||||
$ticket = $ticket->fresh($this->ticketDetailRelations()) ?? $ticket;
|
||||
$this->hydrateTicketUsers($ticket);
|
||||
|
||||
$comments = \Modules\Helpdesk\Models\Comment::query()
|
||||
->where('ticket_id', $ticket->id)
|
||||
->with(['user.company', 'attachments.user.company'])
|
||||
->with(['user.company', 'attachments'])
|
||||
->oldest()
|
||||
->get();
|
||||
|
||||
@@ -340,6 +310,39 @@ final class TicketShow extends Component
|
||||
return $ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load owner and agent users with one query and attach aliases used by the view/workflow.
|
||||
*/
|
||||
private function hydrateTicketUsers(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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return assignable users for this ticket.
|
||||
*
|
||||
@@ -356,7 +359,7 @@ final class TicketShow extends Component
|
||||
$columns[] = 'company_id';
|
||||
}
|
||||
|
||||
$query = User::query()->select($columns)->with('company')->orderBy('name');
|
||||
$query = User::query()->select($columns)->orderBy('name');
|
||||
|
||||
if ($this->ticket->company_id !== null && Schema::hasColumn('users', 'company_id')) {
|
||||
$tenantUsers = (clone $query)
|
||||
|
||||
Reference in New Issue
Block a user