Help us shape the future of Go! Take the 2026 Go Developer Survey

orm

package
v1.20.0-alpha.1 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BelongsTo

type BelongsTo struct {
	Related any
	// ForeignKey is the column on the parent table referencing the related row. Optional.
	ForeignKey string
	// OwnerKey is the column on the related table referenced by ForeignKey. Optional, "id".
	OwnerKey string
	OnQuery  RelationCallback
}

BelongsTo declares the inverse of HasOne / HasMany — this model holds a foreign key referencing the related row.

Defaults: ForeignKey = singular(relatedTable) + "_id"; OwnerKey = "id".

func (BelongsTo) Kind

func (BelongsTo) Kind() RelationKind

type DispatchesEvents

type DispatchesEvents interface {
	// DispatchesEvents returns the event handlers.
	DispatchesEvents() map[EventType]func(Event) error
}

type Event

type Event interface {
	// Context returns the event context.
	Context() context.Context
	// GetAttribute returns the attribute value for the given key.
	GetAttribute(key string) any
	// GetOriginal returns the original attribute value for the given key.
	GetOriginal(key string, def ...any) any
	// IsClean returns true if the given column is clean.
	IsClean(columns ...string) bool
	// IsDirty returns true if the given column is dirty.
	IsDirty(columns ...string) bool
	// Query returns the query instance.
	Query() Query
	// SetAttribute sets the attribute value for the given key.
	SetAttribute(key string, value any)
}

type EventType

type EventType string
const (
	// Create events
	EventCreating EventType = "creating"
	EventCreated  EventType = "created"

	// Update events
	EventUpdating EventType = "updating"
	EventUpdated  EventType = "updated"

	// Save events
	EventSaving EventType = "saving"
	EventSaved  EventType = "saved"

	// Delete events
	EventDeleting      EventType = "deleting"
	EventDeleted       EventType = "deleted"
	EventForceDeleting EventType = "force_deleting"
	EventForceDeleted  EventType = "force_deleted"

	// Restore events
	EventRestoring EventType = "restoring"
	EventRestored  EventType = "restored"

	// Retrieve events
	EventRetrieved EventType = "retrieved"
)

type Factory

type Factory interface {
	// Count sets the number of models that should be generated.
	Count(count int) Factory
	// Create creates a model and persists it to the database.
	Create(value any, attributes ...map[string]any) error
	// CreateQuietly creates a model and persists it to the database without firing any model events.
	CreateQuietly(value any, attributes ...map[string]any) error
	// Make creates a model and returns it, but does not persist it to the database.
	Make(value any, attributes ...map[string]any) error
}

type HasMany

type HasMany struct {
	Related    any
	ForeignKey string
	LocalKey   string
	OnQuery    RelationCallback
}

HasMany declares a one-to-many relation — the multi-result variant of HasOne.

Defaults: ForeignKey = singular(parentTable) + "_id"; LocalKey = "id".

func (HasMany) Kind

func (HasMany) Kind() RelationKind

type HasManyThrough

type HasManyThrough struct {
	Related        any
	Through        any
	FirstKey       string
	SecondKey      string
	LocalKey       string
	SecondLocalKey string
	OnQuery        RelationCallback
}

HasManyThrough is the multi-result variant of HasOneThrough.

func (HasManyThrough) Kind

type HasOne

type HasOne struct {
	// Related is a sample instance of the related model (e.g. &Profile{}).
	Related any
	// ForeignKey is the column on the related table referencing the parent. Optional.
	ForeignKey string
	// LocalKey is the column on the parent referenced by ForeignKey. Optional, defaults to "id".
	LocalKey string
	// OnQuery is a default scope applied to every query built for this relation (eager loads,
	// existence checks, aggregates, Related). Applied before any caller-supplied callback.
	OnQuery RelationCallback
}

HasOne declares a one-to-one relation where the related row holds a foreign key referencing this model.

Defaults: ForeignKey = singular(parentTable) + "_id"; LocalKey = "id".

func (HasOne) Kind

func (HasOne) Kind() RelationKind

type HasOneThrough

type HasOneThrough struct {
	Related any
	// Through is the intermediate model.
	Through any
	// FirstKey is the FK on the through table pointing at parent. Optional.
	FirstKey string
	// SecondKey is the FK on the related table pointing at through. Optional.
	SecondKey string
	// LocalKey is the PK on the parent referenced by FirstKey. Optional, "id".
	LocalKey string
	// SecondLocalKey is the PK on the through table referenced by SecondKey. Optional, "id".
	SecondLocalKey string
	OnQuery        RelationCallback
}

HasOneThrough declares a relation reached through an intermediate ("through") table.

Defaults:

FirstKey       = singular(parentTable) + "_id"
SecondKey      = singular(throughTable) + "_id"
LocalKey       = "id"
SecondLocalKey = "id"

func (HasOneThrough) Kind

func (HasOneThrough) Kind() RelationKind

type Many2Many

type Many2Many struct {
	Related any
	// Table is the pivot table name. Optional.
	Table string
	// ForeignPivotKey is the pivot column referencing the parent. Optional.
	ForeignPivotKey string
	// RelatedPivotKey is the pivot column referencing the related. Optional.
	RelatedPivotKey string
	// ParentKey is the column on the parent referenced by ForeignPivotKey. Optional, "id".
	ParentKey string
	// RelatedKey is the column on the related referenced by RelatedPivotKey. Optional, "id".
	RelatedKey string
	// PivotField is the name of the struct field on the related model that the eager loader will
	// hydrate with pivot column values (e.g. "Pivot", "UserPivot"). Optional — defaults to
	// "Pivot". The field's Go type drives both the pivot SELECT list (every db-tagged column on
	// the struct) and the hydration target. When the related model has no field by this name,
	// no Pivot hydration happens — the relation still works for joining, just doesn't surface
	// pivot columns. Use a non-default name when one related model serves multiple m2m relations
	// with different pivot schemas (e.g. Role with both UserPivot and GroupPivot fields).
	PivotField string
	// PivotTimestamps enables auto-stamping of the pivot table's created_at / updated_at columns
	// on Attach / Sync / Save (and updated_at on UpdateExistingPivot), using default column names.
	// Most users don't need to set this flag explicitly — see "Detection priority" below.
	//
	// Detection priority for pivot timestamps (highest first):
	//
	//   1. Pivot struct field with `gorm:"autoCreateTime"` / `gorm:"autoUpdateTime"` tag. Works
	//      for any field name; column name is taken from the struct's GORM schema.
	//   2. Pivot struct field named CreatedAt / UpdatedAt of type time.Time (Go/GORM convention).
	//   3. PivotTimestamps: true. Fallback for when no Pivot struct is declared (or its struct
	//      has no timestamp fields) but the underlying table still has created_at / updated_at
	//      columns you want auto-filled. Uses default column names.
	//
	// Customize column names via `gorm:"column:..."` on the Pivot struct field. There is
	// intentionally no relation-level override — the Pivot struct is the single source of truth
	// for column metadata.
	PivotTimestamps bool
	OnQuery         RelationCallback
	// OnPivotQuery scopes pivot-table SELECT / UPDATE / DELETE for Sync / Detach /
	// UpdateExistingPivot operations on this relation. Equality conditions added here are NOT
	// auto-injected into Attach INSERT rows — pass them via Attach attrs if needed.
	OnPivotQuery PivotCallback
	// Touches, when true, causes Sync / Attach / Detach / Toggle / UpdateExistingPivot on this
	// relation to bump the parent's `updated_at` after the pivot write completes (and only when
	// the operation actually changed pivot rows). Mirrors fedaco's `touchIfTouching`. Silently
	// no-ops when the parent's schema doesn't carry an updated_at field.
	Touches bool
}

Many2Many declares a many-to-many relation through a pivot table.

Defaults:

Table            = alphabetical singular pair (e.g. "post_tag")
ForeignPivotKey  = singular(parentTable) + "_id"
RelatedPivotKey  = singular(relatedTable) + "_id"
ParentKey        = "id"
RelatedKey       = "id"

func (Many2Many) Kind

func (Many2Many) Kind() RelationKind

type ModelToObserver

type ModelToObserver struct {
	Model    any
	Observer Observer
}

type ModelWithConnection

type ModelWithConnection interface {
	// Connection gets the connection name for the model.
	Connection() string
}

type ModelWithGlobalScopes

type ModelWithGlobalScopes interface {
	GlobalScopes() map[string]func(Query) Query
}

type ModelWithMorphClass

type ModelWithMorphClass interface {
	MorphClass() string
}

ModelWithMorphClass lets a model override the value written to and matched against polymorphic `*_type` columns. The override takes precedence over both the global morph map (registered via orm.MorphMap) and GORM's default of using the parent's table name.

A model that wants to be aliased as e.g. "post" in polymorphic relations declares:

func (Post) MorphClass() string { return "post" }

This is the recommended primary mechanism for aliasing morph types because it co-locates the alias with the model definition. The global morph map is provided as a fallback for models the caller cannot modify (e.g. third-party types) or for teams that prefer a single boot-time registration.

type ModelWithRelations

type ModelWithRelations interface {
	Relations() map[string]Relation
}

ModelWithRelations is implemented by every model that declares relationships. The single map returned by Relations() is the only place relations are declared. GORM relation struct tags (`foreignKey:`, `references:`, `many2many:`, `polymorphic:`) are forbidden — fields that hold related rows must be tagged `gorm:"-"`.

type ModelWithTableName

type ModelWithTableName interface {
	// TableName gets the table name for the model, without the connection prefix.
	TableName() string
}

ModelWithTableName is implemented by every model, which must declare the table it is stored in.

Declaring a table is required, not optional: the ORM resolves a model's table through this method and nothing else. The framework never derives a table name from the struct name and never pluralises, so `type User struct{ ... }` addresses no table until TableName() says which one it is. The method is the single source every part of the framework consults when it needs a model's table - reads, writes, relations and the query builder alike - so there is exactly one place to change it.

A model that declares no table is a configuration error rather than something to guess around: instead of falling back to a naming strategy, the ORM fails with a named error that reports the offending type and names the TableName() method to add.

`make:model` writes this method into every model it generates.

type MorphMany

type MorphMany struct {
	Related    any
	Name       string
	TypeColumn string
	IDColumn   string
	LocalKey   string
	OnQuery    RelationCallback
}

MorphMany is the multi-result variant of MorphOne.

func (MorphMany) Kind

func (MorphMany) Kind() RelationKind

type MorphOne

type MorphOne struct {
	Related any
	// Name is the polymorphic name (e.g. "imageable", "taggable"). Required.
	Name string
	// TypeColumn is the polymorphic type column on the related table. Optional.
	TypeColumn string
	// IDColumn is the polymorphic id column on the related table. Optional.
	IDColumn string
	// LocalKey is the column on the parent referenced by IDColumn. Optional, "id".
	LocalKey string
	OnQuery  RelationCallback
}

MorphOne declares a one-to-one polymorphic relation — the related row holds <Name>_id and <Name>_type referencing one of several possible parent kinds.

Defaults: TypeColumn = Name + "_type"; IDColumn = Name + "_id"; LocalKey = "id".

func (MorphOne) Kind

func (MorphOne) Kind() RelationKind

type MorphRelationCallback

type MorphRelationCallback func(query Query, morphType string) Query

MorphRelationCallback is the per-type variant of RelationCallback used by the *HasMorph family, matching the `function ($query, $type)` callback for whereHasMorph. The second argument is the morph type currently being scoped (the related model's morph class - the table name in GORM's polymorphic convention).

type MorphTo

type MorphTo struct {
	// Name is the polymorphic name. Required.
	Name string
	// TypeColumn is the polymorphic type column on this table. Optional.
	TypeColumn string
	// IDColumn is the polymorphic id column on this table. Optional.
	IDColumn string
	// OwnerKey is the column on each related table referenced by IDColumn. Optional, "id".
	OwnerKey string
	OnQuery  RelationCallback
}

MorphTo declares the inverse polymorphic side: this model holds <Name>_id + <Name>_type and resolves to one of several parent kinds via the morph map registry. There is no Related — the concrete type is determined per row from the type column.

Defaults: TypeColumn = Name + "_type"; IDColumn = Name + "_id"; OwnerKey = "id".

func (MorphTo) Kind

func (MorphTo) Kind() RelationKind

type MorphToMany

type MorphToMany struct {
	Related         any
	Name            string
	Table           string
	TypeColumn      string
	ForeignPivotKey string
	RelatedPivotKey string
	ParentKey       string
	RelatedKey      string
	// PivotField — see Many2Many.PivotField.
	PivotField string
	// PivotTimestamps — see Many2Many.PivotTimestamps.
	PivotTimestamps bool
	OnQuery         RelationCallback
	// OnPivotQuery — see Many2Many.OnPivotQuery.
	OnPivotQuery PivotCallback
	// Touches — see Many2Many.Touches.
	Touches bool
}

MorphToMany declares a polymorphic many-to-many — through a pivot that carries <Name>_id + <Name>_type plus a related FK.

Defaults:

Table            = pluralize(Name)  (e.g. "taggables")
TypeColumn       = Name + "_type"
ForeignPivotKey  = Name + "_id"
RelatedPivotKey  = singular(relatedTable) + "_id"
ParentKey        = "id"
RelatedKey       = "id"

func (MorphToMany) Kind

func (MorphToMany) Kind() RelationKind

type MorphedByMany

type MorphedByMany struct {
	Related         any
	Name            string
	Table           string
	TypeColumn      string
	ForeignPivotKey string
	RelatedPivotKey string
	ParentKey       string
	RelatedKey      string
	// PivotField — see Many2Many.PivotField.
	PivotField string
	// PivotTimestamps — see Many2Many.PivotTimestamps.
	PivotTimestamps bool
	OnQuery         RelationCallback
	// OnPivotQuery — see Many2Many.OnPivotQuery.
	OnPivotQuery PivotCallback
	// Touches — see Many2Many.Touches.
	Touches bool
}

MorphedByMany is the inverse side of MorphToMany — the morph value pins on the related rather than the parent. Field semantics and defaults match MorphToMany.

func (MorphedByMany) Kind

func (MorphedByMany) Kind() RelationKind

type Observer

type Observer interface {
	// Created called when the model has been created.
	Created(Event) error
	// Updated called when the model has been updated.
	Updated(Event) error
	// Deleted called when the model has been deleted.
	Deleted(Event) error
	// ForceDeleted called when the model has been force deleted.
	ForceDeleted(Event) error
}

type ObserverWithCreating

type ObserverWithCreating interface {
	// Creating called when the model is being created.
	Creating(Event) error
}

type ObserverWithDeleting

type ObserverWithDeleting interface {
	// Deleting called when the model is being deleted.
	Deleting(Event) error
}

type ObserverWithForceDeleting

type ObserverWithForceDeleting interface {
	// ForceDeleting called when the model is being force deleted.
	ForceDeleting(Event) error
}

type ObserverWithRestored

type ObserverWithRestored interface {
	// Restored called when the model has been restored.
	Restored(Event) error
}

type ObserverWithRestoring

type ObserverWithRestoring interface {
	// Restoring called when the model is being restored.
	Restoring(Event) error
}

type ObserverWithRetrieved

type ObserverWithRetrieved interface {
	// Retrieved called when the model is retrieved from the database.
	Retrieved(Event) error
}

type ObserverWithSaved

type ObserverWithSaved interface {
	// Saved called when the model has been saved.
	Saved(Event) error
}

type ObserverWithSaving

type ObserverWithSaving interface {
	// Saving called when the model is being saved.
	Saving(Event) error
}

type ObserverWithUpdating

type ObserverWithUpdating interface {
	// Updating called when the model is being updated.
	Updating(Event) error
}

type Orm

type Orm interface {
	// Config gets the database config.
	Config() database.Config
	// Connection gets an Orm instance from the connection pool.
	Connection(name string) Orm
	// DB gets the underlying database connection.
	DB() (*sql.DB, error)
	// Factory gets a new factory instance for the given model name.
	Factory() Factory
	// DatabaseName gets the current database name.
	DatabaseName() string
	// Name gets the current connection name.
	Name() string
	// Related returns a Query pre-scoped to the related rows for the given parent and relation
	// name. parent must be a non-nil pointer to a struct. The returned Query is a fresh chain —
	// call any of Where / OrderBy / Get / First / Count / etc. on it.
	//
	// Per-kind shape:
	//   - HasOne / HasMany:                     Query().Model(related).Where("<id_col>", parent.<local_key>)
	//   - BelongsTo:                            Query().Model(related).Where("<owner_key>", parent.<fk_col>)
	//   - MorphOne / MorphMany:                 HasMany shape + Where("<type_col>", desc.morphValue)
	//   - MorphTo:                              type resolved from morph map; Query().Model(<resolved>).Where("<owner_key>", parent.<id_col>)
	//   - Many2Many / MorphToMany / MorphedByMany: Query().Table(related).Joins("INNER JOIN <pivot> ON ...").Where("<pivot>.<parent_fk>", parent.<pk>)
	//   - HasOneThrough / HasManyThrough:       Query().Table(related).Joins("INNER JOIN <through> ON ...").Where("<through>.<first_key>", parent.<local_key>)
	//
	// Mirrors fedaco's model.NewRelation('foo') for the read path. Write operations live on
	// RelationWriter (see Orm.Relation) — they are not chained off this Query.
	Related(parent any, relation string) Query
	// Relation returns a RelationWriter bound to (parent, name) for FK-safe write operations.
	// All write methods (Save / Create / UpdateOrCreate / Attach / Sync / Detach / Toggle /
	// Associate / Dissociate / etc.) are reached via this builder rather than as flat methods on
	// Orm — the (parent, name) pair binds once.
	Relation(parent any, name string) RelationWriter

	// Observe registers an observer with the Orm.
	Observe(model any, observer Observer)
	// Query gets a new query builder instance.
	Query() Query
	// Fresh resets the Orm instance.
	Fresh()
	// SetQuery sets the query builder instance.
	SetQuery(query Query)
	// Transaction runs a callback wrapped in a database transaction.
	Transaction(txFunc func(tx Query) error) error
	// WithContext sets the context to be used by the Orm.
	WithContext(ctx context.Context) Orm
}

type PivotCallback

type PivotCallback func(query PivotQuery) PivotQuery

PivotCallback scopes pivot-table reads (and the corresponding diff-driven writes) for a BelongsToMany relation. Declared via Many2Many / MorphToMany / MorphedByMany's OnPivotQuery field; applied automatically to Sync / Detach / UpdateExistingPivot and Attach's duplicate- detection SELECT.

type PivotQuery

type PivotQuery interface {
	// Where adds a `column op value` clause to the pivot query. operator defaults to "=" when
	// only two args are passed (column, value).
	Where(column string, args ...any) PivotQuery
	// WhereIn adds a `column IN (...)` clause to the pivot query.
	WhereIn(column string, values []any) PivotQuery
	// WhereNotIn adds a `column NOT IN (...)` clause to the pivot query.
	WhereNotIn(column string, values []any) PivotQuery
	// WhereNull adds a `column IS NULL` clause to the pivot query.
	WhereNull(column string) PivotQuery
	// WhereNotNull adds a `column IS NOT NULL` clause to the pivot query.
	WhereNotNull(column string) PivotQuery
}

PivotQuery is a small builder surface for scoping the pivot-table SQL emitted by Sync / Attach duplicate-skip / Detach / UpdateExistingPivot. It is intentionally narrower than the full Query interface — only the WHERE-style methods that make sense on a single-table pivot read/write are exposed. Mirrors fedaco's `wherePivot` / `wherePivotIn` / `wherePivotNull` accumulators on BelongsToMany.

NOTE: PivotQuery filters only SELECT / UPDATE / DELETE on the pivot table — equality conditions added here are NOT auto-injected into INSERT rows on Attach. Callers that need the conditions to appear on inserted rows should pass them through Attach attrs.

type Query

type Query interface {
	// QueryWithRelations exposes the QueriesRelationships surface (Has / WhereHas / WithCount /
	// HasMorph / etc.). Embedding it into Query lets users chain relationship queries with the
	// rest of the builder: q.Where(...).Has("Books", ">=", 3).Get(&users).
	QueryWithRelations
	// Related returns a Query pre-scoped to the related rows for parent.name. Mirrors Orm.Related
	// but lives on Query so it can be used inside a Transaction callback. parent must be a
	// non-nil pointer to a struct.
	Related(parent any, name string) Query
	// Relation returns a RelationWriter bound to (parent, name) for FK-safe write operations.
	// Mirrors Orm.Relation but lives on Query so writes inside a Transaction callback honor the
	// transaction.
	Relation(parent any, name string) RelationWriter
	// Begin begins a new transaction
	// DEPRECATED Use BeginTransaction instead.
	Begin() (Query, error)
	// BeginTransaction begins a new transaction
	BeginTransaction() (Query, error)
	// Commit commits the changes in a transaction.
	Commit() error
	// Context gets the context used by the query.
	Context() context.Context
	// Count retrieve the "count" result of the query.
	Count() (int64, error)
	// Create inserts new record into the database.
	Create(value any) error
	// Cursor returns a cursor, use scan to iterate over the returned rows.
	Cursor() chan db.Row
	// CursorPaginate fetches a single keyset ("seek") page and scans it into dest, returning the
	// page and its cursors. It never counts, so the page carries no total, and the name is distinct
	// from Cursor, which streams rows.
	CursorPaginate(perPage int, cursor string, dest any) (db.CursorPaginator, error)
	// DB gets the underlying database connection.
	DB() (*sql.DB, error)
	// Delete deletes records matching given conditions, if the conditions are empty will delete all records.
	Delete(value ...any) (*db.Result, error)
	// Distinct preserves the selected fields and returns distinct results. Optional columns
	// specify DISTINCT ON in PostgreSQL and the distinct columns for aggregate queries.
	Distinct(columns ...string) Query
	// Driver gets the driver for the query.
	Driver() string
	// Exec executes raw sql
	Exec(sql string, values ...any) (*db.Result, error)
	// Exists returns true if matching records exist; otherwise, it returns false.
	Exists() (bool, error)
	// Find finds records that match given conditions.
	Find(dest any, conds ...any) error
	// FindOr finds the first record matching the given id, or runs the callback when there is none.
	FindOr(dest any, id any, callback func() error) error
	// FindOrFail finds records that match given conditions or throws an error.
	FindOrFail(dest any, conds ...any) error
	// First finds record that match given conditions.
	First(dest any) error
	// FirstOr finds the first record that matches the given conditions or
	// execute the callback and return its result if no record is found.
	FirstOr(dest any, callback func() error) error
	// FirstOrCreate finds the first record that matches the given attributes
	// or create a new one with those attributes if none was found.
	FirstOrCreate(dest any, conds ...any) error
	// FirstOrFail finds the first record that matches the given conditions or throws an error.
	FirstOrFail(dest any) error
	// FirstOrNew finds the first record that matches the given conditions or
	// return a new instance of the model initialized with those attributes.
	FirstOrNew(dest any, attributes any, values ...any) error
	// FindMany retrieves the rows whose primary key is one of ids into dest, mirroring Laravel's
	// Builder::findMany: an empty list reads nothing and leaves dest empty.
	FindMany(dest any, ids []any, columns ...any) error
	// ForceDelete forces delete records matching given conditions.
	ForceDelete(value ...any) (*db.Result, error)
	// Get retrieves all rows from the database. A column list may be given for this call only,
	// mirroring Laravel's Builder::get($columns); the query's own selection is left untouched.
	Get(dest any, columns ...any) error
	// Group specifies the group method on the query.
	// DEPRECATED Use GroupBy instead.
	Group(column string) Query
	// GroupBy specifies the group method on the query.
	GroupBy(column ...string) Query
	// Having specifying HAVING conditions for the query.
	Having(query any, args ...any) Query
	// InRandomOrder specifies the order randomly.
	InRandomOrder() Query
	// InTransaction checks if the query is in a transaction.
	InTransaction() bool
	// Join specifying JOIN conditions for the query.
	Join(query string, args ...any) Query
	// Limit the number of records returned.
	Limit(limit int) Query
	// Load loads a relationship for the model. args may be a callback or other
	// shapes accepted by With (e.g. "Books:id,name" column pruning is supported by
	// embedding the column list in relation; a callback may be passed via args).
	Load(dest any, relation string, args ...any) error
	// LoadMissing loads a relationship for the model that is not already loaded.
	// args follow the same shapes as Load.
	LoadMissing(dest any, relation string, args ...any) error
	// LockForUpdate locks the selected rows in the table for updating.
	LockForUpdate() Query
	// Model sets the model instance to be queried.
	Model(value any) Query
	// OfMany configures a HasOne or MorphOne relation to return the row whose value of column
	// matches the given SQL aggregate ("MAX" / "MIN") within each parent. Intended for use
	// inside a With(...) callback so the rewrite is local to a single relation:
	//
	//	q.With("LatestImage", func(q orm.Query) orm.Query {
	//	    return q.OfMany("created_at", "MAX")
	//	})
	OfMany(column, aggregate string) Query
	// LatestOfMany is shorthand for OfMany(column, "MAX") with column defaulting to "id" when
	// empty. Mirrors fedaco's latestOfMany().
	LatestOfMany(column ...string) Query
	// OldestOfMany is shorthand for OfMany(column, "MIN") with column defaulting to "id" when
	// empty. Mirrors fedaco's oldestOfMany().
	OldestOfMany(column ...string) Query
	// Offset specifies the number of records to skip before starting to return the records.
	Offset(offset int) Query
	// Omit specifies columns that should be omitted from the query.
	Omit(columns ...string) Query
	// Order specifies the order in which the results should be returned.
	// DEPRECATED Use OrderByRaw instead.
	Order(value any) Query
	// OrderBy specifies the order should be ascending.
	OrderBy(column string, direction ...string) Query
	// OrderByDesc specifies the order should be descending.
	OrderByDesc(column string) Query
	// OrderByRaw specifies the order should be raw.
	OrderByRaw(raw string) Query
	// Latest orders by the given column descending, defaulting to the model's created-at column,
	// mirroring Laravel's Builder::latest.
	Latest(column ...string) Query
	// Oldest orders by the given column ascending, defaulting to the model's created-at column,
	// mirroring Laravel's Builder::oldest.
	Oldest(column ...string) Query
	// OrWhere add an "or where" clause to the query.
	OrWhere(query any, args ...any) Query
	// OrWhereBetween adds an "or where column between x and y" clause to the query.
	OrWhereBetween(column string, x, y any) Query
	// OrWhereIn adds an "or where column in" clause to the query.
	OrWhereIn(column string, values []any) Query
	// OrWhereJsonContains adds an "or where JSON contains" clause to the query.
	OrWhereJsonContains(column string, value any) Query
	// OrWhereJsonContainsKey add a clause that determines if a JSON path exists to the query.
	OrWhereJsonContainsKey(column string) Query
	// OrWhereJsonDoesntContain add an "or where JSON not contains" clause to the query.
	OrWhereJsonDoesntContain(column string, value any) Query
	// OrWhereJsonDoesntContainKey add a clause that determines if a JSON path does not exist to the query.
	OrWhereJsonDoesntContainKey(column string) Query
	// OrWhereJsonLength add an "or where JSON length" clause to the query. Mirrors
	// Laravel's Builder::orWhereJsonLength, so args is either (length) for an
	// equality check or (operator, length) for a comparison.
	OrWhereJsonLength(column string, args ...any) Query
	// OrWhereNotBetween adds an "or where column not between x and y" clause to the query.
	OrWhereNotBetween(column string, x, y any) Query
	// OrWhereNotIn adds an "or where column not in" clause to the query.
	OrWhereNotIn(column string, values []any) Query
	// OrWhereNull adds a "or where column is null" clause to the query.
	OrWhereNull(column string) Query
	// Paginate the given query into a simple paginator.
	Paginate(page, limit int, dest any, total *int64) error
	// Pluck retrieves a single column from the database.
	Pluck(column string, dest any) error
	// Sole retrieves the one row the query matches, mirroring Laravel's Builder::sole: no row is an
	// error, and so is more than one.
	Sole(dest any) error
	// Raw creates a raw query.
	Raw(sql string, values ...any) Query
	// Restore restores a soft deleted model.
	Restore(model ...any) (*db.Result, error)
	// Rollback rolls back the changes in a transaction.
	Rollback() error
	// Save updates value in a database
	Save(value any) error
	// SaveQuietly updates value in a database without firing events
	SaveQuietly(value any) error
	// Scan scans the query result and populates the destination object.
	Scan(dest any) error
	// Scopes applies one or more query scopes.
	Scopes(funcs ...func(Query) Query) Query
	// Select specifies fields that should be retrieved from the database.
	Select(columns ...string) Query
	// SelectRaw specifies a raw SQL query for selecting fields.
	SelectRaw(query any, args ...any) Query
	// SharedLock locks the selected rows in the table.
	SharedLock() Query
	// Sum calculates the sum of a column's values and populates the destination object.
	Sum(column string, dest any) error
	// Avg calculates the average of a column's values.
	Avg(column string, dest any) error
	// Min calculates the minimum value of a column.
	Min(column string, dest any) error
	// Max calculates the maximum value of a column.
	Max(column string, dest any) error
	// Increment adds amount (default 1) to the column of every matching row, refreshing the model's
	// updated-at column and writing extra, mirroring Laravel's Builder::increment.
	Increment(column string, args ...any) (*db.Result, error)
	// Decrement subtracts amount (default 1) from the column of every matching row, refreshing the
	// model's updated-at column and writing extra, mirroring Laravel's Builder::decrement.
	Decrement(column string, args ...any) (*db.Result, error)
	// Touch sets the model's updated-at column (or the named column) to now on every matching row,
	// mirroring Laravel's Builder::touch. A model without an updated-at column touches nothing.
	Touch(column ...string) (*db.Result, error)
	// Table specifies the table for the query.
	//
	// A string is a bare table name, and the connection prefix is applied to it when the statement
	// is rendered. Pass a query.Raw expression to name a table that is already qualified — for
	// example a catalog name that must be addressed verbatim — and it is rendered as given, with no
	// prefix added. That mirrors Laravel's withoutTablePrefix(), which the query builder itself does
	// not expose.
	Table(name any, args ...any) Query
	// ToSql returns the query as a SQL string.
	ToSql() ToSql
	// ToRawSql returns the query as a raw SQL string.
	ToRawSql() ToSql
	// Update updates records with the given column and values
	Update(column any, value ...any) (*db.Result, error)
	// UpdateOrCreate finds the first record that matches the given attributes
	// or create a new one with those attributes if none was found.
	UpdateOrCreate(dest any, attributes any, values any) error
	// Upsert inserts the rows, updating the listed columns of a row whose uniqueBy columns already
	// exist. It mirrors Laravel's Builder::upsert on a model: the model's timestamps are stamped,
	// the created-at column is left alone on update, and the updated-at column is always refreshed.
	// With no update columns every inserted column is updated; an empty update list inserts only.
	Upsert(values any, uniqueBy []string, update ...any) (*db.Result, error)
	// Value retrieves a single column of the first row into dest, mirroring Laravel's
	// Builder::value.
	Value(column string, dest any) error
	// UseWritePdo forces the query's reads to run on the write connection rather than the reader
	// replica. Mirrors Laravel's Builder::useWritePdo.
	UseWritePdo() Query
	// When executes the callback if the condition is true, otherwise the falseCallback if it is given.
	When(condition bool, callback func(query Query) Query, falseCallback ...func(query Query) Query) Query
	// Where add a "where" clause to the query.
	Where(query any, args ...any) Query
	// WhereAll adds a "where all columns match" clause to the query.
	WhereAll(columns []string, args ...any) Query
	// WhereAny adds a "where any of columns match" clause to the query.
	WhereAny(columns []string, args ...any) Query
	// WhereBetween adds a "where column between x and y" clause to the query.
	WhereBetween(column string, x, y any) Query
	// WhereIn adds a "where column in" clause to the query.
	WhereIn(column string, values []any) Query
	// WhereJsonContains add a "where JSON contains" clause to the query.
	WhereJsonContains(column string, value any) Query
	// WhereJsonContainsKey add a clause that determines if a JSON path exists to the query.
	WhereJsonContainsKey(column string) Query
	// WhereJsonDoesntContain add a "where JSON not contains" clause to the query.
	WhereJsonDoesntContain(column string, value any) Query
	// WhereJsonDoesntContainKey add a clause that determines if a JSON path does not exist to the query.
	WhereJsonDoesntContainKey(column string) Query
	// WhereJsonLength add a "where JSON length" clause to the query. Mirrors
	// Laravel's Builder::whereJsonLength, so args is either (length) for an
	// equality check or (operator, length) for a comparison.
	WhereJsonLength(column string, args ...any) Query
	// WhereNone adds a "where none of columns match" clause to the query.
	WhereNone(columns []string, args ...any) Query
	// WhereNotBetween adds a "where column not between x and y" clause to the query.
	WhereNotBetween(column string, x, y any) Query
	// WhereNotIn adds a "where column not in" clause to the query.
	WhereNotIn(column string, values []any) Query
	// WhereNotNull adds a "where column is not null" clause to the query.
	WhereNotNull(column string) Query
	// WhereNull adds a "where column is null" clause to the query.
	WhereNull(column string) Query
	// WhereKey constrains the query to the row (or rows) with the given primary key, mirroring
	// Laravel's Builder::whereKey. A slice stands for several keys; a model stands for its own key.
	WhereKey(id any) Query
	// WhereKeyNot excludes the row (or rows) with the given primary key, mirroring Laravel's
	// Builder::whereKeyNot.
	WhereKeyNot(id any) Query
	// Except excludes the given models by primary key, mirroring Laravel's Builder::except.
	Except(models any) Query
	// WithAttributes adds the attributes as where equalities and remembers them for every model the
	// query creates afterwards, mirroring Laravel's Builder::withAttributes. Pass a map, or a column
	// with its value. asConditions false records the attributes without adding the where clauses.
	WithAttributes(attributes any, args ...any) Query
	// WithoutEvents disables event firing for the query.
	WithoutEvents() Query
	// WithoutGlobalScopes disables all global scopes for the query.
	WithoutGlobalScopes(names ...string) Query
	// Without removes the given relations from the eager-load list set by With.
	// Mirrors fedaco's without().
	Without(relations ...string) Query
	// WithTrashed allows soft deleted models to be included in the results.
	WithTrashed() Query
	// OnlyTrashed restricts the results to the soft deleted models, mirroring Laravel's
	// SoftDeletes::onlyTrashed: the automatic "not deleted" predicate is dropped and replaced by a
	// "deleted_at is not null" one.
	OnlyTrashed() Query
	// With eagerly loads the given relationships using Goravel's own loader (does not
	// delegate to GORM Preload). Accepts the union of fedaco's with(...) shapes:
	//
	//   q.With("Books")
	//   q.With("Books", cb)                                        // string + callback
	//   q.With("Books", "Roles", "Address")                        // multiple strings
	//   q.With("Books:id,name")                                    // column pruning
	//   q.With(map[string]orm.RelationCallback{"Books": cb})       // map of name -> callback
	//   q.With([]any{"Books", map[string]orm.RelationCallback{"Roles": cb}})
	//   q.With("Books.Author")                                     // nested
	//   q.With("Books.Author", cb)                                 // nested + callback
	//
	// Supports HasOne, HasMany, BelongsTo, BelongsToMany, MorphOne, MorphMany,
	// HasOneThrough and HasManyThrough.
	With(args ...any) Query
	// WithOnly clears the eager-load list set by With, then adds the given
	// relations. Mirrors fedaco's withOnly().
	WithOnly(args ...any) Query
}

type QueryWithClone

type QueryWithClone interface {
	Clone() Query
}

QueryWithClone is the optional capability a Query implements when it can hand out an independent copy. Orm.Query() uses it to return a new builder per call, which is what the Query() contract promises and what Laravel's Connection::query() does. A Query without it is returned as stored.

type QueryWithContext

type QueryWithContext interface {
	WithContext(ctx context.Context) Query
}

type QueryWithObserver

type QueryWithObserver interface {
	Observe(model any, observer Observer)
}

type QueryWithRelations

type QueryWithRelations interface {
	// Has adds a relationship count / exists condition to the query.
	// Defaults to operator ">=" and count 1.
	Has(relation string, args ...any) Query
	// OrHas adds a relationship count / exists condition to the query with an "or" conjunction.
	OrHas(relation string, args ...any) Query
	// DoesntHave adds a relationship absence condition - equivalent to Has(rel, "<", 1).
	DoesntHave(relation string, args ...any) Query
	// OrDoesntHave adds a relationship absence condition with an "or" conjunction.
	OrDoesntHave(relation string, args ...any) Query
	// WhereHas adds a relationship count / exists condition to the query with where clauses.
	// Identical semantics to Has but conventionally used with a callback first arg.
	WhereHas(relation string, args ...any) Query
	// OrWhereHas adds a relationship count / exists condition to the query with where clauses
	// and an "or" conjunction.
	OrWhereHas(relation string, args ...any) Query
	// WhereDoesntHave adds a relationship absence condition to the query with where clauses.
	WhereDoesntHave(relation string, args ...any) Query
	// OrWhereDoesntHave adds a relationship absence condition to the query with where clauses
	// and an "or" conjunction.
	OrWhereDoesntHave(relation string, args ...any) Query

	// HasMorph adds a polymorphic relationship count / exists condition to the query.
	// types is a slice of model instances (e.g. []any{&Post{}, &Video{}}); the morph value
	// used in the type column is derived from each model's table name.
	//
	// Note: auto-discovery of distinct morph values via `types = ['*']` is not supported.
	// An explicit list of model instances is required.
	HasMorph(relation string, types []any, args ...any) Query
	// OrHasMorph adds a polymorphic relationship count / exists condition with an "or" conjunction.
	OrHasMorph(relation string, types []any, args ...any) Query
	// DoesntHaveMorph adds a polymorphic relationship absence condition.
	DoesntHaveMorph(relation string, types []any, args ...any) Query
	// OrDoesntHaveMorph adds a polymorphic relationship absence condition with an "or" conjunction.
	OrDoesntHaveMorph(relation string, types []any, args ...any) Query
	// WhereHasMorph adds a polymorphic relationship count / exists condition to the query with
	// where clauses. Callbacks may be MorphRelationCallback for per-type scoping.
	WhereHasMorph(relation string, types []any, args ...any) Query
	// OrWhereHasMorph adds a polymorphic relationship count / exists condition with where clauses
	// and an "or" conjunction.
	OrWhereHasMorph(relation string, types []any, args ...any) Query
	// WhereDoesntHaveMorph adds a polymorphic relationship absence condition with where clauses.
	WhereDoesntHaveMorph(relation string, types []any, args ...any) Query
	// OrWhereDoesntHaveMorph adds a polymorphic relationship absence condition with where clauses
	// and an "or" conjunction.
	OrWhereDoesntHaveMorph(relation string, types []any, args ...any) Query

	// WithAggregate adds a sub-select query to include an aggregate value for a relationship.
	// fn must be one of: count, max, min, sum, avg, exists.
	WithAggregate(relation, column, fn string, args ...any) Query
	// WithCount adds sub-select queries to count the relations. Each entry may be either a
	// string ("Books") or a RelationCount struct for scoped/aliased counts.
	WithCount(relations ...any) Query
	// WithMax adds sub-select queries to include the max of the relation's column.
	WithMax(relation, column string, args ...any) Query
	// WithMin adds sub-select queries to include the min of the relation's column.
	WithMin(relation, column string, args ...any) Query
	// WithSum adds sub-select queries to include the sum of the relation's column.
	WithSum(relation, column string, args ...any) Query
	// WithAvg adds sub-select queries to include the average of the relation's column.
	WithAvg(relation, column string, args ...any) Query
	// WithExists adds sub-select queries to include the existence of related models. The result
	// is emitted as `CASE WHEN EXISTS (...) THEN 1 ELSE 0 END` for cross-dialect portability
	// (SQL Server has no boolean literal), but the dest field may be either `bool` or an integer
	// type - Go's database/sql layer converts 0/1 ints to bool automatically.
	WithExists(relations ...string) Query

	// WhereRelation adds a relationship existence condition with a single where on the related
	// rows, mirroring Laravel's whereRelation: WhereRelation("Books", "title", "Go") is
	// WhereHas("Books", func(q) { q.Where("title", "Go") }). A callback may be given in place of
	// the column to constrain the related rows freely.
	WhereRelation(relation string, column any, args ...any) Query
	// OrWhereRelation is WhereRelation with an "or" conjunction.
	OrWhereRelation(relation string, column any, args ...any) Query
	// WhereDoesntHaveRelation adds a relationship absence condition with a single where on the
	// related rows, mirroring Laravel's whereDoesntHaveRelation.
	WhereDoesntHaveRelation(relation string, column any, args ...any) Query
	// OrWhereDoesntHaveRelation is WhereDoesntHaveRelation with an "or" conjunction.
	OrWhereDoesntHaveRelation(relation string, column any, args ...any) Query
	// WithWhereHas constrains the query by the relation and eager loads it with the same
	// constraint, mirroring Laravel's withWhereHas.
	WithWhereHas(relation string, args ...any) Query
	// WhereBelongsTo constrains the query to the rows that belong to the given model (or models),
	// mirroring Laravel's whereBelongsTo: the parent's foreign key is matched against the related
	// models' keys. The relation defaults to the related model's type name.
	WhereBelongsTo(related any, relation ...string) Query
	// OrWhereBelongsTo is WhereBelongsTo with an "or" conjunction.
	OrWhereBelongsTo(related any, relation ...string) Query
}

QueryWithRelations is the Go port of the QueriesRelationships mixin from Laravel and the 1:1 TypeScript port in fedaco at libs/fedaco/src/fedaco/mixins/queries-relationships.ts.

Where the upstream framework has first-class Relation objects with getRelationExistenceQuery methods, GORM models its relationships through struct-tag metadata. The bridge here surfaces relationship-existence and aggregate-subselect queries on top of that metadata. Callers can write, for example:

users := []User{}
query.Query().Has("Books", ">=", 3).WithCount("Roles").Get(&users)

Existence-style methods (Has / OrHas / DoesntHave / WhereHas / ...) accept a variadic args slice that may carry, in any order:

  • a RelationCallback or func(Query) Query to scope the inner subquery
  • a string operator (e.g. ">=", "<", ">", "=") - defaults to ">="
  • an int count - defaults to 1

Morph-style methods take an additional types []any of model instances; the morph value used in the type column is derived from each model's GORM-resolved table name (e.g. *User -> "users").

QueryWithRelations is embedded into Query, so all of these methods are also reachable directly off Query without a type assertion.

type Relation

type Relation interface {
	// Kind returns the relation flavour for diagnostics (error messages, logging). The resolver
	// itself dispatches by Go type, not by the Kind value.
	Kind() RelationKind
}

Relation is the sealed interface implemented by every per-kind relation declaration struct (HasOne, HasMany, BelongsTo, Many2Many, MorphOne, MorphMany, MorphTo, MorphToMany, MorphedByMany, HasOneThrough, HasManyThrough). The relation() method is unexported so external packages cannot define new kinds — the resolver type-switches on the closed set defined here.

Models declare their relationships in a single map:

func (User) Relations() map[string]orm.Relation {
    return map[string]orm.Relation{
        "Books":   orm.HasMany{Related: &Book{}},
        "Roles":   orm.Many2Many{Related: &Role{}, Table: "user_roles"},
        "Houses":  orm.MorphMany{Related: &House{}, Name: "houseable"},
        "Posts":   orm.HasManyThrough{Related: &Post{}, Through: &Account{}},
    }
}

All relation fields on the model struct must be tagged `gorm:"-"` so GORM doesn't try to auto-resolve them from struct tags.

type RelationCallback

type RelationCallback func(query Query) Query

RelationCallback is the signature of a closure used to scope a relationship existence query. Mirrors the (q) => void closure shape from the QueriesRelationships mixin. The returned Query is the inner subquery used for has / whereHas / withCount / etc.

type RelationCount

type RelationCount struct {
	// Name is the relation method/field name on the parent model (e.g. "Books").
	Name string
	// Alias overrides the default `<relation>_count` column alias when non-empty.
	Alias string
	// Callback scopes the inner count subquery, mirroring the upstream array-keyed callback shape.
	Callback RelationCallback
}

RelationCount is an entry accepted by WithCount that pairs a relation name with an optional scope callback and result alias. Equivalent to the array-keyed `withCount(['posts as p_count' => fn ...])` idiom in Laravel, expressed as a Go struct:

q.WithCount(orm.RelationCount{Name: "Books", Alias: "book_total", Callback: func(q) q.Where(...)})

type RelationKind

type RelationKind string

RelationKind names a relationship flavour for diagnostic / error-message use only. The per-kind structs below (HasOne, HasMany, ...) are the actual user-facing declaration types; the RelationKind constants exist purely so error messages can refer to a kind by name.

const (
	KindHasOne         RelationKind = "hasOne"
	KindHasMany        RelationKind = "hasMany"
	KindBelongsTo      RelationKind = "belongsTo"
	KindMany2Many      RelationKind = "many2Many"
	KindMorphOne       RelationKind = "morphOne"
	KindMorphMany      RelationKind = "morphMany"
	KindMorphTo        RelationKind = "morphTo"
	KindMorphToMany    RelationKind = "morphToMany"
	KindMorphedByMany  RelationKind = "morphedByMany"
	KindHasOneThrough  RelationKind = "hasOneThrough"
	KindHasManyThrough RelationKind = "hasManyThrough"
)

type RelationWriter

type RelationWriter interface {
	// Save inserts or updates child as a member of the relation. Sets child's foreign key (and
	// morph_type for MorphOne/MorphMany) from parent's local key, then persists child.
	// Supported kinds: HasOne, HasMany, MorphOne, MorphMany, Many2Many, MorphToMany.
	Save(child any) error
	// SaveMany is the slice form of Save. children must be a slice or pointer-to-slice.
	SaveMany(children any) error
	// SaveWithPivot is Save with caller-supplied pivot column values for BelongsToMany kinds.
	// On HasOneOrMany kinds attrs is ignored (no pivot row).
	SaveWithPivot(child any, attrs map[string]any) error
	// SaveManyWithPivot is the slice form of SaveWithPivot. attrsPerChild is keyed by the related
	// PK of each child; an entry may be nil to attach without extra columns.
	SaveManyWithPivot(children any, attrsPerChild map[any]map[string]any) error

	// Create persists a new related row. For HasOneOrMany kinds the framework pre-sets FK (and
	// morph type) on dest from parent, then inserts. For BelongsToMany kinds inserts dest first,
	// then writes a pivot row.
	Create(dest any) error
	// CreateMany is the slice form of Create.
	CreateMany(dests any) error

	// FindOrNew finds the related row with primary key id. If absent, fills dest with a new
	// instance of the related model and pre-sets FK (and morph type) — does NOT persist.
	FindOrNew(id any, dest any) error
	// FirstOrNew finds the first related row matching attrs. If absent, fills dest with a new
	// instance carrying attrs+values and pre-set FK — does NOT persist.
	FirstOrNew(attrs, values map[string]any, dest any) error
	// FirstOrCreate is FirstOrNew that persists when no matching row exists. For BelongsToMany
	// kinds also writes a pivot row.
	FirstOrCreate(attrs, values map[string]any, dest any) error
	// UpdateOrCreate finds the first related row matching attrs (or creates one), then overlays
	// values onto it and persists. For BelongsToMany kinds also writes a pivot row when freshly
	// created. Always saves dest.
	UpdateOrCreate(attrs, values map[string]any, dest any) error

	// Associate sets parent's foreign key (and morph_type for MorphTo) to point at owner, then
	// persists parent. Supported kinds: BelongsTo, MorphTo. owner must be a non-nil pointer to a
	// struct.
	Associate(owner any) error
	// Dissociate clears parent's foreign key (and morph_type for MorphTo) and persists parent.
	// Supported kinds: BelongsTo, MorphTo.
	Dissociate() error

	// Attach inserts pivot rows linking parent to each id in ids. For polymorphic pivots the
	// morph_type column is filled from the parent's morph value. Skips ids that already have a
	// pivot row. Supported kinds: Many2Many, MorphToMany, MorphedByMany.
	Attach(ids []any) error
	// AttachWithPivot is Attach with per-row pivot column values. The map key is the related id;
	// the map value is the column-name-to-value map applied to that pivot row.
	AttachWithPivot(idsWithAttrs map[any]map[string]any) error
	// Detach removes pivot rows linking parent to the given ids. With nil ids, removes all pivot
	// rows for parent (and morph type, for polymorphic). Returns the number of rows removed.
	Detach(ids ...any) (int64, error)

	// Sync replaces parent's pivot rows so they exactly match ids: detaches missing entries,
	// attaches new ones, leaves existing untouched.
	Sync(ids []any) (*db.SyncResult, error)
	// SyncWithPivot is Sync with per-ID pivot column values. The map key is the related id; the
	// map value is the column-name-to-value map applied to that pivot row. For existing pivot
	// rows with non-empty attrs, updates the pivot columns (reported in SyncResult.Updated).
	SyncWithPivot(idsWithAttrs map[any]map[string]any) (*db.SyncResult, error)
	// SyncWithPivotValues is a convenience wrapper that applies the same pivot column values to
	// all ids.
	SyncWithPivotValues(ids []any, pivotValues map[string]any) (*db.SyncResult, error)
	// SyncWithoutDetaching is Sync minus the detach step — adds missing entries only.
	SyncWithoutDetaching(ids []any) (*db.SyncResult, error)
	// SyncWithoutDetachingWithPivot is SyncWithPivot minus the detach step.
	SyncWithoutDetachingWithPivot(idsWithAttrs map[any]map[string]any) (*db.SyncResult, error)
	// Toggle attaches missing entries and detaches existing ones.
	Toggle(ids []any) (*db.SyncResult, error)
	// ToggleWithPivot is Toggle with per-ID pivot column values for newly attached rows.
	ToggleWithPivot(idsWithAttrs map[any]map[string]any) (*db.SyncResult, error)

	// UpdateExistingPivot updates pivot columns for an already-attached id.
	UpdateExistingPivot(id any, attrs map[string]any) (int64, error)
}

RelationWriter is the write-side builder for a single (parent, relation) pair, returned by Query.Relation / Orm.Relation. All write operations on a relation flow through this interface; flat methods like Orm.Save(parent, relation, child) intentionally do not exist.

RelationWriter has NO Where / OrderBy / chain methods — fedaco-style "where(...).updateOrCreate(...)" composition is not supported because Goravel's relation system is metadata-driven (declared via ModelWithRelations.Relations()), not method-driven. Search criteria for the find-then-write methods (FirstOrNew/FirstOrCreate/UpdateOrCreate/FindOrNew) are passed via the attrs map, which is combined with the relation's foreign-key scope.

For chained reads on a relation, use Query.Related(parent, name) instead — it returns a regular Query already scoped by the relation's foreign key, supporting the full Where/OrderBy/Get chain.

type ToSql

type ToSql interface {
	Count() string
	Create(value any) string
	Delete(value ...any) string
	Find(dest any, conds ...any) string
	First(dest any) string
	ForceDelete(value ...any) string
	Get(dest any) string
	Pluck(column string, dest any) string
	Save(value any) string
	Sum(column string, dest any) string
	Update(column any, value ...any) string
}

Jump to

Keyboard shortcuts

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