Skip to content

Commit

Permalink
[11.x] Add forceDestroy to SoftDeletes (#52432)
Browse files Browse the repository at this point in the history
* Add `forceDestroy` to `SoftDeletes`

* Update Model.php

---------

Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
jasonmccreary and taylorotwell authored Aug 13, 2024
1 parent 320269a commit cc31ca2
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/Illuminate/Database/Eloquent/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,19 @@ public function forceDelete()
return $this->delete();
}

/**
* Force a hard destroy on a soft deleted model.
*
* This method protects developers from running forceDestroy when the trait is missing.
*
* @param \Illuminate\Support\Collection|array|int|string $ids
* @return bool|null
*/
public static function forceDestroy($ids)
{
return static::destroy($ids);
}

/**
* Perform the actual delete query on this model instance.
*
Expand Down
41 changes: 41 additions & 0 deletions src/Illuminate/Database/Eloquent/SoftDeletes.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Illuminate\Database\Eloquent;

use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Support\Collection as BaseCollection;

/**
* @method static \Illuminate\Database\Eloquent\Builder<static> withTrashed(bool $withTrashed = true)
* @method static \Illuminate\Database\Eloquent\Builder<static> onlyTrashed()
Expand Down Expand Up @@ -74,6 +77,44 @@ public function forceDeleteQuietly()
return static::withoutEvents(fn () => $this->forceDelete());
}

/**
* Destroy the models for the given IDs.
*
* @param \Illuminate\Support\Collection|array|int|string $ids
* @return int
*/
public static function forceDestroy($ids)
{
if ($ids instanceof EloquentCollection) {
$ids = $ids->modelKeys();
}

if ($ids instanceof BaseCollection) {
$ids = $ids->all();
}

$ids = is_array($ids) ? $ids : func_get_args();

if (count($ids) === 0) {
return 0;
}

// We will actually pull the models from the database table and call delete on
// each of them individually so that their events get fired properly with a
// correct set of attributes in case the developers wants to check these.
$key = ($instance = new static)->getKeyName();

$count = 0;

foreach ($instance->withTrashed()->whereIn($key, $ids)->get() as $model) {
if ($model->forceDelete()) {
$count++;
}
}

return $count;
}

/**
* Perform the actual delete query on this model instance.
*
Expand Down
62 changes: 62 additions & 0 deletions tests/Database/DatabaseEloquentSoftDeletesIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,68 @@ public function newModelQuery()
$this->assertTrue($user->exists);
}

public function testForceDestroyFullyDeletesRecord()
{
$this->createUsers();
$deleted = SoftDeletesTestUser::forceDestroy(2);

$this->assertSame(1, $deleted);

$users = SoftDeletesTestUser::withTrashed()->get();

$this->assertCount(1, $users);
$this->assertEquals(1, $users->first()->id);
$this->assertNull(SoftDeletesTestUser::find(2));
}

public function testForceDestroyDeletesAlreadyDeletedRecord()
{
$this->createUsers();
$deleted = SoftDeletesTestUser::forceDestroy(1);

$this->assertSame(1, $deleted);

$users = SoftDeletesTestUser::withTrashed()->get();

$this->assertCount(1, $users);
$this->assertEquals(2, $users->first()->id);
$this->assertNull(SoftDeletesTestUser::find(1));
}

public function testForceDestroyDeletesMultipleRecords()
{
$this->createUsers();
$deleted = SoftDeletesTestUser::forceDestroy([1, 2]);

$this->assertSame(2, $deleted);

$this->assertTrue(SoftDeletesTestUser::withTrashed()->get()->isEmpty());
}

public function testForceDestroyDeletesRecordsFromCollection()
{
$this->createUsers();
$deleted = SoftDeletesTestUser::forceDestroy(collect([1, 2]));

$this->assertSame(2, $deleted);

$this->assertTrue(SoftDeletesTestUser::withTrashed()->get()->isEmpty());
}

public function testForceDestroyDeletesRecordsFromEloquentCollection()
{
$this->createUsers();
$deleted = SoftDeletesTestUser::forceDestroy(SoftDeletesTestUser::all());

$this->assertSame(1, $deleted);

$users = SoftDeletesTestUser::withTrashed()->get();

$this->assertCount(1, $users);
$this->assertEquals(1, $users->first()->id);
$this->assertNull(SoftDeletesTestUser::find(2));
}

public function testRestoreRestoresRecords()
{
$this->createUsers();
Expand Down

0 comments on commit cc31ca2

Please sign in to comment.