soft-deletes

command
v0.37.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2026 License: AGPL-3.0 Imports: 7 Imported by: 0

README

Soft Deletes Example

This example demonstrates the NULL-based soft delete strategy, the traditional approach for implementing soft deletes using NULL values to indicate active records.

Overview

The NULL-based soft delete strategy uses NULL to indicate "not deleted" records and a timestamp to indicate "soft deleted" records. Records are considered:

  • Active when soft_deleted_at IS NULL
  • Soft Deleted when soft_deleted_at IS NOT NULL (contains the deletion timestamp)

Features Demonstrated

  • Using SoftDeletes embed for NULL-based soft deletes
  • Creating tables with nullable soft_deleted_at columns
  • Soft deleting records (sets soft_deleted_at = NOW())
  • Querying active records (default, excludes soft deleted)
  • Including soft deleted records with WithSoftDeleted()
  • Querying only soft deleted records with OnlySoftDeleted()
  • Restoring soft deleted records (sets deleted_at = NULL)
  • Force deleting records permanently with ForceDelete()

Benefits of NULL-Based Strategy

  1. Simple and Intuitive: NULL = active, non-NULL = deleted is easy to understand
  2. Widely Used: Common pattern in many ORMs (Laravel, Django, etc.)
  3. Easy to Debug: NULL values are immediately recognizable in database tools
  4. No Special Values: No need for sentinel values or magic dates

Usage

Model Definition
import "github.com/dracory/neat/database/soft_delete"

type Product struct {
    soft_delete.SoftDeletes  // Uses NULL-based strategy
    ID          uint    `json:"id" db:"id"`
    Name        string  `json:"name" db:"name"`
    Price       float64 `json:"price" db:"price"`
}
Schema Definition
import "github.com/dracory/neat/database/schema/constants"

db.Schema().Create("products", func(blueprint schema.Blueprint) {
    blueprint.ID()
    blueprint.String("name")
    blueprint.Float("price", 10, 2)
    // For NULL-based strategy, soft_deleted_at should be nullable
    blueprint.Timestamp(constants.SoftDeleteAtColumn).Nullable()
})
Basic Operations
// Create a product (deleted_at is NULL by default)
db.Query().Table("products").Create(map[string]any{
    "name": "Laptop",
    "price": 999.99,
})

// Soft delete (sets deleted_at = NOW())
db.Query().Model(&Product{}).Where("name = ?", "Laptop").Delete()

// Query active products (excludes soft deleted - default behavior)
var products []Product
db.Query().Model(&Product{}).Get(&products)

// Include soft deleted products
db.Query().Model(&Product{}).WithSoftDeleted().Get(&products)

// Only soft deleted products
db.Query().Model(&Product{}).OnlySoftDeleted().Get(&products)

// Restore a soft deleted product (sets deleted_at = NULL)
db.Query().Model(&Product{}).Where("id = ?", 1).RestoreSoftDeleted()

// Permanently delete (bypasses soft delete)
db.Query().Model(&Product{}).Where("id = ?", 1).ForceDelete()
Checking Soft Delete Status
var product Product
db.Query().Model(&Product{}).First(&product)

if product.IsSoftDeleted() {
    fmt.Println("This product is soft deleted")
    fmt.Printf("Soft deleted at: %v\n", product.SoftDeletedAt)
}

Using Constants

The neat package provides constants for all default column names to avoid hardcoding:

  • constants.SoftDeleteAtColumn - Default soft delete column name ("soft_deleted_at")
  • constants.DeletedAtColumnName - Laravel-compatible column name ("deleted_at")
  • constants.MaxSoftDeletedAtDefault - Max-date sentinel value ("9999-12-31 23:59:59")
  • constants.DefaultIDColumn - Default ID column name ("id")
  • constants.DefaultCreatedAtColumn - Default created_at column name ("created_at")
  • constants.DefaultUpdatedAtColumn - Default updated_at column name ("updated_at")

Import the constants package to use them:

import "github.com/dracory/neat/database/schema/constants"

Alternative: DeletedAt

For Laravel-compatible schemas that use deleted_at column name:

type Post struct {
    soft_delete.DeletedAt  // Uses "deleted_at" column (Laravel-compatible)
    ID    uint
    Title string
}

Running the Example

cd examples/soft-deletes
go run main.go

Or run the tests:

go test -v

Comparison with Max-Date Strategy

Aspect NULL-Based (SoftDeletes) Max-Date (SoftDeletesMaxDate)
"Not deleted" value NULL 9999-12-31 23:59:59
Column name soft_deleted_at soft_deleted_at
Column constraint Nullable NOT NULL (recommended)
Default value NULL 9999-12-31 23:59:59
Active query condition IS NULL > NOW()
Deleted query condition IS NOT NULL <= NOW()
Index efficiency Varies by database Better range scans
NOT NULL compatible No Yes
Simplicity Very simple Slightly more complex

When to Use NULL-Based Strategy

  • When you need simplicity and clarity
  • When NOT NULL constraints are not required
  • When you want the most common and widely-understood pattern
  • When database index performance is not a critical concern

When to Consider Max-Date Strategy

  • When you need NOT NULL constraints on the soft delete column
  • When you need better index performance on large datasets
  • When working with databases that have poor NULL index handling
  • When you want to avoid NULL handling complexity in queries

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL