You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
where, whereNot, whereInList, contains: stricter matching for strings, null, and bools
Most calls behave the same: numbers still match numeric strings, so where('id', 5) matches '5' and where('price', 1) matches '1.00'. Three edge cases now match fewer rows:
- Two strings must match exactly: '01' no longer matches '1', and where('code', '0e123') no longer matches '0e999' (PHP's loose == read both as numbers - a wrong-row risk for hash lookups)
- null only matches null, like SQL IS NULL (it used to match '', 0, and false)
- true/false mean 1/0 (true used to match any truthy value, even 'abc')
All four methods share one helper (valueMatches). Checked the full matrix against MariaDB: same answers as a SQL WHERE except strings stay case-sensitive and 'abc' never equals 0. New tests pin each rule, and a partition test checks where() + whereNot() always split a set exactly in two. Docs, changelog, and UPGRADING updated.
|`where(string $field, mixed $value = null): static`| Nested only. Keeps rows where `$field == $value` (loose; `'1'` matches 1; Smart args unwrap). Rows without the field are dropped. Chain calls for AND. Warns when `$field` is missing from the first row. Single-arg `where($field)` keeps rows where the field is non-empty (PHP `empty()` rule: NULL, false, 0, "0", "", missing are empty). NOTE: `where($f)` and `where($f, null)` differ - the latter is a loose == null match|
246
-
|`whereNot(string $field, mixed $value = null): static`| Nested only. Drops rows where `$field == $value`; rows WITHOUT the field are kept. Single-arg `whereNot($field)` keeps rows where the field is empty or missing (exact complement of `where($field)`) |
245
+
|`where(string $field, mixed $value = null): static`| Nested only. Keeps rows where `$field` matches `$value`: strings match as exact text (`'0e12'` never matches `'0e99'`), numbers match numerically in either direction (`'1'` matches 1, 1 matches `'1.00'`), null matches only null (SQL IS NULL), bools compare as 1/0 on either side. Smart args unwrap. Rows without the field are dropped. Chain calls for AND. Warns when `$field` is missing from the first row. Single-arg `where($field)` keeps rows where the field is non-empty (PHP `empty()` rule: NULL, false, 0, "0", "", missing are empty). NOTE: `where($f)` and `where($f, null)` differ - the latter matches only stored NULLs|
246
+
|`whereNot(string $field, mixed $value = null): static`| Nested only. Drops rows where `$field` matches `$value` (same matching rules as `where()`); rows WITHOUT the field are kept. Single-arg `whereNot($field)` keeps rows where the field is empty or missing (exact complement of `where($field)`) |
247
247
|`whereInList(string $field, mixed $value): static`| Nested only. Keeps rows where tab-separated `$field` contains `$value` as a whole value (`"\tmenu\tfooter\t"` format, CMS Builder checkbox/multi-select fields) or equals it as a plain single value. Never substring matching |
248
248
|`filter(?callable $callback = null): static`| Both shapes. Callback receives raw `($value, $key)`, keeps on true. No callback: removes falsy (`""`, 0, null, false). Keys preserved like `array_filter()` - chain `values()` for a clean JSON array |
|`->where($field, $value)`| Keeps rows where `$field`loosely equals `$value` (==, so `'1'` matches 1); chain calls to match several fields. With `$field` alone, keeps rows where it's non-empty (PHP `empty()` rule)|
82
-
|`->whereNot($field, $value)`| Drops rows where `$field`loosely equals `$value`; rows without the field are kept. With `$field` alone, keeps rows where it's empty or missing |
|`->filter($callback)`| Keeps elements where `$callback` returns true (closures receive plain PHP values as `($value, $key)`); with no callback, removes falsy values (PHP falsy rule: `""`, `"0"`, 0, NULL, false); keys are kept |
85
-
|`->sort($flags)`| Sorts a flat list ascending by value, renumbering keys; `$flags` choose how values compare (default `SORT_REGULAR`); `SORT_ASC`/`SORT_DESC` throw, sort descending in SQL |
86
-
|`->sortBy($field, $flags)`| Sorts rows ascending by `$field`; pass `SORT_NATURAL` to sort numbers the way people read them |
87
-
|`->unique()`| Removes duplicate values from a flat list, keeping the first of each and preserving keys (compares as text, so 1 and `'1'` match); chain `->values()` to renumber |
|`->where($field, $value)`| Keeps rows where `$field`matches `$value` (`'1'` matches 1, but two strings must match exactly, and null only matches null); chain calls to match several fields. With `$field` alone, keeps rows where it's non-empty (PHP `empty()` rule) |
82
+
|`->whereNot($field, $value)`| Drops rows where `$field`matches `$value` (same rules as `where()`); rows without the field are kept. With `$field` alone, keeps rows where it's empty or missing|
|`->filter($callback)`| Keeps elements where `$callback` returns true (closures receive plain PHP values as `($value, $key)`); with no callback, removes falsy values (PHP falsy rule: `""`, `"0"`, 0, NULL, false); keys are kept |
85
+
|`->sort($flags)`| Sorts a flat list ascending by value, renumbering keys; `$flags` choose how values compare (default `SORT_REGULAR`); `SORT_ASC`/`SORT_DESC` throw, sort descending in SQL |
86
+
|`->sortBy($field, $flags)`| Sorts rows ascending by `$field`; pass `SORT_NATURAL` to sort numbers the way people read them |
87
+
|`->unique()`| Removes duplicate values from a flat list, keeping the first of each and preserving keys (compares as text, so 1 and `'1'` match); chain `->values()` to renumber |
88
88
89
89
### [Transforming and Grouping](transforming-and-grouping.md)
0 commit comments