0
votes

I want to write a function that retrieves a record in the database with GORM where the destination struct is dynamic. Something like this:

type User struct {
  ID    uint
  Email string
}

type BigUser struct {
  ID       uint
  Username string
  Role     string
}

func GetRecord(db *gorm.DB, u SomeUserInterfaceTypeOrSomething) {
  db.Find(&u)
}

GetRecord(db, User{})
GetRecord(db, BigUser{})

So if I pass a User struct variable I get only the ID and the email, if I pass a BigUser struct variable I get all the database fields (for example).

I need this because the GetRecord will be in a library and I want that who will be using the library can pass a custom struct to retrieve only the fields he wants.

I don't know how to achieve this, any advice, or best practice? I'm new with Golang :D

2

2 Answers

2
votes

Passing a pointer to an interface is almost always, if not always, wrong. I.e. the argument passed to your function SHOULD already be a pointer. With that in mind you can use the same approach that json.Unamrshal, or (*gorm.DB).Find for that matter, uses, i.e. accept an empty interface and decode the result into that. And use documentation to inform the user that the passed in value must be a non-nil pointer otherwise the code will fail.

func GetRecord(db *gorm.DB, u interface{}) {
    db.Find(u) // u must already be a pointer, therefore don't do &u here
}

var u User{}
GetRecord(db, &u) // pass a pointer here

bu := new(BigUser) // new returns a pointer
GetRecord(db, bu) // bu is a pointer already, no need for &bu here
1
votes

To pass a structure as UserInterface, you need to make sure that your desire structure implements all interface methods.

Sample code

package main

import "fmt"

type User struct {
    ID    uint
    Email string
}

func (user *User) GetRecord() interface{} {
    // get info from database here
    return &User{
        ID:    user.ID,
        Email: user.Email,
    }
}

type BigUser struct {
    ID       uint
    Username string
    Role     string
}

func (user *BigUser) GetRecord() interface{} {
    // get info from database here
    return &BigUser{
        ID:       user.ID,
        Username: user.Username,
        Role:     user.Role,
    }
}

// While declaring an interface,
// make sure that your desire structures have all those interface methods.
type UserInterface interface {
    GetRecord() interface{}
}

// Function to print the values
func printValues(u UserInterface) {
    fmt.Println(u.GetRecord())
}

func main() {
    var x UserInterface

    x = &User{
        ID:    1,
        Email: "[email protected]",
    }
    printValues(x)

    x = &BigUser{
        ID:       2,
        Username: "YouKnowWhoIam",
        Role:     "ADMIN",
    }
    printValues(x)
}

Output

&{1 [email protected]}
&{2 YouKnowWhoIam ADMIN}