service

package
v0.0.5 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2023 License: MIT Imports: 7 Imported by: 1

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

Constants

This section is empty.

Variables

View Source
var (
	ErrNoIdentityField = errors.New("no identity field found")
	ErrNilID           = errors.New("id is nil")
)
View Source
var (
	ErrNoRecord        = errors.New("no record found")
	ErrMultipleRecords = errors.New("multiple records found")
)

Functions

func Count

func Count[T any](ctx context.Context, options ...QueryOption) (count int64, err error)

Count returns the number of models.

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 Delete

func Delete(ctx context.Context, model any) (rowsAffected int64, err error)

Delete a model from database.

func DeleteByID

func DeleteByID[T orm.Model](ctx context.Context, id any) (rowsAffected int64, err error)

DeleteByID deletes a model from database by its ID.

func DeleteNested

func DeleteNested[P orm.Model, T any](ctx context.Context, parent *P, field string, child *T) error

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

func Get[T any](ctx context.Context, dest any, options ...QueryOption) error

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

func GetByID[T orm.Model](ctx context.Context, id any, dest any, options ...QueryOption) error

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

func GetMany[T any](ctx context.Context, dest any, options ...QueryOption) error

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

func Update

func Update(ctx context.Context, model any) (rowsAffected int64, err error)

Update all fields of an existing model in database.

func UpdateField

func UpdateField[T orm.Model](ctx context.Context, id any, field string, value interface{}) (rowsAffected int64, err error)

UpdateField updates a single fields of an existing model in database. It will try to GetByID first, to make sure the model exists, before updating.

Types

type CreateMode

type CreateMode func(ctx context.Context, modelToCreate any) error

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 IfNotExist

func IfNotExist() CreateMode

IfNotExist creates a model if it does not exist.

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

type QueryOption func(tx *gorm.DB) *gorm.DB

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.

Jump to

Keyboard shortcuts

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