yii1tech / ar-softdelete
为 Yii1 提供了 ActiveRecord 软删除支持
Requires
- php: >=7.1
- yiisoft/yii: ~1.1.0
Requires (Dev)
- phpunit/phpunit: ^6.0 || ^7.0 || ^8.0 || ^9.3 || ^10.0.7
This package is auto-updated.
Last update: 2024-09-11 10:33:07 UTC
README
Yii 1 应用运行时配置扩展
本扩展为 Yii1 ActiveRecord 软删除提供了支持。
有关许可信息,请查看 LICENSE 文件。
安装
安装此扩展的首选方式是通过 composer。
运行以下命令:
php composer.phar require --prefer-dist yii1tech/ar-softdelete
或
"yii1tech/ar-softdelete": "*"
将以下内容添加到您的 composer.json 文件的 "require" 部分:
用法
此扩展为所谓的“软删除”ActiveRecord提供了支持,这意味着记录不会从数据库中删除,而是通过某种标志或状态标记为不再活跃。
此扩展为 Yii1 中的此类解决方案支持提供了 \yii1tech\ar\softdelete\SoftDeleteBehavior
ActiveRecord 行为。您可以通过以下方式将其附加到模型类中
<?php use CActiveRecord; use yii1tech\ar\softdelete\SoftDeleteBehavior; class Item extends CActiveRecord { public function behaviors() { return [ 'softDeleteBehavior' => [ 'class' => SoftDeleteBehavior::class, 'softDeleteAttributeValues' => [ 'is_deleted' => true, ], ], ]; } }
有 2 种应用“软删除”的方式
- 使用
softDelete()
分离的方法 - 修改常规的
delete()
方法
推荐使用 softDelete()
,因为它允许将记录标记为“已删除”,同时保持常规 delete()
方法完整,这样在需要时可以进行“硬删除”。例如
<?php $id = 17; $item = Item::model()->findByPk($id); $item->softDelete(); // mark record as "deleted" $item = Item::model()->findByPk($id); var_dump($item->is_deleted); // outputs "true" $item->delete(); // perform actual deleting of the record $item = Item::model()->findByPk($id); var_dump($item); // outputs "null"
但是,您可能希望以执行“软删除”而不是实际删除记录的方式修改常规 ActiveRecord delete()
方法。这在将“软删除”功能应用于现有代码的情况下是一个常见解决方案。为此功能,您应在行为配置中启用 \yii1tech\ar\softdelete\SoftDeleteBehavior::$replaceRegularDelete
选项
<?php use CActiveRecord; use yii1tech\ar\softdelete\SoftDeleteBehavior; class Item extends CActiveRecord { public function behaviors() { return [ 'softDeleteBehavior' => [ 'class' => SoftDeleteBehavior::class, 'softDeleteAttributeValues' => [ 'is_deleted' => true ], 'replaceRegularDelete' => true // mutate native `delete()` method ], ]; } }
现在调用 delete()
方法将记录标记为“已删除”,而不是删除它
<?php $id = 17; $item = Item::model()->findByPk($id); $item->delete(); // no record removal, mark record as "deleted" instead $item = Item::model()->findByPk($id); var_dump($item->is_deleted); // outputs "true"
查询“软删除”记录
显然,为了只找到“已删除”或只找到“活动”记录,您应在搜索查询中添加相应的条件
<?php // returns only not "deleted" records $notDeletedItems = Item::model() ->findAll('is_deleted = 0'); // returns "deleted" records $deletedItems = Item::model() ->findAll('is_deleted = 1');
然而,您可以使用 \yii1tech\ar\softdelete\SoftDeleteQueryBehavior
来简化此类查询的构建。一旦附加,它就提供了类似于作用域的记录过滤方法。例如
<?php // Find all "deleted" records: $deletedItems = Item::model()->deleted()->findAll(); // Find all "active" records: $notDeletedItems = Item::model()->notDeleted()->findAll();
您可以使用 filterDeleted()
方法轻松创建用于“已删除”记录的列表过滤器
<?php // Filter records by "soft" deleted criteria: $items = Item::model() ->filterDeleted(Yii::app()->request->getParam('filter_deleted')) ->findAll();
此方法在空过滤器值上应用 notDeleted()
作用域,在正过滤器值上应用 deleted()
- 作用域,在负(零)值上不应用作用域(例如,显示“已删除”和“活动”记录)。
注意:`yii1tech\ar\softdelete\SoftDeleteQueryBehavior` 已设计为正确处理连接并避免模糊列错误,然而,仍有可能无法正确处理某些情况。如果您的查询涉及多个具有“软删除”功能的表,请准备好手动指定“软删除”条件。
默认情况下,yii1tech\ar\softdelete\SoftDeleteQueryBehavior
使用来自 yii1tech\ar\softdelete\SoftDeleteBehavior::$softDeleteAttributeValues
的信息来构建其作用域的筛选标准。因此,如果您正在使用用于标记“软删除”记录的复杂逻辑,则可能需要手动配置筛选条件。例如
<?php use CActiveRecord; use yii1tech\ar\softdelete\SoftDeleteBehavior; class Item extends CActiveRecord { public function behaviors() { return [ 'softDeleteBehavior' => [ 'class' => SoftDeleteBehavior::class, 'softDeleteAttributeValues' => [ 'statusId' => 'deleted', ], 'deletedCondition' => [ 'statusId' => 'deleted', ], 'notDeletedCondition' => [ 'statusId' => 'active', ], ], ]; } // ... }
提示:您可以将筛选“未删除”记录的条件应用于搜索查询作为默认值,启用
yii1tech\ar\softdelete\SoftDeleteBehavior::$autoApplyNotDeletedCondition
。
<?php use CActiveRecord; use yii1tech\ar\softdelete\SoftDeleteBehavior; class Item extends CActiveRecord { public function behaviors() { return [ 'softDeleteBehavior' => [ 'class' => SoftDeleteBehavior::class, 'softDeleteAttributeValues' => [ 'is_deleted' => true, ], 'autoApplyNotDeletedCondition' => true, ], ]; } // ... } $notDeletedItems = Item::model()->findAll(); // returns only not "deleted" records $allItems = Item::find() ->deleted() // applies "deleted" condition, preventing default one ->findAll(); // returns "deleted" records $allItems = Item::find() ->filterDeleted('all') // filter all records, preventing default "not deleted" condition ->all(); // returns all records
智能删除
通常,“软删除”功能用于防止数据库历史记录丢失,确保正在使用的数据以及可能具有引用或依赖的数据保留在系统中。然而,有时也允许实际删除此类数据。例如:通常不应删除用户账户记录,而应仅将其标记为“不活跃”,但如果您浏览用户列表并发现很久以前注册的账户,但没有至少一次登录系统,这些记录对历史没有任何价值,可以将其从数据库中删除以节省磁盘空间。
您可以使“软删除”变得更“智能”,以检测记录是否可以从数据库中删除或仅标记为“已删除”。这可以通过\yii1tech\ar\softdelete\SoftDeleteBehavior::$allowDeleteCallback
实现。例如
<?php use CActiveRecord; use yii1tech\ar\softdelete\SoftDeleteBehavior; class User extends CActiveRecord { public function behaviors() { return [ 'softDeleteBehavior' => [ 'class' => SoftDeleteBehavior::class, 'softDeleteAttributeValues' => [ 'is_deleted' => true ], 'allowDeleteCallback' => function ($user) { return $user->last_login_date === null; // allow to delete user, if he has never logged in } ], ]; } } $user = User::model()->find('last_login_date IS NULL'); $user->softDelete(); // removes the record!!! $user = User::find()->find('last_login_date IS NOT NULL'); $user->softDelete(); // marks record as "deleted"
\yii1tech\ar\softdelete\SoftDeleteBehavior::$allowDeleteCallback
逻辑在\yii1tech\ar\softdelete\SoftDeleteBehavior::$replaceRegularDelete
也启用时应用。
处理外键约束
在关系型数据库的用法中,例如MySQL、PostgreSQL等,通常使用“软删除”来保持外键一致性。例如:如果用户在在线商店进行购买,则此购买信息应保留在系统中以供未来记账。此类数据结构的DDL可能如下所示
CREATE TABLE `customer` ( `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 `purchase` ( `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 `customer` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, FOREIGN KEY (`item_id`) REFERENCES `item` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE, ) ENGINE InnoDB;
因此,当从'purchase'到'user'设置外键时,使用'ON DELETE RESTRICT'模式。因此,尝试删除至少有一笔购买的记录时,将发生数据库错误。然而,如果用户记录没有外部引用,则可以将其删除。
对于这种情况,使用\yii1tech\ar\softdelete\SoftDeleteBehavior::$allowDeleteCallback
并不是非常实用。它将需要执行额外的查询以确定是否存在外部引用,从而消除外键数据库功能的优势。
方法\yii1tech\ar\softdelete\SoftDeleteBehavior::safeDelete()
尝试调用常规的CBaseActiveRecord::delete()
方法,如果它因异常失败,则回退到yii1tech\ar\softdelete\SoftDeleteBehavior::softDelete()
。
<?php // if there is a foreign key reference : $customer = Customer::model()->findByPk(15); var_dump(count($customer->purchases)); // outputs; "1" $customer->safeDelete(); // performs "soft" delete! var_dump($customer->isDeleted) // outputs: "true" // if there is NO foreign key reference : $customer = Customer::model()->findByPk(53); var_dump(count($customer->purchases)); // outputs; "0" $customer->safeDelete(); // performs actual delete! $customer = Customer::model()->findByPk(53); var_dump($customer); // outputs: "null"
默认情况下,safeDelete()
方法捕获\CDbException
异常,这意味着在违反外键约束的数据库异常时执行软删除。您可以在此处指定另一个异常类以自定义回退错误级别。例如:使用\Throwable
将导致在常规删除过程中遇到任何错误时回退到软删除。
记录恢复
在某个时候,您可能想要“恢复”以前标记为“已删除”的记录。您可以使用restore()
方法进行此操作
<?php $id = 17; $item = Item::model()->findByPk($id); $item->softDelete(); // mark record as "deleted" $item = Item::model()->findByPk($id); $item->restore(); // restore record var_dump($item->is_deleted); // outputs "false"
默认情况下,用于记录恢复的属性值会自动从\yii1tech\ar\softdelete\SoftDeleteBehavior::$softDeleteAttributeValues
中检测,但最好您通过\yii1tech\ar\softdelete\SoftDeleteBehavior::$restoreAttributeValues
明确指定它们。
提示:如果您启用
\yii1tech\ar\softdelete\SoftDeleteBehavior::$useRestoreAttributeValuesAsDefaults
,则标记已恢复记录的属性值将在新记录插入时自动应用。
事件
默认情况下,\yii1tech\ar\softdelete\SoftDeleteBehavior::softDelete()
以与常规delete()
相同的方式触发\CActiveRecord::onBeforeDelete
和\CActiveRecord::onAfterDelete
事件。
此外,\yii1tech\ar\softdelete\SoftDeleteBehavior
允许您在所有者ActiveRecord类中定义特定方法,以挂钩到软删除过程
beforeSoftDelete()
- 在“软删除”之前触发。afterSoftDelete()
- 在“软删除”之后触发。beforeRestore()
- 在从“已删除”状态恢复记录之前触发。afterRestore()
- 在从“已删除”状态恢复记录之后触发。
例如
<?php use CActiveRecord; use yii1tech\ar\softdelete\SoftDeleteBehavior; class Item extends CActiveRecord { public function behaviors() { return [ 'softDeleteBehavior' => [ 'class' => SoftDeleteBehavior::class, // ... ], ]; } public function beforeSoftDelete(): bool { $this->deleted_at = time(); // log the deletion date return true; } public function beforeRestore(): bool { return $this->deleted_at > (time() - 3600); // allow restoration only for the records, being deleted during last hour } }