diff --git a/README.md b/README.md index cbe93e8..1a083ca 100644 --- a/README.md +++ b/README.md @@ -90,4 +90,25 @@ SELECT * FROM users WHERE is_del = 0; // Delete UPDATE users SET is_del = 1, deleted_at = /* current unix milli second second*/ WHERE ID = 1; -``` \ No newline at end of file +``` + +## Mixed Mode with Delete ID Field +#### Maintaining Unique Key Integrity +This allows you to record the original ID of a deleted record in another field. By doing so, you can maintain the integrity of unique keys by allowing new records with the same unique key to be inserted without conflict. +#### Example +Assume we have a User model where the Email field needs to be unique. By storing the original ID in the DeletedId field and creating a composite unique key with Email and DeletedId, you can insert a new record without violating the unique constraint even after soft deleting an existing record. +```go +type User struct { +ID uint +Name string +Email string +DeletedId uint // Stores the original ID of the deleted record +IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedIDField:DeletedId,DeletedIDFromField:ID"` // use `1` `0` +} + +// Query +SELECT * FROM users WHERE is_del = 0; + +// Delete +UPDATE users SET is_del = 1, deleted_id = /* value from ID */ WHERE ID = 1; +``` diff --git a/soft_delete.go b/soft_delete.go index 8083be7..c3c4a16 100644 --- a/soft_delete.go +++ b/soft_delete.go @@ -73,6 +73,12 @@ func (DeletedAt) DeleteClauses(f *schema.Field) []clause.Interface { if v := settings["DELETEDATFIELD"]; v != "" { // DeletedAtField softDeleteClause.DeleteAtField = f.Schema.LookUpField(v) } + if v := settings["DELETEDIDFIELD"]; v != "" { // DeleteIdField + softDeleteClause.DeleteIdField = f.Schema.LookUpField(v) + } + if v := settings["DELETEDIDFROMFIELD"]; v != "" { // DeleteIdFromField + softDeleteClause.DeleteIdFromField = f.Schema.LookUpField(v) + } return []clause.Interface{softDeleteClause} } @@ -101,10 +107,12 @@ func (sd SoftDeleteUpdateClause) ModifyStatement(stmt *gorm.Statement) { } type SoftDeleteDeleteClause struct { - Field *schema.Field - Flag bool - TimeType schema.TimeType - DeleteAtField *schema.Field + Field *schema.Field + Flag bool + TimeType schema.TimeType + DeleteAtField *schema.Field + DeleteIdField *schema.Field + DeleteIdFromField *schema.Field } func (sd SoftDeleteDeleteClause) Name() string { @@ -135,6 +143,14 @@ func (sd SoftDeleteDeleteClause) ModifyStatement(stmt *gorm.Statement) { stmt.SetColumn(deleteAtField.DBName, value, true) } + deleteIdField := sd.DeleteIdField + deleteIdFromField := sd.DeleteIdFromField + + if deleteIdField != nil && deleteIdFromField != nil { + set = append(set, clause.Assignment{Column: clause.Column{Name: deleteIdField.DBName}, Value: gorm.Expr(deleteIdFromField.DBName)}) + stmt.SetColumn(deleteIdField.DBName, gorm.Expr(deleteIdFromField.DBName), true) + } + if sd.Flag { set = append(clause.Set{{Column: clause.Column{Name: sd.Field.DBName}, Value: FlagDeleted}}, set...) stmt.SetColumn(sd.Field.DBName, FlagDeleted, true)