Documentation
¶
Overview ¶
Package service implements the basic CRUD operations for models.
For any not-in-the-box lower level database operations, you can implement your own services with the orm.DB (a *gorm.DB) instance.
Index ¶
- Variables
- func Count[T any](ctx context.Context, options ...QueryOption) (count int64, err error)
- func CountAssociations(ctx context.Context, model any, field string, options ...QueryOption) (count int64, err error)
- func Create(ctx context.Context, model any, in CreateMode) error
- func Delete(ctx context.Context, model any) (rowsAffected int64, err error)
- func DeleteByID[T orm.Model](ctx context.Context, id any) (rowsAffected int64, err error)
- func DeleteNested[P orm.Model, T any](ctx context.Context, parent *P, field string, child *T) error
- func DeleteNestedByID[P orm.Model, T orm.Model](ctx context.Context, parentID any, field string, childID any) error
- func Get[T any](ctx context.Context, dest any, options ...QueryOption) error
- func GetAssociations(ctx context.Context, model any, field string, dest any, options ...QueryOption) error
- func GetByID[T orm.Model](ctx context.Context, id any, dest any, options ...QueryOption) error
- func GetMany[T any](ctx context.Context, dest any, options ...QueryOption) error
- func Update(ctx context.Context, model any) (rowsAffected int64, err error)
- func UpdateField[T orm.Model](ctx context.Context, id any, field string, value interface{}) (rowsAffected int64, err error)
- type CreateMode
- type QueryOption
Constants ¶
This section is empty.
Variables ¶
var ( ErrNoIdentityField = errors.New("no identity field found") ErrNilID = errors.New("id is nil") )
var ( ErrNoRecord = errors.New("no record found") ErrMultipleRecords = errors.New("multiple records found") )
Functions ¶
func CountAssociations ¶
func CountAssociations(ctx context.Context, model any, field string, options ...QueryOption) (count int64, err error)
CountAssociations count matched associations (model.field).
func Create ¶
func Create(ctx context.Context, model any, in CreateMode) error
Create creates a model in the database. Nested models associated with the model will be created as well.
There are two mode of creating a model:
- IfNotExist: creates a model record if it does not exist.
- NestInto: creates a nested model of the parent model.
Note:
user := User{ profile: Profile{ ... } } Create(&user, IfNotExist()) // creates user, user.profile group := GetByID[Group](123) Create(&user, NestInto(&group, "users")) // user is already in the database: just add it into group.users
func DeleteByID ¶
DeleteByID deletes a model from database by its ID.
func DeleteNested ¶
DeleteNested remove the association between parent and child.
func DeleteNestedByID ¶
func DeleteNestedByID[P orm.Model, T orm.Model](ctx context.Context, parentID any, field string, childID any) error
DeleteNestedByID remove the association between parent and child.
func Get ¶
Get fetch a single model T into dest.
Shout out to GORM for the feature called Smart Select Fields:
https://gorm.io/docs/advanced_query.html#Smart-Select-Fields ,
we can Get a specific part of fields of model T into a "view model" struct. So the generic type T is the type of the Model (which mapping to a scheme, i.e. a table in the database), while the parameter dest given the type of the view model (for API responses or any other usage). Of course, you can always use the original model struct T as its view model, in which case the dest parameter should be a *T.
Use FilterBy and Where options to query on specific fields, and by adding Preload options, you can preload relationships, for example:
Get[User](&user, FilterBy("id", 10), Preload("Sessions")))
means:
SELECT * FROM users WHERE id = 10; // into dest SELECT * FROM sessions WHERE user_id = 10; // into user.Sessions
Because this getting model by id is a common operation, a shortcut GetByID is provided. (but you still have to add Preload options if needed)
func GetAssociations ¶
func GetAssociations(ctx context.Context, model any, field string, dest any, options ...QueryOption) error
GetAssociations find matched associations (model.field) into dest.
func GetByID ¶
GetByID is a shortcut for Get[T](&T, FilterBy("id", id))
Notice: "id" here is the column (or field) name of the primary key of the model which is indicated by the Identity method of orm.Model. So GetByID only works for models that implement the orm.Model interface.
func GetMany ¶
GetMany returns a list of models T into dest. The dest should be a pointer to a slice of "view model" struct (e.g. *[]*T). See the documentation of Get function above for more details.
Adding options parameters, you can query with specific conditions:
- WithPage(limit, offset) => pagination
- OrderBy(field, descending) => ordering
- FilterBy(field, value) => WHERE field=value condition
- Where(query, args...) => for more complicated queries
- Preload(field) => preload a relationship
- PreloadAll() => preload all associations
Example:
GetMany[User](&users, WithPage(10, 0), OrderBy("age", true), FilterBy("name", "John"))
means:
SELECT * FROM users WHERE name = "John" ORDER BY age desc LIMIT 10 OFFSET 0; // into users
Types ¶
type CreateMode ¶
CreateMode is the way to create a model:
- IfNotExist: creates a model if it does not exist.
- NestInto: creates a nested model of the parent model.
TODO: I dont think it's reasonable to (ab)use functional option pattern here
to handle different kinds of creates (CreateMode). It is a temporary solution and should be replaced by seperated functions, say, Create(model) and CreateNested(parentID, field, child).
func NestInto ¶
func NestInto(parent any, field string) CreateMode
NestInto creates a nested model of the parent model in the database. Say, if you have a model User and a model Profile:
CREATE TABLE `user` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, ...) CREATE TABLE `profile` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, ...) CREATE TABLE `user_profiles` ( // a join table `user_id` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL, PRIMARY KEY (`user_id`, `profile_id`) FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`) )
You can create a Profile of a User (i.e. the user has the profile) by calling
Create(&userProfile, NestInto(&user))
The userProfile will be created in the database and associated with the user:
INSERT INTO profile ... INSERT INTO user_profiles (user_id, profile_id)
This is useful to handle POSTs like /api/users/{user_id}/profile
type QueryOption ¶
QueryOption is a function that can be used to construct a query.
func FilterBy ¶
func FilterBy(field string, value any) QueryOption
FilterBy is a query option that sets WHERE field=value condition for GetMany. It can be applied multiple times (for multiple conditions).
Example:
GetMany[User](&users, FilterBy("name", "John"), FilterBy("age", 10))
means:
SELECT * FROM users WHERE name = "John" AND age = 10 ; // into users
func OrderBy ¶
func OrderBy(field string, descending bool) QueryOption
OrderBy is a query option that sets ordering for GetMany. It can be applied multiple times (for multiple orders).
func Preload ¶
func Preload(field string, options ...QueryOption) QueryOption
Preload preloads a relationship (eager loading). It can be applied multiple times (for multiple preloads). And nested preloads (like "User.Sessions") are supported.
Passing QueryOptions to custom preloading SQL, see https://gorm.io/docs/preload.html#Custom-Preloading-SQL
func PreloadAll ¶
func PreloadAll() QueryOption
PreloadAll to Preload all associations. clause.Associations won’t preload nested associations!
func Where ¶
func Where(query any, args ...any) QueryOption
Where offers a more flexible way to set WHERE conditions. Equivalent to gorm.DB.Where(...), see:
https://gorm.io/docs/query.html#Conditions
Example:
GetMany[User](&users, Where("name = ? AND age > ?", "John", 10))
means:
SELECT * FROM users WHERE name = "John" AND age > 10 ; // into users
func WithPage ¶
func WithPage(limit int, offset int) QueryOption
WithPage is a query option that sets pagination for GetMany.