illuminatech / db-safedelete
尝试强制删除,如果失败则回退到软删除。
Requires
- illuminate/database: ^5.8 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0
Requires (Dev)
- illuminate/events: *
- phpunit/phpunit: ^7.5 || ^8.0 || ^9.3 || ^10.5
README
Laravel Eloquent 安全删除
此扩展为 Eloquent 模型提供“安全”删除功能,尝试调用强制删除,如果失败则回退到软删除。
有关许可信息,请检查 LICENSE 文件。
安装
安装此扩展的首选方法是使用 composer。
运行以下命令:
php composer.phar require --prefer-dist illuminatech/db-safedelete
或添加以下内容到您的 composer.json 的 require 部分:
"illuminatech/db-safedelete": "*"
使用方法
此扩展为 Eloquent 模型提供“安全”删除功能,尝试调用强制删除,如果失败则回退到软删除。它建立在常规 Laravel 模型软删除 功能之上。
在关系型数据库(如 MySQL、PostgreSQL 等)中使用,支持外键时,“软”删除被广泛用于保持外键一致性。例如:如果用户在在线商店进行购买,此购买信息应保留在系统中以供未来记账。此类数据结构的 DDL 可能如下所示:
CREATE TABLE `сustomers` ( `id` integer NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL, `address` varchar(64) NOT NULL, `phone` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE InnoDB; CREATE TABLE `purchases` ( `id` integer NOT NULL AUTO_INCREMENT, `customer_id` integer NOT NULL, `item_id` integer NOT NULL, `amount` integer NOT NULL, PRIMARY KEY (`id`) FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, FOREIGN KEY (`item_id`) REFERENCES `items` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, ) ENGINE InnoDB;
因此,在从 'purchase' 到 'user' 设置外键时,使用 'ON DELETE RESTRICT' 模式。因此,在尝试删除至少有一个购买记录的用户记录时,将发生数据库错误。然而,如果用户记录没有外部引用,则可以删除。
此扩展引入了 Illuminatech\DbSafeDelete\SafeDeletes
特性,它是标准 Illuminate\Database\Eloquent\SoftDeletes
的增强版本,允许处理外键约束和自定义删除逻辑。将 Illuminatech\DbSafeDelete\SafeDeletes
附加到模型会以这种方式更改模型的常规 delete()
方法:尝试调用强制删除,如果失败则回退到软删除。例如:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminatech\DbSafeDelete\SafeDeletes; class Customer extends Model { use SafeDeletes; public function purchases() { return $this->hasMany(Purchase::class); } // ... } // if there is a foreign key reference : $customerWithReference = Customer::query() ->whereHas('purchases') ->first(); $customerWithReference->delete(); // performs "soft" delete! // if there is NO foreign key reference : $customerWithoutReference = Customer::query() ->whereDoesntHave('purchases') ->first(); $customerWithoutReference->delete(); // performs actual delete!
注意! 确保您不要在同一个模型类中同时附加 Illuminate\Database\Eloquent\SoftDeletes
和 Illuminatech\DbSafeDelete\SafeDeletes
。因为这会导致 PHP 命名冲突错误,因为 Illuminate\Database\Eloquent\SoftDeletes
已经包含在 Illuminatech\DbSafeDelete\SafeDeletes
中。
智能删除
通常,“软”删除功能用于防止数据库历史丢失,确保已使用且可能具有引用或依赖的数据保持系统内。然而,有时也允许对这类数据进行实际删除。例如:通常不应删除用户账户记录,而应将其标记为“已删除”,但是如果您浏览用户列表并发现很久以前注册但至少没有一次登录系统的账户,这些记录对历史没有价值,可以将其从数据库中删除以节省磁盘空间。
您可以通过 Illuminatech\DbSafeDelete\SafeDeletes::forceDeleteAllowed()
使“软”删除变得“智能”,并检测记录是否可以从数据库中删除或仅标记为“已删除”。例如:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminatech\DbSafeDelete\SafeDeletes; class User extends Model { use SafeDeletes; public function forceDeleteAllowed(): bool { return $this->last_login_at === null; } // ... } $user = User::query()->whereNull('last_login_at')->first(); $user->delete(); // removes the record!!! $user = User::query()->whereNotNull('last_login_at')->first(); $user->delete(); // marks record as "trashed"
手动删除流程控制
使用 Illuminatech\DbSafeDelete\SafeDeletes
,您仍然可以手动执行“软删除”或“强制删除”特定记录,使用以下方法:
softDelete()
- 总是执行“软删除”。forceDelete()
- 总是执行实际删除。safeDelete()
- 尝试执行实际删除,如果失败则应用“软删除”。
例如:
<?php // if there is a foreign key reference : $customerWithReference = Customer::query() ->whereHas('purchases') ->first(); $customerWithReference->forceDelete(); // performs actual delete (triggers a database error actually)! // if there is NO foreign key reference : $customerWithoutReference = Customer::query() ->whereDoesntHave('purchases') ->first(); $customerWithoutReference->softDelete(); // performs "soft" delete! // if there is a foreign key reference : $customerWithReference = Customer::query() ->whereHas('purchases') ->first(); $customerWithReference->safeDelete(); // performs "soft" delete! // if there is NO foreign key reference : $customerWithoutReference = Customer::query() ->whereDoesntHave('purchases') ->first(); $customerWithoutReference->safeDelete(); // performs actual delete!