From 788bb87b01f5d96bd12d3ef432a571ef347b2984 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Fri, 26 Jun 2026 11:13:23 +0100 Subject: [PATCH] alter N+1 detection process --- .../Livewire/HelpdeskDashboardWidget.php | 4 +- Modules/Helpdesk/Livewire/KanbanBoard.php | 6 +- Modules/Helpdesk/Livewire/TicketEdit.php | 5 +- Modules/Helpdesk/Livewire/TicketIndex.php | 2 +- Modules/Helpdesk/Livewire/TicketShow.php | 2 +- Modules/Helpdesk/Models/Ticket.php | 3 + .../Feature/TicketUserNPlusOneFilesTest.php | 25 +++++++ Modules/Helpdesk/changelog.yaml | 5 ++ Modules/Helpdesk/module.json | 2 +- config/querydetector.php | 73 +++++++++++++++++++ 10 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 Modules/Helpdesk/Tests/Feature/TicketUserNPlusOneFilesTest.php create mode 100644 config/querydetector.php diff --git a/Modules/Helpdesk/Livewire/HelpdeskDashboardWidget.php b/Modules/Helpdesk/Livewire/HelpdeskDashboardWidget.php index 705b0249..4f19292f 100644 --- a/Modules/Helpdesk/Livewire/HelpdeskDashboardWidget.php +++ b/Modules/Helpdesk/Livewire/HelpdeskDashboardWidget.php @@ -30,8 +30,8 @@ final class HelpdeskDashboardWidget extends Component 'criticalCount' => $criticalCount, 'closedPercent' => $closedPercent, 'openPercent' => $openPercent, - 'recentCommits' => GitCommit::query()->with(['repository', 'ticket'])->latest('committed_at')->limit(6)->get(), - 'recentTickets' => (clone $base)->with(['status', 'priority'])->latest('last_activity_at')->limit(5)->get(), + 'recentCommits' => GitCommit::query()->with(['repository', 'ticket.user:id,name,email', 'ticket.owner:id,name,email', 'ticket.agent:id,name,email'])->latest('committed_at')->limit(6)->get(), + 'recentTickets' => (clone $base)->with(['user:id,name,email', 'owner:id,name,email', 'agent:id,name,email', 'status', 'priority'])->latest('last_activity_at')->limit(5)->get(), ]); } } diff --git a/Modules/Helpdesk/Livewire/KanbanBoard.php b/Modules/Helpdesk/Livewire/KanbanBoard.php index 1b5e9ec5..0718649e 100644 --- a/Modules/Helpdesk/Livewire/KanbanBoard.php +++ b/Modules/Helpdesk/Livewire/KanbanBoard.php @@ -61,7 +61,7 @@ final class KanbanBoard extends Component 'last_activity_at' => now(), ])->save(); - $freshTicket = $ticket->fresh(['status', 'priority', 'category', 'project', 'team', 'product', 'tags', 'checklistItems', 'board']) ?? $ticket; + $freshTicket = $ticket->fresh(['user:id,name,email', 'owner:id,name,email', 'agent:id,name,email', 'status', 'priority', 'category', 'project', 'team', 'product', 'tags', 'checklistItems', 'board']) ?? $ticket; $workflow->dispatch( $freshTicket, @@ -109,7 +109,7 @@ final class KanbanBoard extends Component $this->board_id = $board->id; - $fresh = $ticket->fresh(['status', 'priority', 'category', 'project', 'team', 'board']) ?? $ticket; + $fresh = $ticket->fresh(['user:id,name,email', 'owner:id,name,email', 'agent:id,name,email', 'status', 'priority', 'category', 'project', 'team', 'board']) ?? $ticket; $workflow->dispatch($fresh, 'ticket.updated', Auth::user()); $message = 'Ticket ' . $fresh->reference . ' moved from ' . $oldBoardName . ' to ' . $board->name . '.'; @@ -185,7 +185,7 @@ final class KanbanBoard extends Component } return Ticket::query() - ->with(['status', 'priority', 'category', 'project', 'team', 'product', 'tags', 'checklistItems', 'board']) + ->with(['user:id,name,email', 'owner:id,name,email', 'agent:id,name,email', 'status', 'priority', 'category', 'project', 'team', 'product', 'tags', 'checklistItems', 'board']) ->visibleTo(Auth::user()) ->where('board_id', $board->id) ->latest('last_activity_at') diff --git a/Modules/Helpdesk/Livewire/TicketEdit.php b/Modules/Helpdesk/Livewire/TicketEdit.php index 8b0f7c58..f7aeeb08 100644 --- a/Modules/Helpdesk/Livewire/TicketEdit.php +++ b/Modules/Helpdesk/Livewire/TicketEdit.php @@ -248,8 +248,9 @@ final class TicketEdit extends Component private function freshTicket(Ticket $ticket): Ticket { return $ticket->fresh([ - 'owner', - 'agent', + 'user:id,name,email', + 'owner:id,name,email', + 'agent:id,name,email', 'status', 'priority', 'category', diff --git a/Modules/Helpdesk/Livewire/TicketIndex.php b/Modules/Helpdesk/Livewire/TicketIndex.php index 06c25186..6d22aa34 100644 --- a/Modules/Helpdesk/Livewire/TicketIndex.php +++ b/Modules/Helpdesk/Livewire/TicketIndex.php @@ -124,7 +124,7 @@ final class TicketIndex extends Component $search = trim($this->search); return Ticket::query() - ->with(['owner', 'agent', 'status', 'priority', 'category', 'project', 'team']) + ->with(['user:id,name,email', 'owner:id,name,email', 'agent:id,name,email', 'status', 'priority', 'category', 'project', 'team']) ->withCount('attachments') ->visibleTo($user) ->when($this->scope === 'open', static fn (Builder $query): Builder => $query->whereNull('completed_at')) diff --git a/Modules/Helpdesk/Livewire/TicketShow.php b/Modules/Helpdesk/Livewire/TicketShow.php index 5ff28600..7a353821 100644 --- a/Modules/Helpdesk/Livewire/TicketShow.php +++ b/Modules/Helpdesk/Livewire/TicketShow.php @@ -365,7 +365,7 @@ final class TicketShow extends Component ->when(! $this->canSeeInternalNotes(), static function ($query): void { $query->where('is_internal', false); }) - ->with(['user.company', 'attachments']) + ->with(['user.company', 'owner:id,name,email,company_id', 'agent:id,name,email', 'attachments']) ->oldest() ->get(); diff --git a/Modules/Helpdesk/Models/Ticket.php b/Modules/Helpdesk/Models/Ticket.php index 7d9a502c..20bd7fee 100644 --- a/Modules/Helpdesk/Models/Ticket.php +++ b/Modules/Helpdesk/Models/Ticket.php @@ -36,6 +36,9 @@ final class Ticket extends Model 'sla_first_response_breached_at', 'sla_resolution_breached_at', 'public_replies_count', 'time_spent_minutes', ]; + /** @var list */ + protected $with = ['user']; + /** @var array */ protected $casts = [ 'company_id' => 'integer', diff --git a/Modules/Helpdesk/Tests/Feature/TicketUserNPlusOneFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketUserNPlusOneFilesTest.php new file mode 100644 index 00000000..15d20d96 --- /dev/null +++ b/Modules/Helpdesk/Tests/Feature/TicketUserNPlusOneFilesTest.php @@ -0,0 +1,25 @@ + env('QUERY_DETECTOR_ENABLED', null), + + /* + * Threshold level for the N+1 query detection. If a relation query will be + * executed more than this amount, the detector will notify you about it. + */ + 'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 1), + + /* + * Here you can whitelist model relations. + * + * Right now, you need to define the model relation both as the class name and the attribute name on the model. + * So if an "Author" model would have a "posts" relation that points to a "Post" class, you need to add both + * the "posts" attribute and the "Post::class", since the relation can get resolved in multiple ways. + */ + 'except' => [ + // Author::class => [ + // Post::class, + // 'posts', + // ] + ], + + /* + * Here you can set a specific log channel to write to + * in case you are trying to isolate queries or have a lot + * going on in the laravel.log. Defaults to laravel.log though. + */ + 'log_channel' => env('QUERY_DETECTOR_LOG_CHANNEL', 'daily'), + + /* + * Define the output format that you want to use. Multiple classes are supported. + * Available options are: + * + * Alert: + * Displays an alert on the website + * \BeyondCode\QueryDetector\Outputs\Alert::class + * + * Console: + * Writes the N+1 queries into your browsers console log + * \BeyondCode\QueryDetector\Outputs\Console::class + * + * Clockwork: (make sure you have the itsgoingd/clockwork package installed) + * Writes the N+1 queries warnings to Clockwork log + * \BeyondCode\QueryDetector\Outputs\Clockwork::class + * + * Debugbar: (make sure you have the barryvdh/laravel-debugbar package installed) + * Writes the N+1 queries into a custom messages collector of Debugbar + * \BeyondCode\QueryDetector\Outputs\Debugbar::class + * + * JSON: + * Writes the N+1 queries into the response body of your JSON responses + * \BeyondCode\QueryDetector\Outputs\Json::class + * + * Log: + * Writes the N+1 queries into the Laravel.log file + * \BeyondCode\QueryDetector\Outputs\Log::class + */ + 'output' => [ + // \BeyondCode\QueryDetector\Outputs\Alert::class, + Log::class, + Debugbar::class, + ], +];