Skip to content

Commit a934887

Browse files
[9.x] Introduce annotated builder contracts (#40546)
* Introduce annotated builder contracts * Add tests from previous branch * StyleCI * Update Builder.php * Update Builder.php Co-authored-by: Taylor Otwell <[email protected]>
1 parent 9b4f011 commit a934887

File tree

6 files changed

+136
-3
lines changed

6 files changed

+136
-3
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Illuminate\Contracts\Database\Eloquent;
4+
5+
use Illuminate\Contracts\Database\Query\Builder as BaseContract;
6+
7+
/**
8+
* This interface is intentionally empty and exists to improve IDE support.
9+
*
10+
* @mixin \Illuminate\Database\Eloquent\Builder
11+
*/
12+
interface Builder extends BaseContract
13+
{
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Illuminate\Contracts\Database\Query;
4+
5+
/**
6+
* This interface is intentionally empty and exists to improve IDE support.
7+
*
8+
* @mixin \Illuminate\Database\Query\Builder
9+
*/
10+
interface Builder
11+
{
12+
}

src/Illuminate/Database/Eloquent/Builder.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use BadMethodCallException;
66
use Closure;
77
use Exception;
8+
use Illuminate\Contracts\Database\Eloquent\Builder as BuilderContract;
89
use Illuminate\Contracts\Support\Arrayable;
910
use Illuminate\Database\Concerns\BuildsQueries;
1011
use Illuminate\Database\Eloquent\Concerns\QueriesRelationships;
@@ -24,7 +25,7 @@
2425
*
2526
* @mixin \Illuminate\Database\Query\Builder
2627
*/
27-
class Builder
28+
class Builder implements BuilderContract
2829
{
2930
use BuildsQueries, ForwardsCalls, QueriesRelationships {
3031
BuildsQueries::sole as baseSole;

src/Illuminate/Database/Eloquent/Relations/Relation.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Illuminate\Database\Eloquent\Relations;
44

55
use Closure;
6+
use Illuminate\Contracts\Database\Eloquent\Builder as BuilderContract;
67
use Illuminate\Database\Eloquent\Builder;
78
use Illuminate\Database\Eloquent\Collection;
89
use Illuminate\Database\Eloquent\Model;
@@ -13,7 +14,7 @@
1314
use Illuminate\Support\Traits\ForwardsCalls;
1415
use Illuminate\Support\Traits\Macroable;
1516

16-
abstract class Relation
17+
abstract class Relation implements BuilderContract
1718
{
1819
use ForwardsCalls, Macroable {
1920
Macroable::__call as macroCall;

src/Illuminate/Database/Query/Builder.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Carbon\CarbonPeriod;
77
use Closure;
88
use DateTimeInterface;
9+
use Illuminate\Contracts\Database\Query\Builder as BuilderContract;
910
use Illuminate\Contracts\Support\Arrayable;
1011
use Illuminate\Database\Concerns\BuildsQueries;
1112
use Illuminate\Database\Concerns\ExplainsQueries;
@@ -25,7 +26,7 @@
2526
use LogicException;
2627
use RuntimeException;
2728

28-
class Builder
29+
class Builder implements BuilderContract
2930
{
3031
use BuildsQueries, ExplainsQueries, ForwardsCalls, Macroable {
3132
__call as macroCall;

tests/Database/DatabaseEloquentSoftDeletesIntegrationTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public function createSchema()
5252
$table->increments('id');
5353
$table->integer('user_id');
5454
$table->string('title');
55+
$table->integer('priority')->default(0);
5556
$table->timestamps();
5657
$table->softDeletes();
5758
});
@@ -491,6 +492,109 @@ public function testHasManyRelationshipCanBeSoftDeleted()
491492
$this->assertCount(1, $abigail->posts()->withTrashed()->get());
492493
}
493494

495+
public function testRelationToSqlAppliesSoftDelete()
496+
{
497+
$this->createUsers();
498+
499+
$abigail = SoftDeletesTestUser::where('email', '[email protected]')->first();
500+
501+
$this->assertSame(
502+
'select * from "posts" where "posts"."user_id" = ? and "posts"."user_id" is not null and "posts"."deleted_at" is null',
503+
$abigail->posts()->toSql()
504+
);
505+
}
506+
507+
public function testRelationExistsAndDoesntExistHonorsSoftDelete()
508+
{
509+
$this->createUsers();
510+
$abigail = SoftDeletesTestUser::where('email', '[email protected]')->first();
511+
512+
// 'exists' should return true before soft delete
513+
$abigail->posts()->create(['title' => 'First Title']);
514+
$this->assertTrue($abigail->posts()->exists());
515+
$this->assertFalse($abigail->posts()->doesntExist());
516+
517+
// 'exists' should return false after soft delete
518+
$abigail->posts()->first()->delete();
519+
$this->assertFalse($abigail->posts()->exists());
520+
$this->assertTrue($abigail->posts()->doesntExist());
521+
522+
// 'exists' should return true after restore
523+
$abigail->posts()->withTrashed()->restore();
524+
$this->assertTrue($abigail->posts()->exists());
525+
$this->assertFalse($abigail->posts()->doesntExist());
526+
527+
// 'exists' should return false after a force delete
528+
$abigail->posts()->first()->forceDelete();
529+
$this->assertFalse($abigail->posts()->exists());
530+
$this->assertTrue($abigail->posts()->doesntExist());
531+
}
532+
533+
public function testRelationCountHonorsSoftDelete()
534+
{
535+
$this->createUsers();
536+
$abigail = SoftDeletesTestUser::where('email', '[email protected]')->first();
537+
538+
// check count before soft delete
539+
$abigail->posts()->create(['title' => 'First Title']);
540+
$abigail->posts()->create(['title' => 'Second Title']);
541+
$this->assertEquals(2, $abigail->posts()->count());
542+
543+
// check count after soft delete
544+
$abigail->posts()->where('title', 'Second Title')->delete();
545+
$this->assertEquals(1, $abigail->posts()->count());
546+
547+
// check count after restore
548+
$abigail->posts()->withTrashed()->restore();
549+
$this->assertEquals(2, $abigail->posts()->count());
550+
551+
// check count after a force delete
552+
$abigail->posts()->where('title', 'Second Title')->forceDelete();
553+
$this->assertEquals(1, $abigail->posts()->count());
554+
}
555+
556+
public function testRelationAggregatesHonorsSoftDelete()
557+
{
558+
$this->createUsers();
559+
$abigail = SoftDeletesTestUser::where('email', '[email protected]')->first();
560+
561+
// check aggregates before soft delete
562+
$abigail->posts()->create(['title' => 'First Title', 'priority' => 2]);
563+
$abigail->posts()->create(['title' => 'Second Title', 'priority' => 4]);
564+
$abigail->posts()->create(['title' => 'Third Title', 'priority' => 6]);
565+
$this->assertEquals(2, $abigail->posts()->min('priority'));
566+
$this->assertEquals(6, $abigail->posts()->max('priority'));
567+
$this->assertEquals(12, $abigail->posts()->sum('priority'));
568+
$this->assertEquals(4, $abigail->posts()->avg('priority'));
569+
570+
// check aggregates after soft delete
571+
$abigail->posts()->where('title', 'First Title')->delete();
572+
$this->assertEquals(4, $abigail->posts()->min('priority'));
573+
$this->assertEquals(6, $abigail->posts()->max('priority'));
574+
$this->assertEquals(10, $abigail->posts()->sum('priority'));
575+
$this->assertEquals(5, $abigail->posts()->avg('priority'));
576+
577+
// check aggregates after restore
578+
$abigail->posts()->withTrashed()->restore();
579+
$this->assertEquals(2, $abigail->posts()->min('priority'));
580+
$this->assertEquals(6, $abigail->posts()->max('priority'));
581+
$this->assertEquals(12, $abigail->posts()->sum('priority'));
582+
$this->assertEquals(4, $abigail->posts()->avg('priority'));
583+
584+
// check aggregates after a force delete
585+
$abigail->posts()->where('title', 'Third Title')->forceDelete();
586+
$this->assertEquals(2, $abigail->posts()->min('priority'));
587+
$this->assertEquals(4, $abigail->posts()->max('priority'));
588+
$this->assertEquals(6, $abigail->posts()->sum('priority'));
589+
$this->assertEquals(3, $abigail->posts()->avg('priority'));
590+
}
591+
592+
public function testSoftDeleteIsAppliedToNewQuery()
593+
{
594+
$query = (new SoftDeletesTestUser)->newQuery();
595+
$this->assertSame('select * from "users" where "users"."deleted_at" is null', $query->toSql());
596+
}
597+
494598
public function testSecondLevelRelationshipCanBeSoftDeleted()
495599
{
496600
$this->createUsers();

0 commit comments

Comments
 (0)