Skip to content

feat(like): add or and negated contains variants - #1

Merged
danielebarbaro merged 2 commits into
mainfrom
feat/like-negation-and-or
Sep 17, 2026
Merged

danielebarbaro merged 2 commits into
mainfrom
feat/like-negation-and-or

Conversation

@danielebarbaro

Copy link
Copy Markdown
Contributor

What

LikeOperator could only add a positive, AND joined contains clause. Filters that OR several keywords together, or exclude keywords with NOT LIKE, still had to be written by hand with whereRaw(), and that is exactly where the MySQL ESCAPE bug tends to come back.

This adds:

LikeOperator::applyContains(Builder $query, string $column, string $term, string $boolean = 'and', bool $not = false): void
LikeOperator::orApplyContains(Builder $query, string $column, string $term): void
LikeOperator::applyNotContains(Builder $query, string $column, string $term): void
LikeOperator::orApplyNotContains(Builder $query, string $column, string $term): void
LikeOperator::applyContainsOnDate(Builder $query, string $column, string $term, string $boolean = 'and', bool $not = false): void

Design

The two new optional parameters copy the signature of Laravel's own whereLike($column, $value, $caseSensitive, $boolean = 'and', $not = false), and the three shortcuts copy its orWhereLike / whereNotLike / orWhereNotLike naming (prefixed like the existing apply* methods). Laravel developers already know that shape, the change is fully backwards compatible, and the shortcuts keep call sites readable without a pair of positional flags. applyContainsOnDate() gets the parameters for free because both methods now share one private builder, but no extra shortcuts, to keep the surface small.

$boolean ends up concatenated into the SQL by the grammar, so anything other than and or or (case insensitive) throws InvalidArgumentException instead of producing broken SQL.

Escaping, operator choice (ILIKE / NOT ILIKE on PostgreSQL) and the per driver ESCAPE clause are unchanged and shared by every variant.

NULL semantics

Unchanged and now documented: NULL NOT LIKE '%x%' is NULL, so a NULL column is excluded by the negated variants just as by the positive ones. A test pins this down, and the README shows the whereNull() plus orApplyNotContains() grouping for callers that want those rows kept.

Tests

New integration tests cover OR joining, OR grouping inside a closure, NOT, OR NOT, the raw $boolean / $not arguments, the date variant, the NULL behaviour and the invalid boolean. The negated variant is checked with decoy rows for % (100% remote), _ (data_engineer) and \. The suite passes on SQLite, MySQL 8 and PostgreSQL 16, along with PHPStan, Pint, Rector and type coverage.

The workbench documents table gains a nullable summary column for the NULL tests.

@danielebarbaro
danielebarbaro merged commit 1d539df into main Sep 17, 2026
24 checks passed
@danielebarbaro
danielebarbaro deleted the feat/like-negation-and-or branch September 17, 2026 15:10
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