Skip to content

fix: batch board records fetch to eliminate per-column N+1 queries#82

Closed
ManukMinasyan wants to merge 1 commit into4.xfrom
fix/batch-board-records
Closed

fix: batch board records fetch to eliminate per-column N+1 queries#82
ManukMinasyan wants to merge 1 commit into4.xfrom
fix/batch-board-records

Conversation

@ManukMinasyan
Copy link
Contributor

Summary

  • Replace per-column getBoardRecords() calls in Board::getViewData() with a single getBatchedBoardRecords() query
  • Fetches all column records in one query, then partitions in PHP
  • Eliminates N+1 queries proportional to the number of board columns

Test plan

  • Verified locally with OpportunitiesBoard and TasksBoard
  • Board renders correctly with batched records

Copilot AI review requested due to automatic review settings February 17, 2026 00:39
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes database query performance by eliminating N+1 queries when loading board records. Previously, each column triggered a separate query via getBoardRecords(). The new implementation fetches all column records in a single query and partitions them in PHP using getBatchedBoardRecords().

Changes:

  • Added getBatchedBoardRecords() method to fetch all column records in one query
  • Modified Board::getViewData() to use the batched approach instead of per-column queries
  • Implemented in-memory partitioning with per-column limit support

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Concerns/HasBoardRecords.php Added new getBatchedBoardRecords() method that fetches all column records in a single query with filtering and partitioning logic
src/Board.php Updated getViewData() to call getBatchedBoardRecords() once instead of calling getBoardRecords() per column

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +178 to +182
$allRecords = (clone $baseQuery)
->whereIn($statusField, $columnIds)
->orderBy($positionField, 'asc')
->orderBy($keyName, 'asc')
->get();
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The position field ordering is applied unconditionally, but the original getBoardRecords() method (line 90) checks if the position field exists using modelHasColumn() before ordering. This could cause SQL errors if the position field doesn't exist in the table. Add a check similar to the one in getBoardRecords():

if ($positionField && $this->modelHasColumn($baseQuery->getModel(), $positionField)) {
    $allRecords = (clone $baseQuery)
        ->whereIn($statusField, $columnIds)
        ->orderBy($positionField, 'asc')
        ->orderBy($keyName, 'asc')
        ->get();
} else {
    $allRecords = (clone $baseQuery)
        ->whereIn($statusField, $columnIds)
        ->orderBy($keyName, 'asc')
        ->get();
}
Suggested change
$allRecords = (clone $baseQuery)
->whereIn($statusField, $columnIds)
->orderBy($positionField, 'asc')
->orderBy($keyName, 'asc')
->get();
if ($positionField && $this->modelHasColumn($baseQuery->getModel(), $positionField)) {
$allRecords = (clone $baseQuery)
->whereIn($statusField, $columnIds)
->orderBy($positionField, 'asc')
->orderBy($keyName, 'asc')
->get();
} else {
$allRecords = (clone $baseQuery)
->whereIn($statusField, $columnIds)
->orderBy($keyName, 'asc')
->get();
}

Copilot uses AI. Check for mistakes.
@ManukMinasyan
Copy link
Contributor Author

Closing — the per-column getBoardRecords() approach with LIMIT is correct. The batched method removes the limit and loads all records, which is worse at scale. Not an actual N+1.

@ManukMinasyan ManukMinasyan deleted the fix/batch-board-records branch February 17, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments