Skip to content

Commit 0f8624f

Browse files
[9.x] Add whereNot() to Query Builder and Eloquent Builder (#41096)
* Add whereNot() to Query Builder and Eloquent Builder * Fix styling in whereNot() * Add missing \Closure types in Eloquent Builder whereNested() definitions * formatting * formatting Co-authored-by: Taylor Otwell <[email protected]>
1 parent e87c36d commit 0f8624f

File tree

6 files changed

+154
-0
lines changed

6 files changed

+154
-0
lines changed

src/Illuminate/Database/Eloquent/Builder.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,29 @@ public function orWhere($column, $operator = null, $value = null)
316316
return $this->where($column, $operator, $value, 'or');
317317
}
318318

319+
/**
320+
* Add a "where not" clause to the query.
321+
*
322+
* @param \Closure $callback
323+
* @param string $boolean
324+
* @return $this
325+
*/
326+
public function whereNot(Closure $callback, $boolean = 'and')
327+
{
328+
return $this->where($callback, null, null, $boolean.' not');
329+
}
330+
331+
/**
332+
* Add an "or where not" clause to the query.
333+
*
334+
* @param \Closure $callback
335+
* @return $this
336+
*/
337+
public function orWhereNot(Closure $callback)
338+
{
339+
return $this->whereNot($callback, 'or');
340+
}
341+
319342
/**
320343
* Add an "order by" clause for a timestamp to the query.
321344
*

src/Illuminate/Database/Query/Builder.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,29 @@ public function orWhere($column, $operator = null, $value = null)
883883
return $this->where($column, $operator, $value, 'or');
884884
}
885885

886+
/**
887+
* Add a "where not" clause to the query.
888+
*
889+
* @param \Closure $callback
890+
* @param string $boolean
891+
* @return $this
892+
*/
893+
public function whereNot(Closure $callback, $boolean = 'and')
894+
{
895+
return $this->whereNested($callback, $boolean.' not');
896+
}
897+
898+
/**
899+
* Add an "or where not" clause to the query.
900+
*
901+
* @param \Closure $callback
902+
* @return $this
903+
*/
904+
public function orWhereNot(Closure $callback)
905+
{
906+
return $this->whereNot($callback, 'or');
907+
}
908+
886909
/**
887910
* Add a "where" clause comparing two columns to the query.
888911
*

tests/Database/DatabaseEloquentBuilderTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,44 @@ public function testRealNestedWhereWithMultipleScopesAndOneDeadScope()
884884
$this->assertEquals(['bar', 9000], $query->getBindings());
885885
}
886886

887+
public function testWhereNot()
888+
{
889+
$nestedQuery = m::mock(Builder::class);
890+
$nestedRawQuery = $this->getMockQueryBuilder();
891+
$nestedQuery->shouldReceive('getQuery')->once()->andReturn($nestedRawQuery);
892+
$model = $this->getMockModel()->makePartial();
893+
$model->shouldReceive('newQueryWithoutRelationships')->once()->andReturn($nestedQuery);
894+
$builder = $this->getBuilder();
895+
$builder->getQuery()->shouldReceive('from');
896+
$builder->setModel($model);
897+
$builder->getQuery()->shouldReceive('addNestedWhereQuery')->once()->with($nestedRawQuery, 'and not');
898+
$nestedQuery->shouldReceive('foo')->once();
899+
900+
$result = $builder->whereNot(function ($query) {
901+
$query->foo();
902+
});
903+
$this->assertEquals($builder, $result);
904+
}
905+
906+
public function testOrWhereNot()
907+
{
908+
$nestedQuery = m::mock(Builder::class);
909+
$nestedRawQuery = $this->getMockQueryBuilder();
910+
$nestedQuery->shouldReceive('getQuery')->once()->andReturn($nestedRawQuery);
911+
$model = $this->getMockModel()->makePartial();
912+
$model->shouldReceive('newQueryWithoutRelationships')->once()->andReturn($nestedQuery);
913+
$builder = $this->getBuilder();
914+
$builder->getQuery()->shouldReceive('from');
915+
$builder->setModel($model);
916+
$builder->getQuery()->shouldReceive('addNestedWhereQuery')->once()->with($nestedRawQuery, 'or not');
917+
$nestedQuery->shouldReceive('foo')->once();
918+
919+
$result = $builder->orWhereNot(function ($query) {
920+
$query->foo();
921+
});
922+
$this->assertEquals($builder, $result);
923+
}
924+
887925
public function testRealQueryHigherOrderOrWhereScopes()
888926
{
889927
$model = new EloquentBuilderTestHigherOrderWhereScopeStub;

tests/Database/DatabaseQueryBuilderTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,30 @@ public function testNestedWhereBindings()
16931693
$this->assertEquals([0 => 'foo', 1 => 'bar'], $builder->getBindings());
16941694
}
16951695

1696+
public function testWhereNot()
1697+
{
1698+
$builder = $this->getBuilder();
1699+
$builder->select('*')->from('users')->whereNot(function ($q) {
1700+
$q->where('email', '=', 'foo');
1701+
});
1702+
$this->assertSame('select * from "users" where not ("email" = ?)', $builder->toSql());
1703+
$this->assertEquals([0 => 'foo'], $builder->getBindings());
1704+
1705+
$builder = $this->getBuilder();
1706+
$builder->select('*')->from('users')->where('name', '=', 'bar')->whereNot(function ($q) {
1707+
$q->where('email', '=', 'foo');
1708+
});
1709+
$this->assertSame('select * from "users" where "name" = ? and not ("email" = ?)', $builder->toSql());
1710+
$this->assertEquals([0 => 'bar', 1 => 'foo'], $builder->getBindings());
1711+
1712+
$builder = $this->getBuilder();
1713+
$builder->select('*')->from('users')->where('name', '=', 'bar')->orWhereNot(function ($q) {
1714+
$q->where('email', '=', 'foo');
1715+
});
1716+
$this->assertSame('select * from "users" where "name" = ? or not ("email" = ?)', $builder->toSql());
1717+
$this->assertEquals([0 => 'bar', 1 => 'foo'], $builder->getBindings());
1718+
}
1719+
16961720
public function testFullSubSelects()
16971721
{
16981722
$builder = $this->getBuilder();

tests/Integration/Database/EloquentWhereTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,33 @@ public function testWhereAndWhereOrBehavior()
6363
);
6464
}
6565

66+
public function testWhereNot()
67+
{
68+
/** @var \Illuminate\Tests\Integration\Database\UserWhereTest $firstUser */
69+
$firstUser = UserWhereTest::create([
70+
'name' => 'test-name',
71+
'email' => 'test-email',
72+
'address' => 'test-address',
73+
]);
74+
75+
/** @var \Illuminate\Tests\Integration\Database\UserWhereTest $secondUser */
76+
$secondUser = UserWhereTest::create([
77+
'name' => 'test-name1',
78+
'email' => 'test-email1',
79+
'address' => 'test-address1',
80+
]);
81+
82+
$this->assertTrue($secondUser->is(UserWhereTest::whereNot(function ($query) use ($firstUser) {
83+
$query->where('name', '=', $firstUser->name);
84+
})->first()));
85+
$this->assertTrue($firstUser->is(UserWhereTest::where('name', $firstUser->name)->whereNot(function ($query) use ($secondUser) {
86+
$query->where('email', $secondUser->email);
87+
})->first()));
88+
$this->assertTrue($secondUser->is(UserWhereTest::where('name', 'wrong-name')->orWhereNot(function ($query) use ($firstUser) {
89+
$query->where('email', $firstUser->email);
90+
})->first()));
91+
}
92+
6693
public function testFirstWhere()
6794
{
6895
/** @var \Illuminate\Tests\Integration\Database\UserWhereTest $firstUser */

tests/Integration/Database/QueryBuilderTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,25 @@ public function testWhereValueSubQueryBuilder()
132132
$this->assertTrue(DB::table('posts')->where($subQuery, '!=', 'Does not match')->exists());
133133
}
134134

135+
public function testWhereNot()
136+
{
137+
$results = DB::table('posts')->whereNot(function ($query) {
138+
$query->where('title', 'Foo Post');
139+
})->get();
140+
141+
$this->assertCount(1, $results);
142+
$this->assertSame('Bar Post', $results[0]->title);
143+
}
144+
145+
public function testOrWhereNot()
146+
{
147+
$results = DB::table('posts')->where('id', 1)->orWhereNot(function ($query) {
148+
$query->where('title', 'Foo Post');
149+
})->get();
150+
151+
$this->assertCount(2, $results);
152+
}
153+
135154
public function testWhereDate()
136155
{
137156
$this->assertSame(1, DB::table('posts')->whereDate('created_at', '2018-01-02')->count());

0 commit comments

Comments
 (0)