Skip to content

Group the table search so it narrows a report instead of widening it - #118

Merged
SimonBarrettACT merged 1 commit into
mainfrom
bugfix/search-ors-past-query-constraints
Sep 7, 2026
Merged

Group the table search so it narrows a report instead of widening it#118
SimonBarrettACT merged 1 commit into
mainfrom
bugfix/search-ors-past-query-constraints

Conversation

@SimonBarrettACT

Copy link
Copy Markdown
Member

Fixes ACT-Training/people#3515.

TableBuilder::rowsQuery() appended each searchable column as a bare top-level orWhere, after everything query() returned and after the model's global scopes:

$query->orWhere($actualColumnName, 'like', '%'.$this->searchBy.'%');

So the predicate became:

WHERE (report constraints) AND terminated = 0 AND ... OR column LIKE '%term%'

Any report whose query() carried real constraints therefore discarded them the moment someone typed in the search box. Nothing inside query() could defend against it — wrapping that body in a nested where() group does not help, because the orWhere sits outside that group too.

Measured impact

From the consuming app, against production data (report.training.health-and-safety-list-table):

Search Rows
(none) 115 correct
"a" 32,215 the entire records table
"Fire" 186 71 not health & safety courses, including "Fire up your Facebook page — organic growth strategy and tactics"

A second report, Resources\PayrollHistory, leaks the same way — 502 rows becomes 684 — but latently: it sets displaySearch(false), so the box is not rendered and searchBy cannot normally be set from the UI. It is one displaySearch() away from being live.

The fix

The loop is wrapped in its own where() group, so search composes as AND (a OR b). Every report using TableBuilder becomes correct without changing the report.

Behaviour is otherwise unchanged: a leading orWhere inside a group renders without its boolean, so the grouped SQL is the same set of terms, correctly parenthesised.

Tests

Three new tests in tests/TableBuilderSearchScopingTest.php, driving rowsQuery() against a constrained report with a searchable column and asserting on returned rows rather than SQL strings:

  • the unsearched report returns only its constrained rows
  • a search term keeps the constraints — this is the bug, and it returned the unconstrained row before the fix
  • a search never returns more rows than no search at all

The first and third pass against the old code; the second fails, which is the defect.

Full suite: 10 passed. Pint clean. PHPStan reports 20 errors, all pre-existing — verified by stashing this change and re-running on clean main, which reports the same 20 in the same files. None are in TableBuilder.php or the new test.

After this merges

The consuming app pins act-training/query-builder: ^2.4.1. This wants a 2.4.2 release and a composer update in people before ACT-Training/people#3515 can be closed — and until it ships, any new report with a constrained query still needs displaySearch(false).

🤖 Generated with Claude Code

https://claude.ai/code/session_01MfdXn5v557zRZ9F6bYcjAd

TableBuilder::rowsQuery() appended each searchable column as a bare
top-level orWhere, after everything query() returned and after the
model's global scopes. The predicate became

    WHERE (report constraints) AND ... OR column LIKE '%term%'

so any report whose query() carried real constraints discarded them the
moment someone typed in the search box. Nothing inside query() could
prevent it: wrapping that body in a nested where() does not help, because
the orWhere sits outside that group too.

Measured in the consuming app against production data: a health and
safety report returning 115 rows returned 32,215 — the entire records
table — when searched for "a", and searching "Fire" returned 71 courses
that were not health and safety, including "Fire up your Facebook page".

The loop is now wrapped in its own where() group, so search composes as
AND (a OR b) and every report using TableBuilder becomes correct without
change.

Refs ACT-Training/people#3515

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MfdXn5v557zRZ9F6bYcjAd
@SimonBarrettACT
SimonBarrettACT merged commit 1b56a7d into main Sep 7, 2026
2 of 8 checks passed
@SimonBarrettACT
SimonBarrettACT deleted the bugfix/search-ors-past-query-constraints branch September 7, 2026 14:03
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