started to add indexes and fixed N+1 issue

This commit is contained in:
Mark Gregory
2026-06-12 14:28:25 +01:00
parent e79a029bed
commit 49340d42af
9 changed files with 469 additions and 255 deletions
@@ -1,56 +1,131 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
declare(strict_types=1);
class HelpdeskPerformanceIndexes extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/** @return void */
public function up(): void
{
Schema::table('helpdesk_tickets', function (Blueprint $table): void {
$table->index(['company_id', 'status_id']);
$table->index(['company_id', 'priority_id']);
$table->index(['company_id', 'category_id']);
$table->index(['company_id', 'project_id']);
$table->index(['company_id', 'team_id']);
$table->index(['agent_id', 'completed_at']);
$table->index(['user_id', 'created_at']);
$table->index(['status_id', 'created_at']);
$table->index(['last_activity_at']);
$table->index(['due_at']);
$table->index(['reference']);
});
$this->addIndex('helpdesk_tickets', ['company_id', 'status_id'], 'hdesk_tickets_company_status_idx');
$this->addIndex('helpdesk_tickets', ['company_id', 'priority_id'], 'hdesk_tickets_company_priority_idx');
$this->addIndex('helpdesk_tickets', ['company_id', 'category_id'], 'hdesk_tickets_company_category_idx');
$this->addIndex('helpdesk_tickets', ['company_id', 'team_id'], 'hdesk_tickets_company_team_idx');
$this->addIndex('helpdesk_tickets', ['status_id', 'created_at'], 'hdesk_tickets_status_created_idx');
$this->addIndex('helpdesk_tickets', ['due_at'], 'hdesk_tickets_due_at_idx');
$this->addIndex('helpdesk_tickets', ['archived_at'], 'hdesk_tickets_archived_at_idx');
Schema::table('helpdesk_comments', function (Blueprint $table): void {
$table->index(['ticket_id', 'parent_id']);
$table->index(['ticket_id', 'created_at']);
$table->index(['parent_id', 'created_at']);
$table->index(['user_id', 'created_at']);
});
$this->addIndex('helpdesk_comments', ['parent_id', 'created_at'], 'hdesk_comments_parent_created_idx');
$this->addIndex('helpdesk_comments', ['user_id', 'created_at'], 'hdesk_comments_user_created_idx');
Schema::table('helpdesk_attachments', function (Blueprint $table): void {
$table->index(['ticket_id']);
$table->index(['comment_id']);
$table->index(['user_id']);
});
$this->addIndex('helpdesk_attachments', ['ticket_id'], 'hdesk_attachments_ticket_idx');
$this->addIndex('helpdesk_attachments', ['comment_id'], 'hdesk_attachments_comment_idx');
$this->addIndex('helpdesk_attachments', ['user_id'], 'hdesk_attachments_user_idx');
Schema::table('helpdesk_projects', function (Blueprint $table): void {
$table->index(['company_id', 'archived_at']);
});
if (Schema::hasTable('helpdesk_projects')) {
$this->addIndex('helpdesk_projects', ['company_id', 'archived_at'], 'hdesk_projects_company_archived_idx');
}
Schema::table('helpdesk_teams', function (Blueprint $table): void {
$table->index(['company_id', 'archived_at']);
});
Schema::table('helpdesk_category_user', function (Blueprint $table): void {
$table->index(['category_id', 'user_id']);
$table->index(['user_id', 'category_id']);
if (Schema::hasTable('helpdesk_teams')) {
$this->addIndex('helpdesk_teams', ['company_id', 'archived_at'], 'hdesk_teams_company_archived_idx');
}
if (Schema::hasTable('helpdesk_category_user')) {
$this->addIndex('helpdesk_category_user', ['category_id', 'user_id'], 'hdesk_category_user_category_user_idx');
$this->addIndex('helpdesk_category_user', ['user_id', 'category_id'], 'hdesk_category_user_user_category_idx');
}
}
/** @return void */
public function down(): void
{
foreach ([
'helpdesk_tickets' => [
'hdesk_tickets_company_status_idx',
'hdesk_tickets_company_priority_idx',
'hdesk_tickets_company_category_idx',
'hdesk_tickets_company_team_idx',
'hdesk_tickets_status_created_idx',
'hdesk_tickets_due_at_idx',
'hdesk_tickets_archived_at_idx',
],
'helpdesk_comments' => [
'hdesk_comments_parent_created_idx',
'hdesk_comments_user_created_idx',
],
'helpdesk_attachments' => [
'hdesk_attachments_ticket_idx',
'hdesk_attachments_comment_idx',
'hdesk_attachments_user_idx',
],
'helpdesk_projects' => [
'hdesk_projects_company_archived_idx',
],
'helpdesk_teams' => [
'hdesk_teams_company_archived_idx',
],
'helpdesk_category_user' => [
'hdesk_category_user_category_user_idx',
'hdesk_category_user_user_category_idx',
],
] as $table => $indexes) {
if (! Schema::hasTable($table)) {
continue;
}
Schema::table($table, function (Blueprint $tableBlueprint) use ($table, $indexes): void {
foreach ($indexes as $index) {
if ($this->indexExists($table, $index)) {
$tableBlueprint->dropIndex($index);
}
}
});
}
}
/**
* Add an index only when the table exists and the named index does not exist.
*
* @param string $table
* @param array<int, string> $columns
* @param string $index
* @return void
*/
private function addIndex(string $table, array $columns, string $index): void
{
if (! Schema::hasTable($table) || $this->indexExists($table, $index)) {
return;
}
Schema::table($table, static function (Blueprint $tableBlueprint) use ($columns, $index): void {
$tableBlueprint->index($columns, $index);
});
}
}
/**
* Determine whether an index exists for the current connection.
*
* @param string $table
* @param string $index
* @return bool
*/
private function indexExists(string $table, string $index): bool
{
if (method_exists(Schema::getFacadeRoot(), 'hasIndex')) {
return Schema::hasIndex($table, $index);
}
if (DB::getDriverName() === 'pgsql') {
return DB::selectOne(
'select 1 from pg_indexes where schemaname = current_schema() and tablename = ? and indexname = ? limit 1',
[$table, $index]
) !== null;
}
return false;
}
};