alter N+1 detection process
This commit is contained in:
@@ -30,8 +30,8 @@ final class HelpdeskDashboardWidget extends Component
|
|||||||
'criticalCount' => $criticalCount,
|
'criticalCount' => $criticalCount,
|
||||||
'closedPercent' => $closedPercent,
|
'closedPercent' => $closedPercent,
|
||||||
'openPercent' => $openPercent,
|
'openPercent' => $openPercent,
|
||||||
'recentCommits' => GitCommit::query()->with(['repository', 'ticket'])->latest('committed_at')->limit(6)->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(['status', 'priority'])->latest('last_activity_at')->limit(5)->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(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ final class KanbanBoard extends Component
|
|||||||
'last_activity_at' => now(),
|
'last_activity_at' => now(),
|
||||||
])->save();
|
])->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(
|
$workflow->dispatch(
|
||||||
$freshTicket,
|
$freshTicket,
|
||||||
@@ -109,7 +109,7 @@ final class KanbanBoard extends Component
|
|||||||
|
|
||||||
$this->board_id = $board->id;
|
$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());
|
$workflow->dispatch($fresh, 'ticket.updated', Auth::user());
|
||||||
|
|
||||||
$message = 'Ticket ' . $fresh->reference . ' moved from ' . $oldBoardName . ' to ' . $board->name . '.';
|
$message = 'Ticket ' . $fresh->reference . ' moved from ' . $oldBoardName . ' to ' . $board->name . '.';
|
||||||
@@ -185,7 +185,7 @@ final class KanbanBoard extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Ticket::query()
|
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())
|
->visibleTo(Auth::user())
|
||||||
->where('board_id', $board->id)
|
->where('board_id', $board->id)
|
||||||
->latest('last_activity_at')
|
->latest('last_activity_at')
|
||||||
|
|||||||
@@ -248,8 +248,9 @@ final class TicketEdit extends Component
|
|||||||
private function freshTicket(Ticket $ticket): Ticket
|
private function freshTicket(Ticket $ticket): Ticket
|
||||||
{
|
{
|
||||||
return $ticket->fresh([
|
return $ticket->fresh([
|
||||||
'owner',
|
'user:id,name,email',
|
||||||
'agent',
|
'owner:id,name,email',
|
||||||
|
'agent:id,name,email',
|
||||||
'status',
|
'status',
|
||||||
'priority',
|
'priority',
|
||||||
'category',
|
'category',
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ final class TicketIndex extends Component
|
|||||||
$search = trim($this->search);
|
$search = trim($this->search);
|
||||||
|
|
||||||
return Ticket::query()
|
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')
|
->withCount('attachments')
|
||||||
->visibleTo($user)
|
->visibleTo($user)
|
||||||
->when($this->scope === 'open', static fn (Builder $query): Builder => $query->whereNull('completed_at'))
|
->when($this->scope === 'open', static fn (Builder $query): Builder => $query->whereNull('completed_at'))
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ final class TicketShow extends Component
|
|||||||
->when(! $this->canSeeInternalNotes(), static function ($query): void {
|
->when(! $this->canSeeInternalNotes(), static function ($query): void {
|
||||||
$query->where('is_internal', false);
|
$query->where('is_internal', false);
|
||||||
})
|
})
|
||||||
->with(['user.company', 'attachments'])
|
->with(['user.company', 'owner:id,name,email,company_id', 'agent:id,name,email', 'attachments'])
|
||||||
->oldest()
|
->oldest()
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ final class Ticket extends Model
|
|||||||
'sla_first_response_breached_at', 'sla_resolution_breached_at', 'public_replies_count', 'time_spent_minutes',
|
'sla_first_response_breached_at', 'sla_resolution_breached_at', 'public_replies_count', 'time_spent_minutes',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** @var list<string> */
|
||||||
|
protected $with = ['user'];
|
||||||
|
|
||||||
/** @var array<string, string> */
|
/** @var array<string, string> */
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'company_id' => 'integer',
|
'company_id' => 'integer',
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Modules\Helpdesk\Tests\Feature;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
final class TicketUserNPlusOneFilesTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_ticket_collection_queries_eager_load_user_owner_and_agent(): void
|
||||||
|
{
|
||||||
|
$index = file_get_contents(module_path('Helpdesk', 'Livewire/TicketIndex.php'));
|
||||||
|
$kanban = file_get_contents(module_path('Helpdesk', 'Livewire/KanbanBoard.php'));
|
||||||
|
$dashboard = file_get_contents(module_path('Helpdesk', 'Livewire/HelpdeskDashboardWidget.php'));
|
||||||
|
|
||||||
|
self::assertIsString($index);
|
||||||
|
self::assertIsString($kanban);
|
||||||
|
self::assertIsString($dashboard);
|
||||||
|
|
||||||
|
self::assertStringContainsString("'user:id,name,email', 'owner:id,name,email', 'agent:id,name,email'", $index);
|
||||||
|
self::assertStringContainsString("'user:id,name,email', 'owner:id,name,email', 'agent:id,name,email'", $kanban);
|
||||||
|
self::assertStringContainsString("'user:id,name,email', 'owner:id,name,email', 'agent:id,name,email'", $dashboard);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -638,3 +638,8 @@
|
|||||||
- Added explicit regression guard text for all selectable GitFlow prefixes used by the ticket developer actions branch command builder.
|
- Added explicit regression guard text for all selectable GitFlow prefixes used by the ticket developer actions branch command builder.
|
||||||
- Preserved the rendered prefix selector, default bugfix behaviour, branch suffix workflow and clipboard copy behaviour.
|
- Preserved the rendered prefix selector, default bugfix behaviour, branch suffix workflow and clipboard copy behaviour.
|
||||||
- Preserved append-only changelog history.
|
- Preserved append-only changelog history.
|
||||||
|
|
||||||
|
- version: 1.10.39
|
||||||
|
date: 2026-06-26
|
||||||
|
changes:
|
||||||
|
- Eager loaded direct ticket user, owner and agent relations on ticket list surfaces to remove Helpdesk ticket user N+1 warnings.
|
||||||
|
|||||||
@@ -23,5 +23,5 @@
|
|||||||
"Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider"
|
"Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider"
|
||||||
],
|
],
|
||||||
"files": [],
|
"files": [],
|
||||||
"version": "1.10.34"
|
"version": "1.10.39"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use BeyondCode\QueryDetector\Outputs\Debugbar;
|
||||||
|
use BeyondCode\QueryDetector\Outputs\Log;
|
||||||
|
|
||||||
|
return [
|
||||||
|
/*
|
||||||
|
* Enable or disable the query detection.
|
||||||
|
* If this is set to "null", the app.debug config value will be used.
|
||||||
|
*/
|
||||||
|
'enabled' => 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,
|
||||||
|
],
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user