Skip to content

Commit

Permalink
PHPORM-232 Support whereLike and whereNotLike (#3108)
Browse files Browse the repository at this point in the history
* PHPORM-232 Support whereLike and whereNotLike
* Check required methods from Laravel in tests
  • Loading branch information
GromNaN authored Aug 26, 2024
1 parent a2eb54a commit 59e16b9
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 112 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## [4.8.0] - next

* Add `Query\Builder::incrementEach()` and `decrementEach()` methods by @SmallRuralDog in [#2550](https://github.com/mongodb/laravel-mongodb/pull/2550)
* Add `Query\Builder::whereLike()` and `whereNotLike()` methods by @GromNaN in [#3108](https://github.com/mongodb/laravel-mongodb/pull/3108)
* Deprecate `Connection::collection()` and `Schema\Builder::collection()` methods by @GromNaN in [#3062](https://github.com/mongodb/laravel-mongodb/pull/3062)
* Deprecate `Model::$collection` property to customize collection name. Use `$table` instead by @GromNaN in [#3064](https://github.com/mongodb/laravel-mongodb/pull/3064)

Expand Down
10 changes: 9 additions & 1 deletion src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,8 @@ protected function compileWhereBasic(array $where): array
// All backslashes are converted to \\, which are needed in matching regexes.
preg_quote($value),
);
$value = new Regex('^' . $regex . '$', 'i');
$flags = $where['caseSensitive'] ?? false ? '' : 'i';
$value = new Regex('^' . $regex . '$', $flags);

// For inverse like operations, we can just use the $not operator with the Regex
$operator = $operator === 'like' ? '=' : 'not';
Expand Down Expand Up @@ -1324,6 +1325,13 @@ protected function compileWhereNotIn(array $where): array
return [$where['column'] => ['$nin' => array_values($where['values'])]];
}

protected function compileWhereLike(array $where): array
{
$where['operator'] = $where['not'] ? 'not like' : 'like';

return $this->compileWhereBasic($where);
}

protected function compileWhereNull(array $where): array
{
$where['operator'] = '=';
Expand Down
281 changes: 170 additions & 111 deletions tests/Query/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@
use function collect;
use function method_exists;
use function now;
use function sprintf;
use function var_export;

class BuilderTest extends TestCase
{
#[DataProvider('provideQueryBuilderToMql')]
public function testMql(array $expected, Closure $build): void
public function testMql(array $expected, Closure $build, ?string $requiredMethod = null): void
{
if ($requiredMethod && ! method_exists(Builder::class, $requiredMethod)) {
$this->markTestSkipped(sprintf('Method "%s::%s()" does not exist.', Builder::class, $requiredMethod));
}

$builder = $build(self::getBuilder());
$this->assertInstanceOf(Builder::class, $builder);
$mql = $builder->toMql();
Expand Down Expand Up @@ -748,6 +753,48 @@ function (Builder $builder) {
fn (Builder $builder) => $builder->where('name', 'like', '_ac__me_'),
];

yield 'whereLike' => [
['find' => [['name' => new Regex('^1$', 'i')], []]],
fn
(Builder $builder) => $builder->whereLike('name', '1'),
'whereLike',
];

yield 'whereLike case not sensitive' => [
['find' => [['name' => new Regex('^1$', 'i')], []]],
fn
(Builder $builder) => $builder->whereLike('name', '1', false),
'whereLike',
];

yield 'whereLike case sensitive' => [
['find' => [['name' => new Regex('^1$', '')], []]],
fn
(Builder $builder) => $builder->whereLike('name', '1', true),
'whereLike',
];

yield 'whereNotLike' => [
['find' => [['name' => ['$not' => new Regex('^1$', 'i')]], []]],
fn
(Builder $builder) => $builder->whereNotLike('name', '1'),
'whereNotLike',
];

yield 'whereNotLike case not sensitive' => [
['find' => [['name' => ['$not' => new Regex('^1$', 'i')]], []]],
fn
(Builder $builder) => $builder->whereNotLike('name', '1', false),
'whereNotLike',
];

yield 'whereNotLike case sensitive' => [
['find' => [['name' => ['$not' => new Regex('^1$', '')]], []]],
fn
(Builder $builder) => $builder->whereNotLike('name', '1', true),
'whereNotLike',
];

$regex = new Regex('^acme$', 'si');
yield 'where BSON\Regex' => [
['find' => [['name' => $regex], []]],
Expand Down Expand Up @@ -1161,142 +1208,154 @@ function (Builder $elemMatchQuery): void {
];

// Method added in Laravel v10.47.0
if (method_exists(Builder::class, 'whereAll')) {
/** @see DatabaseQueryBuilderTest::testWhereAll */
yield 'whereAll' => [
[
'find' => [
['$and' => [['last_name' => 'Doe'], ['email' => 'Doe']]],
[], // options
],
/** @see DatabaseQueryBuilderTest::testWhereAll */
yield 'whereAll' => [
[
'find' => [
['$and' => [['last_name' => 'Doe'], ['email' => 'Doe']]],
[], // options
],
fn(Builder $builder) => $builder->whereAll(['last_name', 'email'], 'Doe'),
];

yield 'whereAll operator' => [
[
'find' => [
[
'$and' => [
['last_name' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
['email' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
],
],
fn
(Builder $builder) => $builder->whereAll(['last_name', 'email'], 'Doe'),
'whereAll',
];

yield 'whereAll operator' => [
[
'find' => [
[
'$and' => [
['last_name' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
['email' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
],
[], // options
],
[], // options
],
fn(Builder $builder) => $builder->whereAll(['last_name', 'email'], 'not like', '%Doe%'),
];

/** @see DatabaseQueryBuilderTest::testOrWhereAll */
yield 'orWhereAll' => [
[
'find' => [
[
'$or' => [
['first_name' => 'John'],
['$and' => [['last_name' => 'Doe'], ['email' => 'Doe']]],
],
],
fn
(Builder $builder) => $builder->whereAll(['last_name', 'email'], 'not like', '%Doe%'),
'whereAll',
];

/** @see DatabaseQueryBuilderTest::testOrWhereAll */
yield 'orWhereAll' => [
[
'find' => [
[
'$or' => [
['first_name' => 'John'],
['$and' => [['last_name' => 'Doe'], ['email' => 'Doe']]],
],
[], // options
],
[], // options
],
fn(Builder $builder) => $builder
->where('first_name', 'John')
->orWhereAll(['last_name', 'email'], 'Doe'),
];

yield 'orWhereAll operator' => [
[
'find' => [
[
'$or' => [
['first_name' => new Regex('^.*John.*$', 'i')],
[
'$and' => [
['last_name' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
['email' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
],
],
fn
(Builder $builder) => $builder
->where('first_name', 'John')
->orWhereAll(['last_name', 'email'], 'Doe'),
'orWhereAll',
];

yield 'orWhereAll operator' => [
[
'find' => [
[
'$or' => [
['first_name' => new Regex('^.*John.*$', 'i')],
[
'$and' => [
['last_name' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
['email' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
],
],
],
[], // options
],
[], // options
],
fn(Builder $builder) => $builder
->where('first_name', 'like', '%John%')
->orWhereAll(['last_name', 'email'], 'not like', '%Doe%'),
];
}
],
fn
(Builder $builder) => $builder
->where('first_name', 'like', '%John%')
->orWhereAll(['last_name', 'email'], 'not like', '%Doe%'),
'orWhereAll',
];

// Method added in Laravel v10.47.0
if (method_exists(Builder::class, 'whereAny')) {
/** @see DatabaseQueryBuilderTest::testWhereAny */
yield 'whereAny' => [
[
'find' => [
['$or' => [['last_name' => 'Doe'], ['email' => 'Doe']]],
[], // options
],
/** @see DatabaseQueryBuilderTest::testWhereAny */
yield 'whereAny' => [
[
'find' => [
['$or' => [['last_name' => 'Doe'], ['email' => 'Doe']]],
[], // options
],
fn(Builder $builder) => $builder->whereAny(['last_name', 'email'], 'Doe'),
];

yield 'whereAny operator' => [
[
'find' => [
[
'$or' => [
['last_name' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
['email' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
],
],
fn
(Builder $builder) => $builder->whereAny(['last_name', 'email'], 'Doe'),
'whereAny',
];

yield 'whereAny operator' => [
[
'find' => [
[
'$or' => [
['last_name' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
['email' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
],
[], // options
],
[], // options
],
fn(Builder $builder) => $builder->whereAny(['last_name', 'email'], 'not like', '%Doe%'),
];

/** @see DatabaseQueryBuilderTest::testOrWhereAny */
yield 'orWhereAny' => [
[
'find' => [
[
'$or' => [
['first_name' => 'John'],
['$or' => [['last_name' => 'Doe'], ['email' => 'Doe']]],
],
],
fn
(Builder $builder) => $builder->whereAny(['last_name', 'email'], 'not like', '%Doe%'),
'whereAny',
];

/** @see DatabaseQueryBuilderTest::testOrWhereAny */
yield 'orWhereAny' => [
[
'find' => [
[
'$or' => [
['first_name' => 'John'],
['$or' => [['last_name' => 'Doe'], ['email' => 'Doe']]],
],
[], // options
],
[], // options
],
fn(Builder $builder) => $builder
->where('first_name', 'John')
->orWhereAny(['last_name', 'email'], 'Doe'),
];

yield 'orWhereAny operator' => [
[
'find' => [
[
'$or' => [
['first_name' => new Regex('^.*John.*$', 'i')],
[
'$or' => [
['last_name' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
['email' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
],
],
fn
(Builder $builder) => $builder
->where('first_name', 'John')
->orWhereAny(['last_name', 'email'], 'Doe'),
'whereAny',
];

yield 'orWhereAny operator' => [
[
'find' => [
[
'$or' => [
['first_name' => new Regex('^.*John.*$', 'i')],
[
'$or' => [
['last_name' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
['email' => ['$not' => new Regex('^.*Doe.*$', 'i')]],
],
],
],
[], // options
],
[], // options
],
fn(Builder $builder) => $builder
->where('first_name', 'like', '%John%')
->orWhereAny(['last_name', 'email'], 'not like', '%Doe%'),
];
}
],
fn
(Builder $builder) => $builder
->where('first_name', 'like', '%John%')
->orWhereAny(['last_name', 'email'], 'not like', '%Doe%'),
'orWhereAny',
];
}

#[DataProvider('provideExceptions')]
Expand Down

0 comments on commit 59e16b9

Please sign in to comment.