orm

package
v0.23.0 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package orm provides an ActiveRecord-style Model, a fluent query builder, hooks, casts, and relationships. Models are plain Go structs that embed orm.Model (or one of the timestamp variants) and live without a global router — the only required dependency is a *database.Connection.

Example:

type User struct {
    orm.Model
    Name  string
    Email string `orm:"unique"`
}

user := &User{Name: "Ada", Email: "ada@example.com"}
if err := orm.Save(ctx, conn, user); err != nil { ... }

var users []User
err := orm.Query[User](conn).Where("email", "like", "%@example.com").Get(ctx, &users)

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = errors.New("orm: record not found")

ErrNotFound is returned by First/FirstOrFail when no row matches.

Functions

func Delete

func Delete[T any](ctx context.Context, conn *database.Connection, model *T) error

Delete removes the model's row from the database. For soft-deletable models (those carrying a deleted_at column) it issues an UPDATE that stamps deleted_at instead of a real DELETE; use ForceDelete for an unconditional removal.

func FirstOrCreate added in v0.21.0

func FirstOrCreate[T any](ctx context.Context, conn *database.Connection, where map[string]any, defaults ...map[string]any) (*T, error)

FirstOrCreate returns the first row matching where, or inserts a new row merging where and the optional defaults (defaults override where on key collisions). The returned model reflects the persisted row, including a generated primary key on the create path.

func ForceDelete

func ForceDelete[T any](ctx context.Context, conn *database.Connection, model *T) error

ForceDelete permanently removes the model's row, bypassing soft deletes.

func Pluck

func Pluck[T any, V any](ctx context.Context, b *Builder[T], col string) ([]V, error)

Pluck extracts a single column as []V.

func Restore

func Restore[T any](ctx context.Context, conn *database.Connection, model *T) error

Restore clears the deleted_at column of a soft-deleted model, bringing the row back into the default query scope. It is an error to call Restore on a model that is not soft-deletable.

func Save

func Save[T any](ctx context.Context, conn *database.Connection, model *T) error

Save persists a model: it inserts when the primary key is zero, otherwise updates. Hooks are dispatched around the operation. Timestamps are set automatically.

func UpdateOrCreate added in v0.21.0

func UpdateOrCreate[T any](ctx context.Context, conn *database.Connection, where map[string]any, values map[string]any) (*T, error)

UpdateOrCreate updates the first row matching where with values, or inserts a new row merging where and values when no match exists. The returned model reflects the persisted row.

Types

type AfterCreateHook

type AfterCreateHook interface{ AfterCreate(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type AfterDeleteHook

type AfterDeleteHook interface{ AfterDelete(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type AfterFindHook

type AfterFindHook interface{ AfterFind(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type AfterSaveHook

type AfterSaveHook interface{ AfterSave(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type AfterUpdateHook

type AfterUpdateHook interface{ AfterUpdate(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type BeforeCreateHook

type BeforeCreateHook interface{ BeforeCreate(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type BeforeDeleteHook

type BeforeDeleteHook interface{ BeforeDelete(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type BeforeSaveHook

type BeforeSaveHook interface{ BeforeSave(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type BeforeUpdateHook

type BeforeUpdateHook interface{ BeforeUpdate(*HookContext) error }

BeforeCreate / AfterCreate / etc. are optional hook interfaces.

type Builder

type Builder[T any] struct {
	// contains filtered or unexported fields
}

Builder is a model-aware wrapper around query.Builder.

func Query

func Query[T any](conn *database.Connection) *Builder[T]

Query returns a fresh model builder.

func (*Builder[T]) Chunk added in v0.21.0

func (b *Builder[T]) Chunk(ctx context.Context, size int, fn func([]T) error) error

Chunk iterates the result set in batches of size, invoking fn for each non-empty batch. It paginates by ascending primary key (a keyset cursor) so the whole set is never loaded into memory at once. Returning a non-nil error from fn stops the iteration and propagates the error. size is clamped to a minimum of 1.

func (*Builder[T]) Count

func (b *Builder[T]) Count(ctx context.Context) (int64, error)

Count rows matching the query (honoring the soft-delete scope).

func (*Builder[T]) Exists

func (b *Builder[T]) Exists(ctx context.Context) (bool, error)

Exists reports whether any matching row exists (honoring the soft-delete scope).

func (*Builder[T]) Find

func (b *Builder[T]) Find(ctx context.Context, id any) (*T, error)

Find by primary key. It clones the underlying builder so repeated Find calls on the same receiver (Find(id1) then Find(id2)) do not accumulate WHERE clauses.

func (*Builder[T]) First

func (b *Builder[T]) First(ctx context.Context) (*T, error)

First returns the first matching row, or ErrNotFound. It runs against a clone of the underlying builder so the receiver keeps its full clause state and can still be used for Get/Count afterwards.

func (*Builder[T]) FirstOrFail added in v0.20.0

func (b *Builder[T]) FirstOrFail(ctx context.Context) (*T, error)

FirstOrFail is an alias for First: it returns the first matching row or ErrNotFound. Provided for call-site readability when the absence of a row is an error condition.

func (*Builder[T]) Get

func (b *Builder[T]) Get(ctx context.Context, dst *[]T) error

Get executes the query and populates dst (must be *[]T).

func (*Builder[T]) Limit

func (b *Builder[T]) Limit(n int) *Builder[T]

Limit applies a LIMIT.

func (*Builder[T]) Offset

func (b *Builder[T]) Offset(n int) *Builder[T]

Offset applies an OFFSET.

func (*Builder[T]) OnlyTrashed

func (b *Builder[T]) OnlyTrashed() *Builder[T]

OnlyTrashed restricts the results to soft-deleted rows only. No-op on models that are not soft-deletable.

func (*Builder[T]) OrWhere

func (b *Builder[T]) OrWhere(args ...any) *Builder[T]

OrWhere appends an OR WHERE.

func (*Builder[T]) OrderBy

func (b *Builder[T]) OrderBy(col, dir string) *Builder[T]

OrderBy appends ORDER BY.

func (*Builder[T]) Paginate added in v0.21.0

func (b *Builder[T]) Paginate(ctx context.Context, page, perPage int) (*Paginator[T], error)

Paginate runs a COUNT (respecting the current WHERE and soft-delete scope) followed by a LIMIT/OFFSET query, returning the requested page. page and perPage are clamped to a minimum of 1.

func (*Builder[T]) QB

func (b *Builder[T]) QB() *query.Builder

Raw query builder access.

func (*Builder[T]) Scope added in v0.21.0

func (b *Builder[T]) Scope(fn func(*Builder[T]) *Builder[T]) *Builder[T]

Scope applies a reusable constraint to the builder and returns it, so common query fragments can be factored into a named function:

active := func(q *orm.Builder[User]) *orm.Builder[User] { return q.Where("active", true) }
orm.Query[User](conn).Scope(active).Get(ctx, &users)

func (*Builder[T]) Where

func (b *Builder[T]) Where(args ...any) *Builder[T]

Where appends a WHERE clause.

func (*Builder[T]) WhereIn

func (b *Builder[T]) WhereIn(col string, values any) *Builder[T]

WhereIn appends an IN constraint.

func (*Builder[T]) WhereNotNull

func (b *Builder[T]) WhereNotNull(col string) *Builder[T]

WhereNotNull appends IS NOT NULL.

func (*Builder[T]) WhereNull

func (b *Builder[T]) WhereNull(col string) *Builder[T]

WhereNull appends an IS NULL constraint.

func (*Builder[T]) With added in v0.22.0

func (b *Builder[T]) With(names ...string) *Builder[T]

With records relations to eager-load after the base query returns. Names may be dotted to load nested relations in one call: With("posts", "posts.comments") loads each parent's posts, then loads the comments of every loaded post — one query per relation level, never N+1. Duplicate names are de-duplicated; the order of first appearance is preserved.

func (*Builder[T]) WithTrashed

func (b *Builder[T]) WithTrashed() *Builder[T]

WithTrashed includes soft-deleted rows in the results. No-op on models that are not soft-deletable.

func (*Builder[T]) WithTx

func (b *Builder[T]) WithTx(tx *database.Tx) *Builder[T]

WithTx pins the builder to a transaction.

func (*Builder[T]) WithWhere added in v0.22.0

func (b *Builder[T]) WithWhere(name string, constraint func(qb *query.Builder)) *Builder[T]

WithWhere is the constrained variant of With: it eager-loads a single relation while applying a typed constraint to the relation's query. The constraint receives the raw *query.Builder for the related table after the foreign-key filter (and soft-delete scope) have been applied, so callers add WHERE/ORDER BY/LIMIT clauses on top:

orm.Query[User](conn).
    WithWhere("posts", func(q *query.Builder) { q.Where("published", "=", true).OrderBy("id", "desc") }).
    Get(ctx, &users)

Nested constraints are not expressed through this method; combine it with plain With for the nested levels.

type Connectioner

type Connectioner interface {
	ConnectionName() string
}

Connectioner may be implemented on a model to pin it to a specific named connection from the database.Manager. Useful in multi-tenant or multi-database setups.

type Fillable

type Fillable interface {
	FillableColumns() []string
}

Fillable may be implemented to whitelist columns for mass assignment.

type Hidden

type Hidden interface {
	HiddenColumns() []string
}

Hidden may be implemented to hide columns from ToMap serialization.

type HookContext

type HookContext struct {
	Ctx  context.Context
	Conn *database.Connection
	Tx   *database.Tx
}

HookContext is passed to lifecycle hooks. It exposes the active transaction-or-connection executor and the underlying context.Context.

func (*HookContext) Executor

func (h *HookContext) Executor() database.Executor

Executor returns the active executor, preferring the transaction if open.

type Model

type Model struct {
	ID        uint64    `column:"id" orm:"primary;autoincrement"`
	CreatedAt time.Time `column:"created_at"`
	UpdatedAt time.Time `column:"updated_at"`
}

Model is the base struct most models embed. It provides the standard id/created_at/updated_at columns.

type Paginator added in v0.21.0

type Paginator[T any] struct {
	Data     []T   `json:"data"`
	Total    int64 `json:"total"`
	Page     int   `json:"page"`
	PerPage  int   `json:"per_page"`
	LastPage int   `json:"last_page"`
	HasMore  bool  `json:"has_more"`
}

Paginator is the result of Builder.Paginate. Data holds the current page's rows; the remaining fields describe the position within the full result set.

type RelationDef added in v0.22.0

type RelationDef struct {
	Kind       RelationKind
	Field      string // destination struct field on the parent model
	Related    any    // zero value of the related model type, e.g. Post{}
	ForeignKey string
	OwnerKey   string

	// Pivot configuration (BelongsToMany only).
	PivotTable     string
	PivotForeignFK string
	PivotRelatedFK string

	// Polymorphic configuration (Morph* only): MorphName yields the
	// "<name>_id" / "<name>_type" columns, MorphValue the type discriminator.
	MorphName  string
	MorphValue string
}

RelationDef declares a single named relationship on a model. A model exposes its relations by implementing WithRelations; the ORM resolves a string name passed to Builder.With into the matching RelationDef and batch-loads it.

Field is the destination struct field (Go field name) the loaded rows are written into — a slice field for the *Many kinds, a struct or *struct field for the single-row kinds. Related is a zero value of the related model type (e.g. Post{}); its schema supplies the related table and is used to allocate the destination rows.

The key fields follow the same convention as the relations package: an empty ForeignKey/OwnerKey is not inferred here — declare them explicitly. ForeignKey is the child column for Has*/Morph* (e.g. "user_id"), or the parent's own column for BelongsTo. OwnerKey defaults to "id" when empty.

type RelationKind added in v0.22.0

type RelationKind int

RelationKind enumerates the relationship kinds an eager load can resolve. It mirrors relations.Kind but is re-exported here so model authors declare relations without importing the relations package directly.

const (
	// HasOne: one child row whose foreign key points back at this model's key.
	HasOne RelationKind = iota
	// HasMany: many child rows keyed by a foreign key back to this model.
	HasMany
	// BelongsTo: the parent row this model's foreign key points at.
	BelongsTo
	// BelongsToMany: many related rows reached through a pivot table.
	BelongsToMany
	// MorphOne: a single polymorphic child (morph_type/morph_id columns).
	MorphOne
	// MorphMany: many polymorphic children (morph_type/morph_id columns).
	MorphMany
)

type SoftDeletes added in v0.21.0

type SoftDeletes struct {
	ID        uint64     `column:"id" orm:"primary;autoincrement"`
	CreatedAt time.Time  `column:"created_at"`
	UpdatedAt time.Time  `column:"updated_at"`
	DeletedAt *time.Time `column:"deleted_at"`
}

SoftDeletes is a base struct that adds a nullable deleted_at column on top of the standard id/created_at/updated_at columns. A model embedding it becomes "soft-deletable": orm.Delete sets deleted_at instead of issuing a real DELETE, and query builders exclude soft-deleted rows by default. Use orm.ForceDelete for a real delete and orm.Restore to bring a row back.

type Tabler

type Tabler interface {
	TableName() string
}

Tabler may be implemented on a model to override the inferred table name.

type Timestamps

type Timestamps struct {
	ID        uint64    `column:"id" orm:"primary;autoincrement"`
	CreatedAt time.Time `column:"created_at"`
	UpdatedAt time.Time `column:"updated_at"`
}

Timestamps is an alias for Model kept for compatibility with code that distinguished it from a soft-delete-aware base.

type WithRelations added in v0.22.0

type WithRelations interface {
	Relations() map[string]RelationDef
}

WithRelations is implemented by models that support eager loading via Builder.With. It returns a map from relation name to its declaration.

func (User) Relations() map[string]orm.RelationDef {
    return map[string]orm.RelationDef{
        "posts": {Kind: orm.HasMany, Field: "Posts", Related: Post{}, ForeignKey: "user_id"},
    }
}

Jump to

Keyboard shortcuts

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