orm

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package orm is a Lucid-style model layer: a generic repository over GORM models.

Relations, hooks (BeforeCreate, ...) and soft deletes use GORM's native struct tags, interfaces and gorm.DeletedAt; Query.Preload exposes eager loading.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bind

func Bind[T any](db *migro.DB, v *T) *T

Bind attaches v (a struct pointer) to db so its instance methods and relations work. A model with a non-zero primary key is treated as already persisted.

func CompareHash

func CompareHash(hash, plain string) bool

CompareHash reports whether plain matches a bcrypt hash made by CastHashed.

func EnforceMorphMap

func EnforceMorphMap(m map[string]any)

EnforceMorphMap is MorphMap that also refuses models missing from the map (Laravel's Relation::enforceMorphMap): without it an unmapped model silently stores its type name, and renaming the Go type later orphans every row.

func Fresh

func Fresh[T any](ctx context.Context, v *T) (*T, error)

Fresh returns a newly loaded copy of v, leaving v untouched.

func Is

func Is(a, b any) bool

Is reports whether a and b are the same row: the same model type and the same non-zero primary key (Eloquent's is). IsNot is its negation.

func IsNot

func IsNot(a, b any) bool

func JSON

func JSON(v any) ([]byte, error)

JSON is json.Marshal over ToMap.

func JSONWith

func JSONWith(v any, opts ...SerializeOpt) ([]byte, error)

JSONWith is JSON with per-call options.

func Load

func Load[T any](ctx context.Context, db *migro.DB, v *T, relations ...string) error

Load eager-loads relations onto an already fetched model (Eloquent's $model->load()).

func LoadAvg

func LoadAvg[T any](ctx context.Context, db *migro.DB, v *T, relation, column string) error

func LoadCount

func LoadCount[T any](ctx context.Context, db *migro.DB, v *T, relations ...string) error

LoadCount, LoadSum, LoadAvg, LoadMin, LoadMax and LoadExists compute the same aggregates for models already fetched (Eloquent's loadCount, loadSum, ...).

func LoadExists

func LoadExists[T any](ctx context.Context, db *migro.DB, v *T, relation string) error

func LoadMax

func LoadMax[T any](ctx context.Context, db *migro.DB, v *T, relation, column string) error

func LoadMin

func LoadMin[T any](ctx context.Context, db *migro.DB, v *T, relation, column string) error

func LoadSum

func LoadSum[T any](ctx context.Context, db *migro.DB, v *T, relation, column string) error

func MapTo

func MapTo[T, R any](c Collection[T], fn func(m *T) R) []R

MapTo transforms every model into another type. It is a function because Go methods cannot introduce a type parameter.

func MorphMap

func MorphMap(m map[string]any)

MorphMap registers the aliases stored in *_type columns (Laravel's Relation::morphMap):

orm.MorphMap(map[string]any{"post": Post{}, "video": Video{}})

Models missing from the map use their snake_case type name as alias.

func NewULID

func NewULID() string

NewULID returns a ULID: 48 bits of millisecond time and 80 random bits in Crockford base32.

func NewUUID

func NewUUID() string

NewUUID returns a UUIDv7: a millisecond timestamp followed by random bits, so ids sort by creation.

func Pluck

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

Pluck returns one column of every matching row (Eloquent's pluck).

func PluckAs

func PluckAs[T, V any](c Collection[T], col string) ([]V, error)

PluckAs is Pluck with the values asserted to V; a value of another type is a CAST_FAILED error.

func PruneAll

func PruneAll(ctx context.Context, db *migro.DB) (map[string]int64, error)

PruneAll prunes every registered model, stopping at the first failure. It returns the count per name.

func Push

func Push[T any](ctx context.Context, v *T) error

Push saves v and every relation loaded on it, depth first (Eloquent's push): a loaded belongs-to parent is saved first and v's foreign key follows it; loaded children are linked to v and saved after it. Everything runs in one transaction.

func Reduce

func Reduce[T, A any](c Collection[T], initial A, fn func(acc A, m *T) A) A

Reduce folds the collection into one value.

func RegisterMorph

func RegisterMorph[T any](r *MorphRegistry, db *migro.DB, alias string) error

RegisterMorph registers model T under alias (e.g. "post"). T must have a single primary key.

func RegisterPrunable

func RegisterPrunable[T any](name string)

RegisterPrunable lets PruneAll (and the model:prune command) find T.

func Replicate

func Replicate[T any](ctx context.Context, v *T, except ...string) (*T, error)

Replicate returns an unsaved copy of v (Eloquent's replicate): the primary key, creation and update times, the soft-delete mark, loaded relations and the columns named in except are cleared. The copy is bound to the same database, so Save inserts it. before:replicate listeners may abort it.

func ToMap

func ToMap(v any) (map[string]any, error)

ToMap serializes a bound model the way Eloquent's toArray does: column names as keys, casts' Get applied, Hidden / Visible applied, Appends added, loaded relations nested, extras (counts, pivot) included.

func ToMapWith

func ToMapWith(v any, opts ...SerializeOpt) (map[string]any, error)

ToMapWith is ToMap with per-call options.

func Value

func Value[T, V any](ctx context.Context, q *Query[T], col string) (V, error)

Value returns one column of the first matching row, or NOT_FOUND.

Types

type Appends

type Appends interface{ Appends() map[string]any }

Appends returns computed values added to ToMap / JSON (Eloquent's $appends).

type BaseModel

type BaseModel struct {
	Record
	ID        uint `gorm:"primaryKey"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

BaseModel is an optional embeddable base with an auto-increment uint key, like Lucid's BaseModel.

type BelongsTo

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

func (*BelongsTo[T]) Associate

func (r *BelongsTo[T]) Associate(parent *T) error

Associate points the owner's foreign key at parent (it does not save the owner).

func (*BelongsTo[T]) Dissociate

func (r *BelongsTo[T]) Dissociate() error

Dissociate clears the owner's foreign key (the column must be nullable; it does not save the owner).

func (*BelongsTo) Get

func (o *BelongsTo) Get(ctx context.Context) (*T, error)

Get returns the related row (nil when there is none), loading it on first use.

func (*BelongsTo) Item

func (o *BelongsTo) Item() *T

Item returns the loaded row, or nil. A relation tagged orm:"withDefault" answers with an empty, unsaved model instead of nil once it has been loaded.

func (*BelongsTo) Query

func (o *BelongsTo) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*BelongsTo) Value

func (o *BelongsTo) Value() (driver.Value, error)

Value makes the relation a driver.Valuer that always yields nil. GORM then merges the `gorm` tags of the relation's inner fields into the field's own settings, and the on those inner fields hides the relation from GORM without the user tagging every field.

type BelongsToMany

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

func (*BelongsToMany[T]) Attach

func (r *BelongsToMany[T]) Attach(ctx context.Context, ids ...any) error

Attach links the given keys (or models), skipping links that already exist.

func (*BelongsToMany[T]) AttachWith

func (r *BelongsToMany[T]) AttachWith(ctx context.Context, attrs map[string]any, ids ...any) error

AttachWith links keys and stores extra pivot columns on the new rows.

func (*BelongsToMany[T]) Detach

func (r *BelongsToMany[T]) Detach(ctx context.Context, ids ...any) error

func (*BelongsToMany) Get

func (m *BelongsToMany) Get(ctx context.Context) (Collection[T], error)

Get returns the related rows, loading them on first use.

func (*BelongsToMany) Items

func (m *BelongsToMany) Items() Collection[T]

Items returns the loaded rows (empty until loaded; see Loaded).

func (*BelongsToMany) Query

func (m *BelongsToMany) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*BelongsToMany[T]) Sync

func (r *BelongsToMany[T]) Sync(ctx context.Context, ids ...any) (SyncResult, error)

Sync makes the links exactly ids; SyncWithoutDetaching only adds.

func (*BelongsToMany[T]) SyncWithoutDetaching

func (r *BelongsToMany[T]) SyncWithoutDetaching(ctx context.Context, ids ...any) (SyncResult, error)

func (*BelongsToMany[T]) Toggle

func (r *BelongsToMany[T]) Toggle(ctx context.Context, ids ...any) (SyncResult, error)

func (*BelongsToMany[T]) UpdateExistingPivot

func (r *BelongsToMany[T]) UpdateExistingPivot(ctx context.Context, id any, attrs map[string]any) error

func (*BelongsToMany) Value

func (m *BelongsToMany) Value() (driver.Value, error)

Value hides the relation from GORM; see one.Value.

type Cast

type Cast interface {
	Get(raw any) (any, error)
	Set(v any) (any, error)
}

Casts are Eloquent's mutators and accessors in one place. A model lists them per column:

func (User) Casts() map[string]orm.Cast {
    return map[string]orm.Cast{"password": orm.CastHashed(), "settings": orm.CastJSON()}
}

Set runs when a value enters the model through Fill, Update, Make, CreateFrom or SetAttribute; Get runs when it leaves through ToMap, JSON or GetAttribute. Plain Go assignment (user.Name = "x") and Save are untouched: the struct field always holds the stored value.

func CastBool

func CastBool() Cast

CastBool coerces on both sides: "1", "true", "on", "yes" and non-zero numbers are true.

func CastDate

func CastDate(layout string) Cast

CastDate parses strings into time.Time on the way in and formats times on the way out, using layout (Eloquent's date / datetime:Y-m-d cast).

func CastEncrypted

func CastEncrypted(key []byte) Cast

CastEncrypted encrypts a string with AES-GCM on Set and decrypts it on Get (Eloquent's encrypted cast). key must be 16, 24 or 32 bytes; a wrong size fails every use with CAST_FAILED. The stored value is base64 of nonce+ciphertext, so the column must be text. Changing the key makes existing rows unreadable.

func CastEnum

func CastEnum(values ...string) Cast

CastEnum rejects any value outside values with CAST_FAILED before the row is written.

func CastFloat

func CastFloat() Cast

CastFloat coerces numeric strings and integers to float64 on both sides.

func CastHashed

func CastHashed() Cast

CastHashed bcrypt-hashes a string on Set (Eloquent's hashed cast). A value that already is a bcrypt hash is kept, so re-filling a loaded model does not hash twice. Get returns the hash: list the column in Hidden. Verify with CompareHash.

func CastHashedCost

func CastHashedCost(cost int) Cast

CastHashedCost is CastHashed with an explicit bcrypt cost.

func CastInt

func CastInt() Cast

CastInt coerces numeric strings and floats to int64 on both sides.

func CastJSON

func CastJSON() Cast

CastJSON stores any value as JSON text in a string column (Eloquent's array / json cast). Set encodes, Get decodes into map[string]any, []any or a scalar. For a typed value use JSONColumn[T].

type CastFunc

type CastFunc struct {
	OnGet func(raw any) (any, error)
	OnSet func(v any) (any, error)
}

CastFunc builds a cast from two functions; a nil side passes the value through. It is the way to write a custom accessor or mutator.

"email": orm.CastFunc{OnSet: func(v any) (any, error) { return strings.ToLower(fmt.Sprint(v)), nil }}

func (CastFunc) Get

func (c CastFunc) Get(raw any) (any, error)

func (CastFunc) Set

func (c CastFunc) Set(v any) (any, error)

type Casted

type Casted interface{ Casts() map[string]Cast }

Casted is implemented by models that declare casts, keyed by column name.

type Collection

type Collection[T any] []T

Collection is a list of models with Eloquent's collection helpers. It is a defined slice type, so it indexes, ranges and appends like []T, and converts to and from []T freely.

Methods that read a column or the primary key need bound models (rows loaded through a query or a relation); on a collection of hand-built structs they return UNBOUND_MODEL.

func (Collection[T]) Contains

func (c Collection[T]) Contains(id any) (bool, error)

Contains reports whether a model with that primary key is in the collection.

func (Collection[T]) Count

func (c Collection[T]) Count() int

Count is the number of models.

func (Collection[T]) Each

func (c Collection[T]) Each(fn func(i int, m *T) error) error

Each calls fn for every model; returning an error stops the walk.

func (Collection[T]) Except

func (c Collection[T]) Except(ids ...any) (Collection[T], error)

Except drops the models whose primary key is in ids.

func (Collection[T]) Filter

func (c Collection[T]) Filter(keep func(m *T) bool) Collection[T]

Filter keeps the models for which keep returns true. The result holds copies that stay bound to the database (Save and lazy relations work on them), but edits to a copy do not show in the original collection.

func (Collection[T]) Find

func (c Collection[T]) Find(id any) (*T, error)

Find returns the model with the given primary key, or nil. Keys compare by printed value, so 1, uint(1) and "1" match.

func (Collection[T]) First

func (c Collection[T]) First() *T

First returns a pointer to the first model, or nil.

func (Collection[T]) Fresh

func (c Collection[T]) Fresh(ctx context.Context) (Collection[T], error)

Fresh reloads the models from the database, keeping their order. Rows that no longer exist are dropped.

func (Collection[T]) GroupBy

func (c Collection[T]) GroupBy(col string) (map[any]Collection[T], error)

GroupBy buckets the models by a column; NULLs are skipped.

func (Collection[T]) IsEmpty

func (c Collection[T]) IsEmpty() bool

IsEmpty reports whether there are no models.

func (Collection[T]) JSON

func (c Collection[T]) JSON(opts ...SerializeOpt) ([]byte, error)

JSON encodes the collection as a JSON array of serialized models.

func (Collection[T]) KeyBy

func (c Collection[T]) KeyBy(col string) (map[any]T, error)

KeyBy indexes the models by a column. The map holds plain copies of the structs. The key keeps the field's Go type (uint for an ID), pointer columns are dereferenced and NULLs are skipped. Later duplicates win.

func (Collection[T]) Last

func (c Collection[T]) Last() *T

Last returns a pointer to the last model, or nil.

func (Collection[T]) Load

func (c Collection[T]) Load(ctx context.Context, relations ...string) error

Load eager-loads relations onto every model with one query per relation (Eloquent's load()).

func (Collection[T]) LoadMissing

func (c Collection[T]) LoadMissing(ctx context.Context, relations ...string) error

LoadMissing loads a relation only onto the models that do not have it loaded yet.

func (Collection[T]) ModelKeys

func (c Collection[T]) ModelKeys() ([]any, error)

ModelKeys returns the primary key of every model.

func (Collection[T]) Only

func (c Collection[T]) Only(ids ...any) (Collection[T], error)

Only keeps the models whose primary key is in ids.

func (Collection[T]) Pluck

func (c Collection[T]) Pluck(col string) ([]any, error)

Pluck returns one column of every model (Eloquent's pluck).

func (Collection[T]) Reject

func (c Collection[T]) Reject(drop func(m *T) bool) Collection[T]

Reject is the inverse of Filter.

func (Collection[T]) ToMaps

func (c Collection[T]) ToMaps(opts ...SerializeOpt) ([]map[string]any, error)

ToMaps serializes every model with ToMapWith.

func (Collection[T]) Unique

func (c Collection[T]) Unique(col string) (Collection[T], error)

Unique keeps the first model of every distinct column value.

type ColumnInfo

type ColumnInfo struct {
	Name          string
	GoType        string
	DBType        string
	PrimaryKey    bool
	AutoIncrement bool
	NotNull       bool
	Unique        bool
	HasDefault    bool
}

ColumnInfo describes one column of a model.

type Connected

type Connected interface{ Connection() string }

Connected lets a model say which Manager connection it lives on (Eloquent's $connection).

type CursorPage

type CursorPage[T any] struct {
	Data       Collection[T]
	PerPage    int
	NextCursor string
	PrevCursor string
}

CursorPage is one keyset page. Pass NextCursor or PrevCursor back to CursorPaginate; an empty cursor means there is no page in that direction.

type Defaults

type Defaults interface{ Defaults() map[string]any }

Defaults returns the values a model starts with when built through Model.Make or CreateFrom (Eloquent's $attributes). A struct literal is never touched: Go cannot tell a field left at its zero value from one set to it.

type Fillable

type Fillable interface{ Fillable() []string }

Fillable lists the only columns Fill / CreateFrom may set (Eloquent's $fillable).

type GlobalScoped

type GlobalScoped interface{ GlobalScopes() []NamedScope }

GlobalScoped is implemented by models that always filter (or order) their queries:

func (Post) GlobalScopes() []orm.NamedScope {
    return []orm.NamedScope{{Name: "visible", Fn: func(g *gorm.DB) *gorm.DB { return g.Where("hidden = ?", false) }}}
}

Global scopes apply to Query, Find, FindMany, All, counts, updates, deletes and to eager-loaded relations of that model.

type Guarded

type Guarded interface{ Guarded() []string }

Guarded lists columns Fill may not set (Eloquent's $guarded). Ignored when Fillable exists.

type HasMany

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

func (*HasMany[T]) Create

func (r *HasMany[T]) Create(ctx context.Context, attrs map[string]any) (*T, error)

Create builds a child from attrs (mass assignment rules apply), links and inserts it.

func (*HasMany[T]) CreateMany

func (r *HasMany[T]) CreateMany(ctx context.Context, rows []map[string]any) ([]T, error)

CreateMany creates one child per attrs map, atomically.

func (*HasMany) Get

func (m *HasMany) Get(ctx context.Context) (Collection[T], error)

Get returns the related rows, loading them on first use.

func (*HasMany) Items

func (m *HasMany) Items() Collection[T]

Items returns the loaded rows (empty until loaded; see Loaded).

func (*HasMany) Query

func (m *HasMany) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*HasMany[T]) Save

func (r *HasMany[T]) Save(ctx context.Context, child *T) error

Save sets the foreign key on child and persists it (Eloquent's $user->posts()->save($post)).

func (*HasMany[T]) SaveMany

func (r *HasMany[T]) SaveMany(ctx context.Context, children []T) error

SaveMany links and persists each child (Eloquent's saveMany).

func (*HasMany) Value

func (m *HasMany) Value() (driver.Value, error)

Value hides the relation from GORM; see one.Value.

type HasManyThrough

type HasManyThrough[T, Through any] struct {
	// contains filtered or unexported fields
}

func (*HasManyThrough) Get

func (m *HasManyThrough) Get(ctx context.Context) (Collection[T], error)

Get returns the related rows, loading them on first use.

func (*HasManyThrough) Items

func (m *HasManyThrough) Items() Collection[T]

Items returns the loaded rows (empty until loaded; see Loaded).

func (*HasManyThrough) Query

func (m *HasManyThrough) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*HasManyThrough) Value

func (m *HasManyThrough) Value() (driver.Value, error)

Value hides the relation from GORM; see one.Value.

type HasManyThroughLoader

type HasManyThroughLoader[T any, Through any] struct {
	ForeignKey        string
	ThroughLocalKey   string
	ThroughForeignKey string
	// Scope optionally filters or orders the T query.
	Scope func(*gorm.DB) *gorm.DB
}

HasManyThroughLoader loads T rows reachable from a parent via a Through model, like Lucid's hasManyThrough: Country -> User (Through) -> Post (T).

ForeignKey         column on Through pointing at the parent   (users.country_id)
ThroughLocalKey    Through's key referenced by T              (users.id), default "id"
ThroughForeignKey  column on T pointing at Through            (posts.user_id)

func (HasManyThroughLoader[T, Through]) Load

func (r HasManyThroughLoader[T, Through]) Load(ctx context.Context, db *migro.DB, parentKeys []any) (map[any][]T, error)

Load returns, per parent key, the T rows reachable through Through.

type HasOne

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

func (*HasOne[T]) Create

func (r *HasOne[T]) Create(ctx context.Context, attrs map[string]any) (*T, error)

func (*HasOne) Get

func (o *HasOne) Get(ctx context.Context) (*T, error)

Get returns the related row (nil when there is none), loading it on first use.

func (*HasOne) Item

func (o *HasOne) Item() *T

Item returns the loaded row, or nil. A relation tagged orm:"withDefault" answers with an empty, unsaved model instead of nil once it has been loaded.

func (*HasOne) Query

func (o *HasOne) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*HasOne[T]) Save

func (r *HasOne[T]) Save(ctx context.Context, child *T) error

func (*HasOne) Value

func (o *HasOne) Value() (driver.Value, error)

Value makes the relation a driver.Valuer that always yields nil. GORM then merges the `gorm` tags of the relation's inner fields into the field's own settings, and the on those inner fields hides the relation from GORM without the user tagging every field.

type HasOneThrough

type HasOneThrough[T, Through any] struct {
	// contains filtered or unexported fields
}

HasOneThrough reaches a single T through an intermediate model (Eloquent's hasOneThrough).

func (*HasOneThrough) Get

func (o *HasOneThrough) Get(ctx context.Context) (*T, error)

Get returns the related row (nil when there is none), loading it on first use.

func (*HasOneThrough) Item

func (o *HasOneThrough) Item() *T

Item returns the loaded row, or nil. A relation tagged orm:"withDefault" answers with an empty, unsaved model instead of nil once it has been loaded.

func (*HasOneThrough) Query

func (o *HasOneThrough) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*HasOneThrough) Value

func (o *HasOneThrough) Value() (driver.Value, error)

Value makes the relation a driver.Valuer that always yields nil. GORM then merges the `gorm` tags of the relation's inner fields into the field's own settings, and the on those inner fields hides the relation from GORM without the user tagging every field.

type Hidden

type Hidden interface{ Hidden() []string }

Hidden lists columns left out of ToMap / JSON (Eloquent's $hidden).

type JSONColumn

type JSONColumn[T any] struct{ Data T }

JSONColumn is a typed JSON column: the struct field holds a T and the database holds its JSON text (Eloquent's array / AsArrayObject cast, with the type checked by the compiler).

type User struct {
    orm.BaseModel
    Settings orm.JSONColumn[Settings]
}
user.Settings.Data.Theme = "dark"

A zero T (nil map, nil slice, nil pointer) is stored as NULL.

func (JSONColumn[T]) GormDBDataType

func (JSONColumn[T]) GormDBDataType(db *gorm.DB, _ *schema.Field) string

GormDBDataType picks the native type per database.

func (JSONColumn[T]) GormDataType

func (JSONColumn[T]) GormDataType() string

GormDataType tells GORM which column type to assume.

func (JSONColumn[T]) MarshalJSON

func (j JSONColumn[T]) MarshalJSON() ([]byte, error)

MarshalJSON makes ToMap / JSON output the nested value, not {"Data": ...}.

func (*JSONColumn[T]) Scan

func (j *JSONColumn[T]) Scan(src any) error

Scan implements sql.Scanner.

func (*JSONColumn[T]) UnmarshalJSON

func (j *JSONColumn[T]) UnmarshalJSON(b []byte) error

UnmarshalJSON is the inverse of MarshalJSON.

func (JSONColumn[T]) Value

func (j JSONColumn[T]) Value() (driver.Value, error)

Value implements driver.Valuer.

type MassPrunable

type MassPrunable[T any] interface{ MassPrunable(q *Query[T]) *Query[T] }

MassPrunable is Prunable deleted with one statement and no events (Eloquent's MassPrunable). When a model has both, MassPrunable wins.

type Model

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

Model is the repository for T.

func New

func New[T any](db *migro.DB) *Model[T]

New binds a model type to a database (or transaction-bound *migro.DB).

func Using

func Using[T any](ctx context.Context, m *migro.Manager) (*Model[T], error)

Using returns the repository for T on the connection T names, or the manager's default:

func (AuditLog) Connection() string { return "audit" }
logs, err := orm.Using[AuditLog](ctx, manager)

func (*Model[T]) All

func (m *Model[T]) All(ctx context.Context) (Collection[T], error)

func (*Model[T]) Create

func (m *Model[T]) Create(ctx context.Context, v *T) error

func (*Model[T]) CreateFrom

func (m *Model[T]) CreateFrom(ctx context.Context, attrs map[string]any) (*T, error)

CreateFrom builds a model from attrs (mass assignment rules apply) and inserts it (Eloquent's Model::create).

func (*Model[T]) CreateMany

func (m *Model[T]) CreateMany(ctx context.Context, vs []T) error

func (*Model[T]) Delete

func (m *Model[T]) Delete(ctx context.Context, v *T) error

Delete removes v (soft when T has gorm.DeletedAt).

func (*Model[T]) Destroy

func (m *Model[T]) Destroy(ctx context.Context, ids ...any) (int64, error)

Destroy deletes rows by primary key (soft when the model soft deletes) and returns how many.

func (*Model[T]) Find

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

Find loads by primary key; returns migro.ErrNotFound-matching error when missing.

func (*Model[T]) FindBy

func (m *Model[T]) FindBy(ctx context.Context, column string, value any) (*T, error)

FindBy loads the first row where column = value.

func (*Model[T]) FindMany

func (m *Model[T]) FindMany(ctx context.Context, ids ...any) (Collection[T], error)

FindMany loads several rows by primary key; missing ids are simply absent.

func (*Model[T]) FindOrFail

func (m *Model[T]) FindOrFail(ctx context.Context, id any) (*T, error)

FindOrFail is Find: a missing row is already a NOT_FOUND error.

func (*Model[T]) FirstOrCreate

func (m *Model[T]) FirstOrCreate(ctx context.Context, where T, attrs T) (*T, bool, error)

FirstOrCreate finds a row matching the non-zero fields of where, or creates it with attrs applied. Returns whether a row was created.

func (*Model[T]) FirstOrCreateMany

func (m *Model[T]) FirstOrCreateMany(ctx context.Context, keys []string, rows []T) ([]T, error)

FirstOrCreateMany is UpdateOrCreateMany that leaves existing rows untouched (Lucid's fetchOrCreateMany).

func (*Model[T]) FirstOrNew

func (m *Model[T]) FirstOrNew(ctx context.Context, where T, attrs T) (*T, bool, error)

FirstOrNew finds a row matching the non-zero fields of where, or returns an unsaved one built from where and attrs. Returns whether the row exists.

func (*Model[T]) InsertOrIgnore

func (m *Model[T]) InsertOrIgnore(ctx context.Context, rows []T) error

InsertOrIgnore inserts rows and silently skips the ones that violate a unique key.

func (*Model[T]) Make

func (m *Model[T]) Make(attrs map[string]any) (*T, error)

Make builds an unsaved model from attrs, honouring Fillable/Guarded (Eloquent's new Model($attrs)).

func (*Model[T]) Prune

func (m *Model[T]) Prune(ctx context.Context) (int64, error)

Prune deletes the model's stale rows for good, soft-deleted ones included, and returns how many.

func (*Model[T]) Query

func (m *Model[T]) Query() *Query[T]

Query starts a fluent query.

Global scopes declared by the model (see GlobalScoped) are applied to it.

func (*Model[T]) Save

func (m *Model[T]) Save(ctx context.Context, v *T) error

Save inserts or updates all fields by primary key.

func (*Model[T]) Truncate

func (m *Model[T]) Truncate(ctx context.Context) error

Truncate empties the table (soft-deleted rows included). The statement is DELETE-based on SQLite, which has no TRUNCATE.

func (*Model[T]) UpdateOrCreate

func (m *Model[T]) UpdateOrCreate(ctx context.Context, where T, values T) (*T, error)

UpdateOrCreate finds by where and applies values, or creates the row with where+values.

func (*Model[T]) UpdateOrCreateMany

func (m *Model[T]) UpdateOrCreateMany(ctx context.Context, keys []string, rows []T) ([]T, error)

UpdateOrCreateMany upserts each row by the given key columns, in one transaction (Lucid's updateOrCreateMany): an existing row gets the other fields of the row applied.

func (*Model[T]) Upsert

func (m *Model[T]) Upsert(ctx context.Context, rows []T, uniqueBy []string, update []string) error

Upsert inserts rows, and for a row whose uniqueBy columns already exist updates the update columns (all columns except the keys and created_at when update is empty), in one statement (Eloquent's upsert). uniqueBy needs a unique index. MySQL ignores uniqueBy and uses whichever unique key conflicts. SQL Server and Oracle run a MERGE; Redshift has no upsert and is refused.

type ModelInfo

type ModelInfo struct {
	Name        string
	Table       string
	SoftDeletes bool
	Columns     []ColumnInfo
	Relations   []RelationInfo
	// Traits lists the optional behaviours the model opts into: Fillable, Hidden, Touches, ...
	Traits []string
}

ModelInfo is what Describe reports (the model:show command prints it).

func Describe

func Describe(db *migro.DB, model any) (ModelInfo, error)

Describe reports a model's table, columns, relations and optional behaviours.

type MorphMany

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

func (*MorphMany[T]) Create

func (r *MorphMany[T]) Create(ctx context.Context, attrs map[string]any) (*T, error)

func (*MorphMany[T]) CreateMany

func (r *MorphMany[T]) CreateMany(ctx context.Context, rows []map[string]any) ([]T, error)

func (*MorphMany) Get

func (m *MorphMany) Get(ctx context.Context) (Collection[T], error)

Get returns the related rows, loading them on first use.

func (*MorphMany) Items

func (m *MorphMany) Items() Collection[T]

Items returns the loaded rows (empty until loaded; see Loaded).

func (*MorphMany) Query

func (m *MorphMany) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*MorphMany[T]) Save

func (r *MorphMany[T]) Save(ctx context.Context, child *T) error

func (*MorphMany[T]) SaveMany

func (r *MorphMany[T]) SaveMany(ctx context.Context, children []T) error

SaveMany links and persists each child.

func (*MorphMany) Value

func (m *MorphMany) Value() (driver.Value, error)

Value hides the relation from GORM; see one.Value.

type MorphOne

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

func (*MorphOne[T]) Create

func (r *MorphOne[T]) Create(ctx context.Context, attrs map[string]any) (*T, error)

func (*MorphOne) Get

func (o *MorphOne) Get(ctx context.Context) (*T, error)

Get returns the related row (nil when there is none), loading it on first use.

func (*MorphOne) Item

func (o *MorphOne) Item() *T

Item returns the loaded row, or nil. A relation tagged orm:"withDefault" answers with an empty, unsaved model instead of nil once it has been loaded.

func (*MorphOne) Query

func (o *MorphOne) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*MorphOne[T]) Save

func (r *MorphOne[T]) Save(ctx context.Context, child *T) error

func (*MorphOne) Value

func (o *MorphOne) Value() (driver.Value, error)

Value makes the relation a driver.Valuer that always yields nil. GORM then merges the `gorm` tags of the relation's inner fields into the field's own settings, and the on those inner fields hides the relation from GORM without the user tagging every field.

type MorphRegistry

type MorphRegistry struct {
	// contains filtered or unexported fields
}

MorphRegistry maps type aliases (the value stored in a *_type column) to model loaders. It is an instance, not a global, so each application wires its own.

func NewMorphRegistry

func NewMorphRegistry() *MorphRegistry

func (*MorphRegistry) Load

func (r *MorphRegistry) Load(ctx context.Context, db *migro.DB, refs []Ref) (map[Ref]any, error)

Load resolves refs to model pointers (*T of the registered type) with one query per type. Refs whose row no longer exists are absent from the result.

type MorphTo

type MorphTo struct {
	// contains filtered or unexported fields
}

MorphTo is the inverse of MorphOne/MorphMany: the owner holds <name>_type and <name>_id, and the target is any model registered with MorphMap. Item is a pointer to that model.

func (*MorphTo) Get

func (m *MorphTo) Get(ctx context.Context) (any, error)

Get returns the target (nil when it no longer exists), loading it on first use.

func (*MorphTo) Item

func (m *MorphTo) Item() any

func (*MorphTo) Load

func (m *MorphTo) Load(ctx context.Context) error

Load fetches the relation if it was not loaded yet.

func (*MorphTo) Loaded

func (m *MorphTo) Loaded() bool

Loaded reports whether the relation has been loaded (eagerly or lazily).

func (*MorphTo) Value

func (m *MorphTo) Value() (driver.Value, error)

Value hides the relation from GORM; see one.Value.

type MorphToMany

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

func (*MorphToMany[T]) Attach

func (r *MorphToMany[T]) Attach(ctx context.Context, ids ...any) error

func (*MorphToMany[T]) AttachWith

func (r *MorphToMany[T]) AttachWith(ctx context.Context, attrs map[string]any, ids ...any) error

func (*MorphToMany[T]) Detach

func (r *MorphToMany[T]) Detach(ctx context.Context, ids ...any) error

func (*MorphToMany) Get

func (m *MorphToMany) Get(ctx context.Context) (Collection[T], error)

Get returns the related rows, loading them on first use.

func (*MorphToMany) Items

func (m *MorphToMany) Items() Collection[T]

Items returns the loaded rows (empty until loaded; see Loaded).

func (*MorphToMany) Query

func (m *MorphToMany) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*MorphToMany[T]) Sync

func (r *MorphToMany[T]) Sync(ctx context.Context, ids ...any) (SyncResult, error)

func (*MorphToMany[T]) SyncWithoutDetaching

func (r *MorphToMany[T]) SyncWithoutDetaching(ctx context.Context, ids ...any) (SyncResult, error)

func (*MorphToMany[T]) Toggle

func (r *MorphToMany[T]) Toggle(ctx context.Context, ids ...any) (SyncResult, error)

func (*MorphToMany) Value

func (m *MorphToMany) Value() (driver.Value, error)

Value hides the relation from GORM; see one.Value.

type MorphToManyLoader

type MorphToManyLoader[T any] struct {
	Pivot      Pivot
	RelatedKey string
	Scope      func(*gorm.DB) *gorm.DB
}

MorphToManyLoader loads T (the related side, e.g. Tag) for polymorphic parents (e.g. Post). RelatedKey is T's key referenced by the pivot, default "id".

func (MorphToManyLoader[T]) Attach

func (r MorphToManyLoader[T]) Attach(ctx context.Context, db *migro.DB, parentKey any, relatedKeys ...any) error

Attach links related keys to the parent, skipping links that already exist.

func (MorphToManyLoader[T]) Detach

func (r MorphToManyLoader[T]) Detach(ctx context.Context, db *migro.DB, parentKey any, relatedKeys ...any) error

Detach removes the given links, or every link of the parent when no keys are passed.

func (MorphToManyLoader[T]) Load

func (r MorphToManyLoader[T]) Load(ctx context.Context, db *migro.DB, parentKeys []any) (map[any][]T, error)

func (MorphToManyLoader[T]) Sync

func (r MorphToManyLoader[T]) Sync(ctx context.Context, db *migro.DB, parentKey any, relatedKeys ...any) error

Sync makes the parent's links exactly relatedKeys, atomically.

type MorphedByMany

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

func (*MorphedByMany[T]) Attach

func (r *MorphedByMany[T]) Attach(ctx context.Context, ids ...any) error

func (*MorphedByMany[T]) Detach

func (r *MorphedByMany[T]) Detach(ctx context.Context, ids ...any) error

func (*MorphedByMany) Get

func (m *MorphedByMany) Get(ctx context.Context) (Collection[T], error)

Get returns the related rows, loading them on first use.

func (*MorphedByMany) Items

func (m *MorphedByMany) Items() Collection[T]

Items returns the loaded rows (empty until loaded; see Loaded).

func (*MorphedByMany) Query

func (m *MorphedByMany) Query() (*Query[T], error)

Query returns a query over the related table already constrained to this owner.

func (*MorphedByMany[T]) Sync

func (r *MorphedByMany[T]) Sync(ctx context.Context, ids ...any) (SyncResult, error)

func (*MorphedByMany) Value

func (m *MorphedByMany) Value() (driver.Value, error)

Value hides the relation from GORM; see one.Value.

type MorphedByManyLoader

type MorphedByManyLoader[T any] struct {
	Pivot    Pivot
	MorphKey string
	Scope    func(*gorm.DB) *gorm.DB
}

MorphedByManyLoader is the inverse: given related keys (Tags), load the morphable T (Posts). MorphKey is T's key stored in the pivot, default "id".

func (MorphedByManyLoader[T]) Load

func (r MorphedByManyLoader[T]) Load(ctx context.Context, db *migro.DB, relatedKeys []any) (map[any][]T, error)

type NamedScope

type NamedScope struct {
	Name string
	Fn   func(*gorm.DB) *gorm.DB
}

NamedScope is a global scope. The name lets one query switch it off with WithoutGlobalScope.

type Page

type Page[T any] struct {
	Data        Collection[T]
	Total       int64
	PerPage     int
	CurrentPage int
	LastPage    int
}

Page is one page of models.

type Pivot

type Pivot struct {
	Table         string
	MorphIDColumn string // taggable_id
	MorphTypeCol  string // taggable_type
	RelatedIDCol  string // tag_id
	// MorphType is the alias stored in MorphTypeCol for the morphable side (e.g. "post").
	MorphType string
}

Pivot describes a polymorphic pivot table, e.g. taggables(tag_id, taggable_id, taggable_type).

type PivotModels

type PivotModels interface{ PivotModels() map[string]any }

PivotModels lets a model say that a many-to-many relation's pivot rows are a struct of their own (Eloquent's using(Pivot::class)). Attach then creates each row through that struct, so its hooks run, its own key is generated (UUIDKey, a BeforeCreate) and its defaults apply. The map is keyed by the relation's field name; the value is the pivot struct or a pointer to it. The struct's columns must include the pivot's two keys and every column you pass to AttachWith.

func (User) PivotModels() map[string]any { return map[string]any{"Roles": RoleUser{}} }

type Prunable

type Prunable[T any] interface{ Prunable(q *Query[T]) *Query[T] }

Prunable selects the stale rows of T that Prune deletes one by one, firing model events and ForceDelete semantics (Eloquent's Prunable):

func (Session) Prunable(q *orm.Query[Session]) *orm.Query[Session] { return q.Where("expires_at < ?", time.Now()) }

type Pruning

type Pruning interface {
	Pruning(ctx context.Context) error
}

Pruning is an optional hook run on each row before a Prunable delete, e.g. to remove its files.

type Query

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

Query is a mutable, chainable query for model T.

func (*Query[T]) Apply

func (q *Query[T]) Apply(scopes ...func(*Query[T]) *Query[T]) *Query[T]

Apply runs local scopes against the query, in order.

func (*Query[T]) Avg

func (q *Query[T]) Avg(ctx context.Context, col string) (float64, error)

func (*Query[T]) Chunk

func (q *Query[T]) Chunk(ctx context.Context, size int, fn func(rows []T) error) error

Chunk walks the result in primary-key order, size rows at a time, stopping at the first error. Rows are hydrated like Get (bound, With / WithCount applied).

func (*Query[T]) ChunkByID

func (q *Query[T]) ChunkByID(ctx context.Context, size int, column string, fn func(rows []T) error) error

ChunkByID is Chunk keyed on column instead of the primary key (Eloquent's chunkById). The column must be unique and ascending-comparable; an empty column means the primary key.

func (*Query[T]) Count

func (q *Query[T]) Count(ctx context.Context) (int64, error)

func (*Query[T]) Cursor

func (q *Query[T]) Cursor(ctx context.Context) iter.Seq2[*T, error]

Cursor streams the rows one at a time through a single open statement (Eloquent's cursor): memory stays flat for huge results. Relations are not eager loaded; use Lazy when you need With. Breaking out of the loop closes the statement.

func (*Query[T]) CursorPaginate

func (q *Query[T]) CursorPaginate(ctx context.Context, perPage int, cursor string) (CursorPage[T], error)

CursorPaginate pages by the query's OrderBy columns instead of an offset, so deep pages stay fast and rows inserted meanwhile do not shift the page. The query needs at least one OrderBy; the primary key is appended as a tie-breaker. Order columns must not be NULL.

func (*Query[T]) DB

func (q *Query[T]) DB() *migro.DB

DB returns the database the query runs on.

func (*Query[T]) Decrement

func (q *Query[T]) Decrement(ctx context.Context, col string, n int) (int64, error)

func (*Query[T]) Delete

func (q *Query[T]) Delete(ctx context.Context) (int64, error)

Delete removes matching rows (soft when T has gorm.DeletedAt); GORM refuses without a Where.

func (*Query[T]) Distinct

func (q *Query[T]) Distinct(cols ...string) *Query[T]

Distinct drops duplicate rows, optionally by the given columns.

func (*Query[T]) DoesntHave

func (q *Query[T]) DoesntHave(relation string) *Query[T]

DoesntHave keeps models with no related row; WhereDoesntHave ignores rows outside scope.

func (*Query[T]) Each

func (q *Query[T]) Each(ctx context.Context, size int, fn func(*T) error) error

Each runs fn for every row, size rows at a time, stopping at the first error.

func (*Query[T]) Exists

func (q *Query[T]) Exists(ctx context.Context) (bool, error)

func (*Query[T]) First

func (q *Query[T]) First(ctx context.Context) (*T, error)

First returns the first match or a NOT_FOUND error.

func (*Query[T]) FirstOrFail

func (q *Query[T]) FirstOrFail(ctx context.Context) (*T, error)

FirstOrFail is First: a missing row is already a NOT_FOUND error.

func (*Query[T]) FirstWhere

func (q *Query[T]) FirstWhere(ctx context.Context, col string, value any) (*T, error)

FirstWhere is Where(col = value) followed by First.

func (*Query[T]) Get

func (q *Query[T]) Get(ctx context.Context) (Collection[T], error)

func (*Query[T]) GroupBy

func (q *Query[T]) GroupBy(cols ...string) *Query[T]

GroupBy groups by columns; combine it with Select and Having for aggregates.

func (*Query[T]) Has

func (q *Query[T]) Has(relation string) *Query[T]

Has keeps models that have at least one related row; WhereHas also constrains those rows.

func (*Query[T]) Having

func (q *Query[T]) Having(query any, args ...any) *Query[T]

Having filters groups: Having("COUNT(*) > ?", 3). Never concatenate input into the condition.

func (*Query[T]) Increment

func (q *Query[T]) Increment(ctx context.Context, col string, n int) (int64, error)

Increment adds n to a column of every matching row, atomically.

func (*Query[T]) Joins

func (q *Query[T]) Joins(clause string, args ...any) *Query[T]

func (*Query[T]) Latest

func (q *Query[T]) Latest(col ...string) *Query[T]

Latest orders newest first by created_at (or the given column); Oldest is the opposite.

func (*Query[T]) Lazy

func (q *Query[T]) Lazy(ctx context.Context, size int) iter.Seq2[*T, error]

Lazy streams the rows chunk by chunk (Eloquent's lazyById): each chunk is hydrated like Get, so With and WithCount work, and memory holds one chunk at a time.

func (*Query[T]) Limit

func (q *Query[T]) Limit(n int) *Query[T]

func (*Query[T]) LockForUpdate

func (q *Query[T]) LockForUpdate() *Query[T]

LockForUpdate adds FOR UPDATE; SharedLock adds a shared row lock. Use them inside a transaction. SQLite ignores both (it locks the whole database); SQL Server and Oracle's shared lock are not supported and fail with UNSUPPORTED_FEATURE.

func (*Query[T]) Max

func (q *Query[T]) Max(ctx context.Context, col string) (any, error)

func (*Query[T]) Min

func (q *Query[T]) Min(ctx context.Context, col string) (any, error)

Min and Max keep the column's own type (nil when no row matches).

func (*Query[T]) Not

func (q *Query[T]) Not(query any, args ...any) *Query[T]

func (*Query[T]) Offset

func (q *Query[T]) Offset(n int) *Query[T]

func (*Query[T]) Oldest

func (q *Query[T]) Oldest(col ...string) *Query[T]

func (*Query[T]) OnlyTrashed

func (q *Query[T]) OnlyTrashed() *Query[T]

OnlyTrashed returns only soft-deleted rows.

func (*Query[T]) OrWhere

func (q *Query[T]) OrWhere(query any, args ...any) *Query[T]

func (*Query[T]) OrWhereRaw

func (q *Query[T]) OrWhereRaw(sql string, args ...any) *Query[T]

OrWhereRaw is WhereRaw joined with OR.

func (*Query[T]) OrderBy

func (q *Query[T]) OrderBy(col, dir string) *Query[T]

OrderBy: dir is "asc" or "desc".

func (*Query[T]) OrderByDesc

func (q *Query[T]) OrderByDesc(col string) *Query[T]

func (*Query[T]) Paginate

func (q *Query[T]) Paginate(ctx context.Context, page, perPage int) (Page[T], error)

Paginate returns page (1-based) with totals.

func (*Query[T]) Preload

func (q *Query[T]) Preload(relation string, args ...any) *Query[T]

Preload eager-loads a GORM relation by field name (avoids N+1).

func (*Query[T]) Scope

func (q *Query[T]) Scope(fns ...func(*gorm.DB) *gorm.DB) *Query[T]

Scope applies a reusable query fragment.

func (*Query[T]) Select

func (q *Query[T]) Select(cols ...string) *Query[T]

func (*Query[T]) SharedLock

func (q *Query[T]) SharedLock() *Query[T]

func (*Query[T]) SimplePaginate

func (q *Query[T]) SimplePaginate(ctx context.Context, page, perPage int) (SimplePage[T], error)

SimplePaginate returns page (1-based) and whether a next page exists.

func (*Query[T]) Sole

func (q *Query[T]) Sole(ctx context.Context) (*T, error)

Sole returns the only match: NOT_FOUND for none, MULTIPLE_RECORDS_FOUND for several.

func (*Query[T]) Sum

func (q *Query[T]) Sum(ctx context.Context, col string) (float64, error)

Sum and Avg return 0 when no row matches.

func (*Query[T]) Tap

func (q *Query[T]) Tap(fn func(*Query[T])) *Query[T]

Tap runs fn for its side effects and returns the query.

func (*Query[T]) ToSQL

func (q *Query[T]) ToSQL(ctx context.Context) (string, error)

ToSQL renders the SELECT this query would run, with its bindings inlined, without running it (Eloquent's toRawSql). Use it for debugging; never feed the result back to the database.

func (*Query[T]) Union

func (q *Query[T]) Union(others ...*Query[T]) *Query[T]

Union combines the receiver with others, removing duplicates; UnionAll keeps them. The result is a query over the combined rows: add OrderBy, Limit or Paginate to it afterwards. Each side keeps its own conditions and global scopes, and Select lists must line up.

func (*Query[T]) UnionAll

func (q *Query[T]) UnionAll(others ...*Query[T]) *Query[T]

func (*Query[T]) Update

func (q *Query[T]) Update(ctx context.Context, values map[string]any) (int64, error)

Update sets columns on matching rows; GORM refuses without a Where.

func (*Query[T]) When

func (q *Query[T]) When(cond bool, fn func(*Query[T]) *Query[T]) *Query[T]

When applies fn only if cond is true (Eloquent's when()).

func (*Query[T]) Where

func (q *Query[T]) Where(query any, args ...any) *Query[T]

func (*Query[T]) WhereBelongsTo

func (q *Query[T]) WhereBelongsTo(parent any, relation ...string) *Query[T]

WhereBelongsTo keeps the rows that belong to parent through a belongs-to relation (Eloquent's whereBelongsTo): posts.Query().WhereBelongsTo(user). With several relations to the same model, name the relation: WhereBelongsTo(user, "Editor").

func (*Query[T]) WhereBetween

func (q *Query[T]) WhereBetween(col string, from, to any) *Query[T]

func (*Query[T]) WhereColumn

func (q *Query[T]) WhereColumn(left, op, right string) *Query[T]

WhereColumn compares two columns: WhereColumn("updated_at", ">", "created_at").

func (*Query[T]) WhereDate

func (q *Query[T]) WhereDate(col, op string, value any) *Query[T]

WhereDate compares the date part of a date/time column (value is "2026-01-31" or a time.Time).

func (*Query[T]) WhereDay

func (q *Query[T]) WhereDay(col, op string, value int) *Query[T]

func (*Query[T]) WhereDoesntHave

func (q *Query[T]) WhereDoesntHave(relation string, scope scopeFn) *Query[T]

func (*Query[T]) WhereExists

func (q *Query[T]) WhereExists(sub SubQuery) *Query[T]

WhereExists adds EXISTS (sub). Correlate the subquery with WhereColumn:

users.Query().WhereExists(posts.Query().WhereColumn("e_posts.user_id", "=", "e_users.id"))

func (*Query[T]) WhereFullText

func (q *Query[T]) WhereFullText(search string, cols ...string) *Query[T]

WhereFullText matches a search text against columns that have a full-text index (see schema Table.FullText). MySQL uses MATCH AGAINST and PostgreSQL to_tsvector.

func (*Query[T]) WhereFullTextBoolean

func (q *Query[T]) WhereFullTextBoolean(search string, cols ...string) *Query[T]

WhereFullTextBoolean is WhereFullText with operator syntax (+word -word "a phrase").

func (*Query[T]) WhereHas

func (q *Query[T]) WhereHas(relation string, scope scopeFn) *Query[T]

func (*Query[T]) WhereHasMorph

func (q *Query[T]) WhereHasMorph(relation string, aliases []string, scope scopeFn) *Query[T]

WhereHasMorph keeps the rows whose morph-to relation points at one of the given morph aliases, and whose target satisfies scope (nil for any target): WhereHasMorph("Commentable", []string{"post"}, nil).

func (*Query[T]) WhereIn

func (q *Query[T]) WhereIn(col string, values any) *Query[T]

func (*Query[T]) WhereJSON

func (q *Query[T]) WhereJSON(col, path, op string, value any) *Query[T]

WhereJSON compares the text at a JSON path inside col: WhereJSON("meta", "address.city", "=", "Rio"). Path segments are names or array indexes separated by dots.

func (*Query[T]) WhereLike

func (q *Query[T]) WhereLike(col, pattern string) *Query[T]

WhereLike matches col against a LIKE pattern; WhereNotLike negates it.

func (*Query[T]) WhereMonth

func (q *Query[T]) WhereMonth(col, op string, value int) *Query[T]

func (*Query[T]) WhereNotExists

func (q *Query[T]) WhereNotExists(sub SubQuery) *Query[T]

WhereNotExists adds NOT EXISTS (sub).

func (*Query[T]) WhereNotIn

func (q *Query[T]) WhereNotIn(col string, values any) *Query[T]

func (*Query[T]) WhereNotLike

func (q *Query[T]) WhereNotLike(col, pattern string) *Query[T]

func (*Query[T]) WhereNotNull

func (q *Query[T]) WhereNotNull(col string) *Query[T]

func (*Query[T]) WhereNull

func (q *Query[T]) WhereNull(col string) *Query[T]

func (*Query[T]) WhereRaw

func (q *Query[T]) WhereRaw(sql string, args ...any) *Query[T]

WhereRaw adds a raw condition. Pass values as args; never concatenate input into sql.

func (*Query[T]) WhereRelation

func (q *Query[T]) WhereRelation(relation string, query any, args ...any) *Query[T]

WhereRelation is WhereHas with a plain condition: WhereRelation("Posts", "published = ?", true).

func (*Query[T]) WhereYear

func (q *Query[T]) WhereYear(col, op string, value int) *Query[T]

WhereYear, WhereMonth and WhereDay compare one component of a date/time column.

func (*Query[T]) With

func (q *Query[T]) With(relation string, scope ...scopeFn) *Query[T]

With eager-loads relations by field name; "Posts.Comments" nests. An optional scope constrains the last relation of the path: With("Posts", func(q *gorm.DB) *gorm.DB { return q.Where(...) }).

func (*Query[T]) WithAvg

func (q *Query[T]) WithAvg(relation, column string) *Query[T]

func (*Query[T]) WithCount

func (q *Query[T]) WithCount(relations ...string) *Query[T]

WithCount adds "<relation>_count" to each model's Extras (Eloquent's withCount).

func (*Query[T]) WithExists

func (q *Query[T]) WithExists(relation string) *Query[T]

WithExists adds "<relation>_exists" (a bool) to each model's Extras.

func (*Query[T]) WithMax

func (q *Query[T]) WithMax(relation, column string) *Query[T]

func (*Query[T]) WithMin

func (q *Query[T]) WithMin(relation, column string) *Query[T]

func (*Query[T]) WithSum

func (q *Query[T]) WithSum(relation, column string) *Query[T]

WithSum, WithAvg, WithMin and WithMax add an aggregate of a related column to each model's Extras as "<relation>_sum_<column>" (and so on), computed in one extra query. Sum is 0 when nothing is related; the others are nil. Supported on has-one/many, morph one/many and the many-to-many kinds.

func (*Query[T]) WithTrashed

func (q *Query[T]) WithTrashed() *Query[T]

WithTrashed includes soft-deleted rows.

func (*Query[T]) WithoutGlobalScope

func (q *Query[T]) WithoutGlobalScope(names ...string) *Query[T]

WithoutGlobalScope switches the named global scopes off for this query.

func (*Query[T]) WithoutGlobalScopes

func (q *Query[T]) WithoutGlobalScopes() *Query[T]

WithoutGlobalScopes switches every global scope off for this query.

type R

type R struct {
	// contains filtered or unexported fields
}

R collects the fields of one resource item.

func (*R) Fail

func (r *R) Fail(err error)

Fail records an error; Make / Collection return the first one.

func (*R) Loaded

func (r *R) Loaded(relation string) bool

Loaded reports whether the named relation is loaded on the model.

func (*R) Merge

func (r *R) Merge(fields map[string]any)

Merge copies every entry of fields into the item.

func (*R) MergeWhen

func (r *R) MergeWhen(cond bool, fields map[string]any)

MergeWhen merges fields only if cond is true.

func (*R) Set

func (r *R) Set(key string, v any)

Set always includes the key.

func (*R) When

func (r *R) When(cond bool, key string, v any)

When includes the key only if cond is true.

func (*R) WhenLoaded

func (r *R) WhenLoaded(relation string, fn func() (any, error))

WhenLoaded sets key (the relation's snake_case name) to fn() only when the relation is loaded on the model, so a resource never triggers lazy queries. relation is the Go field name.

type Record

type Record struct {
	// contains filtered or unexported fields
}

Record carries the active-record behaviour (Save, Delete, Refresh, Fill, IsDirty, relations, ...) without any column. BaseModel, UUIDModel and ULIDModel embed it and add a primary key; embed it yourself to bring your own key.

Models returned by Model[T] / Query[T] are bound to their database, which enables the instance methods and lazy relations. A struct you build by hand is unbound until you create it through the repository or call Bind.

func (*Record) Decrement

func (b *Record) Decrement(ctx context.Context, column string, n int) error

Decrement is Increment with a negative amount.

func (*Record) Delete

func (b *Record) Delete(ctx context.Context) error

Delete removes the row, or marks it deleted when the model has a gorm.DeletedAt column.

func (*Record) DeleteQuietly

func (b *Record) DeleteQuietly(ctx context.Context) error

DeleteQuietly is Delete without model events.

func (*Record) Dirty

func (b *Record) Dirty() map[string]any

Dirty returns the changed columns with their current values.

func (*Record) Exists

func (b *Record) Exists() bool

Exists reports whether the model has been persisted.

func (*Record) Extra

func (b *Record) Extra(key string) any

Extra returns one extra value.

func (*Record) Extras

func (b *Record) Extras() map[string]any

Extras holds computed values attached by queries: "<relation>_count" from WithCount and "pivot_<column>" on models loaded through a many-to-many relation (Lucid's $extras).

func (*Record) Fill

func (b *Record) Fill(attrs map[string]any) error

Fill sets columns (by column or field name) from attrs. Fields outside Fillable, or inside Guarded, fail with MASS_ASSIGNMENT instead of being silently dropped. Models implementing neither are open.

func (*Record) ForceDelete

func (b *Record) ForceDelete(ctx context.Context) error

ForceDelete removes the row for good, soft deletes included.

func (*Record) GetAttribute

func (b *Record) GetAttribute(column string) (any, error)

GetAttribute returns one column through its cast (an accessor).

func (*Record) GetChanges

func (b *Record) GetChanges() map[string]any

GetChanges returns the columns the last Save wrote, with their new values.

func (*Record) GetOriginal

func (b *Record) GetOriginal(column string) any

GetOriginal returns the value a column had when the model was loaded or last saved.

func (*Record) Increment

func (b *Record) Increment(ctx context.Context, column string, n int) error

Increment adds n to a numeric column atomically in SQL, then reloads the model.

func (*Record) IncrementEach

func (b *Record) IncrementEach(ctx context.Context, amounts map[string]int) error

IncrementEach adds a different amount to several columns in one atomic UPDATE, then reloads the model.

func (*Record) IsClean

func (b *Record) IsClean(columns ...string) bool

IsClean is the inverse of IsDirty.

func (*Record) IsDirty

func (b *Record) IsDirty(columns ...string) bool

IsDirty reports whether any (or, when given, any of the named) columns changed since load/save.

func (*Record) Refresh

func (b *Record) Refresh(ctx context.Context) error

Refresh reloads the model's columns from the database.

func (*Record) Restore

func (b *Record) Restore(ctx context.Context) error

Restore clears the soft-delete mark.

func (*Record) Save

func (b *Record) Save(ctx context.Context) error

Save inserts a new model, or updates only the columns that changed since it was loaded (Eloquent's save). A model with no changes issues no query.

func (*Record) SaveQuietly

func (b *Record) SaveQuietly(ctx context.Context) error

SaveQuietly is Save without model events (Eloquent's saveQuietly).

func (*Record) SaveWithoutTimestamps

func (b *Record) SaveWithoutTimestamps(ctx context.Context) error

SaveWithoutTimestamps saves without touching updated_at, and without events or hooks (Eloquent's withoutTimestamps). A new row is inserted with the timestamps it already carries.

func (*Record) SetAttribute

func (b *Record) SetAttribute(column string, v any) error

SetAttribute sets one column through its cast (a mutator), ignoring Fillable / Guarded: it is the programmatic counterpart of `$model->column = value`.

func (*Record) Touch

func (b *Record) Touch(ctx context.Context) error

Touch bumps updated_at without touching other columns.

func (*Record) Trashed

func (b *Record) Trashed() bool

Trashed reports whether the model is soft deleted.

func (*Record) Update

func (b *Record) Update(ctx context.Context, attrs map[string]any) error

Update fills attrs (honouring Fillable/Guarded) and saves.

func (*Record) UseTransaction

func (b *Record) UseTransaction(tx *migro.DB)

UseTransaction rebinds the model to tx, so its next Save, Delete or lazy load runs inside the transaction (Lucid's useTransaction). Relations already loaded stay loaded.

func (*Record) WasChanged

func (b *Record) WasChanged(columns ...string) bool

WasChanged reports whether the last Save wrote any (or, when given, any of the named) columns. A Save that inserted the row counts every column as changed.

func (*Record) WasRecentlyCreated

func (b *Record) WasRecentlyCreated() bool

WasRecentlyCreated reports whether the last Save inserted the row.

type Ref

type Ref struct {
	Type string
	ID   any
}

Ref points at a row of a registered morph type. ID must be comparable.

type RelationInfo

type RelationInfo struct {
	Name    string
	Kind    string // has_one, has_many, belongs_to, belongs_to_many, ...
	Related string // the related model, "(any)" for morph-to
	// Keys spells out how the relation joins, e.g. "posts.user_id -> users.id".
	Keys string
}

RelationInfo describes one relation of a model.

type Resource

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

Resource turns a model into the exact shape your API returns, the way Laravel's JsonResource does. Unlike ToMap, nothing is exposed unless you list it.

var UserResource = orm.NewResource(func(u *User, r *orm.R) {
    r.Set("id", u.ID)
    r.Set("name", u.Name)
    r.When(isAdmin, "email", u.Email)
    r.WhenLoaded("Posts", func() (any, error) { return PostResource.List(u.Posts.Items()) })
})

body, err := UserResource.Make(user)            // {"data": {...}}
body, err = UserResource.Collection(users)      // {"data": [...]}
body, err = UserResource.Paginated(page)        // {"data": [...], "meta": {...}}

func NewResource

func NewResource[T any](fields func(m *T, r *R)) *Resource[T]

NewResource builds a resource from a function that describes one model.

func (*Resource[T]) Additional

func (r *Resource[T]) Additional(extra map[string]any) *Resource[T]

Additional adds top-level keys next to the wrapped payload (Laravel's additional()).

func (*Resource[T]) Collection

func (r *Resource[T]) Collection(items []T) (any, error)

Collection is the response for a list.

func (*Resource[T]) List

func (r *Resource[T]) List(items []T) ([]map[string]any, error)

List describes many models without wrapping.

func (*Resource[T]) Make

func (r *Resource[T]) Make(m *T) (any, error)

Make is the response for one model.

func (*Resource[T]) Map

func (r *Resource[T]) Map(m *T) (map[string]any, error)

Map describes one model without any wrapping: use it to nest a resource inside another.

func (*Resource[T]) Paginated

func (r *Resource[T]) Paginated(page Page[T], baseURL ...string) (any, error)

Paginated is the response for a page: the items plus "meta" (current_page, last_page, per_page, total, from, to). Pass a base URL to also get "links" (first, last, prev, next).

func (*Resource[T]) WithoutWrapping

func (r *Resource[T]) WithoutWrapping() *Resource[T]

WithoutWrapping returns the bare item (Make) or array (Collection). Extra top-level keys (Additional, Paginated's meta) merge into a bare item; a bare array that has to carry them keeps its wrapper.

func (*Resource[T]) Wrap

func (r *Resource[T]) Wrap(key string) *Resource[T]

Wrap changes the key the payload sits under ("data" by default).

type SerializeAs

type SerializeAs interface{ SerializeAs() map[string]string }

SerializeAs renames columns and relations in ToMap / JSON: {"created_at": "createdAt"} (Lucid's serializeAs).

type SerializeOpt

type SerializeOpt func(*serializeCfg)

SerializeOpt adjusts one ToMapWith / JSONWith call.

func DateFormat

func DateFormat(layout string) SerializeOpt

DateFormat renders time.Time values with a Go layout, nested relations included.

func MakeHidden

func MakeHidden(cols ...string) SerializeOpt

MakeHidden hides extra columns for this call only (Eloquent's makeHidden). It wins over MakeVisible.

func MakeVisible

func MakeVisible(cols ...string) SerializeOpt

MakeVisible shows columns the model hides, for this call only (Eloquent's makeVisible).

type SimplePage

type SimplePage[T any] struct {
	Data        Collection[T]
	PerPage     int
	CurrentPage int
	HasMore     bool
}

SimplePage is a page without a total: one extra row tells whether another page follows, so it skips the COUNT query Paginate pays for.

type SoftDeleteModel

type SoftDeleteModel struct {
	BaseModel
	DeletedAt gorm.DeletedAt `gorm:"index"`
}

SoftDeleteModel adds soft deletes: Delete sets DeletedAt and queries hide those rows.

type SoftDeleteULIDModel

type SoftDeleteULIDModel struct {
	ULIDModel
	DeletedAt gorm.DeletedAt `gorm:"index"`
}

SoftDeleteULIDModel is ULIDModel with soft deletes.

type SoftDeleteUUIDModel

type SoftDeleteUUIDModel struct {
	UUIDModel
	DeletedAt gorm.DeletedAt `gorm:"index"`
}

SoftDeleteUUIDModel is UUIDModel with soft deletes.

type SubQuery

type SubQuery interface {
	// contains filtered or unexported methods
}

SubQuery is any Query[S]; it lets WhereExists take a query over another model.

type SyncResult

type SyncResult struct{ Attached, Detached []any }

SyncResult reports what Sync / Toggle changed.

type Touches

type Touches interface{ Touches() []string }

Touches lists belongs-to relations whose parent gets a fresh updated_at whenever this model is created, saved, deleted or restored through the repository or the instance methods (Eloquent's $touches). Bulk query updates do not touch, as in Eloquent.

type ULIDKey

type ULIDKey struct {
	ID string `gorm:"primaryKey;size:26"`
}

ULIDKey is UUIDKey with a 26-character ULID (Eloquent's HasUlids).

func (*ULIDKey) BeforeCreate

func (k *ULIDKey) BeforeCreate(*gorm.DB) error

type ULIDModel

type ULIDModel struct {
	Record
	ID        string `gorm:"primaryKey;size:26"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

ULIDModel is BaseModel with a 26-character ULID key (Eloquent's HasUlids).

func (*ULIDModel) BeforeCreate

func (k *ULIDModel) BeforeCreate(*gorm.DB) error

type UUIDKey

type UUIDKey struct {
	ID string `gorm:"primaryKey;size:36"`
}

UUIDKey is an embeddable primary key that fills itself with a time-ordered UUID (version 7) on insert (Eloquent's HasUuids). It is the bare key: embed UUIDModel instead to also get the timestamps and the instance methods (Save, Delete, ...). A BeforeCreate hook on the model itself replaces this one.

func (*UUIDKey) BeforeCreate

func (k *UUIDKey) BeforeCreate(*gorm.DB) error

type UUIDModel

type UUIDModel struct {
	Record
	ID        string `gorm:"primaryKey;size:36"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

UUIDModel is BaseModel with a time-ordered UUID (version 7) string key, filled on insert (Eloquent's HasUuids). Every instance method works, unlike the bare UUIDKey.

func (*UUIDModel) BeforeCreate

func (k *UUIDModel) BeforeCreate(*gorm.DB) error

type Visible

type Visible interface{ Visible() []string }

Visible, when present, is the only set of columns ToMap / JSON includes ($visible).

Jump to

Keyboard shortcuts

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