Your Question

I am in need of some formatting before doing a query. Mainly converting shortuuid to uuid. What I have in mind is having 2 fields in my custom gorm.Model. e.g. ID which is a string (shortuuid, ignored in gorm) and UUID which is the primary key.

When doing a query, I am assigning the ID field (which again, is a shortuuid), before the query actually runs I decode this shortuuid and assign the uuid to it's UUID field, thus querying with this instead.

I looked into https://github.com/go-gorm/gorm/issues/1159, mainly the Before("gorm:query) part. Now, it does not seem like v2 exposes CallMethod anymore, so I was looking at the docs and found the part of writing plugins, but I am honestly at a loss.

Feels like I am doing something wrong, do you have any idea of how to do this correctly?

I'm looking into this as to not expose a squence in my database, so uuid looked good. But it's way too long to show in the api and my website, so found shortuuid which implements base57 (based my logic around this, with the possibility of decoding the string to uuid).

So the flow would be: - Query with shortuuid -> Convert to uuid - Return results with uuid -> Convert to shortuuid.

Any ideas?

Comment From: jinzhu

refer https://github.com/go-gorm/gorm/blob/master/callbacks/create.go#L13-L28

Comment From: vincegio

Ah right, thank you!

Comment From: vincegio

I have implemented some copies of the code in gorm.

package callbacks

import (
    "gorm.io/gorm"
        "reflect"
)

type BeforeQueryInterface interface {
    BeforeQuery(db *gorm.DB) error
}

func BeforeQuery(db *gorm.DB) {
    if db.Error == nil {
        callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {
            if i, ok := value.(BeforeQueryInterface); ok {
                called = true
                db.AddError(i.BeforeQuery(tx))
            }
            return called
        })
    }
}

func callMethod(db *gorm.DB, fc func(value interface{}, tx *gorm.DB) bool) {
    tx := db.Session(&gorm.Session{})
    if called := fc(db.Statement.Dest, tx); !called {
        switch db.Statement.ReflectValue.Kind() {
        case reflect.Slice, reflect.Array:
            db.Statement.CurDestIndex = 0
            for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
                fc(reflect.Indirect(db.Statement.ReflectValue.Index(i)).Addr().Interface(), tx)
                db.Statement.CurDestIndex++
            }
        case reflect.Struct:
            fc(db.Statement.ReflectValue.Addr().Interface(), tx)
        }
    }
}

Where I initialize my db I do:

db.Callback().Query().Before("gorm:query").Register("ltc:before_query", callbacks.BeforeQuery)

In the model struct, I have implemented:

func (m *SampleModel) BeforeQuery(db *gorm.DB) error {
    fmt.Println(m)
    return nil
}

This is called successfully prior to the query itself, like expected. However, the struct is empty, nothing has been initialized.

Here's an example of how I query:

var res SampleModel
err := db.Where(SampleModel{...}).First(&res).Error

Having a print in BeforeCreate shows the data I am trying to add to the db. Am I missing something?

Comment From: KiddoV

@vincegio I know I am late for the party but... the struct should be empty in your case for sure: https://github.com/go-gorm/gorm/issues/3935#issuecomment-758476305

I think this custom hook you made is useful to do things like update the table before SELECT is called.

Comment From: mirusky

Nice work @vincegio

@jinzhu why don't support it natively in gorm?