Golang Iris: Initialize parent controller's model to child controller's model - go

I understand that Go is not an object-oriented language but I'm trying to implement a inheritance structure in my Iris controllers as suggested by this article. My main motivation for doing so is to avoid repetition. So far, it has been working for me. Take a look at the following code for example.
// APIController.go (package Controllers)
type APIController struct {
mvc.C
}
func (c *APIController) Post(data map[string][]string) ([]byte, error) {
data_parsed := c.ParseFormData(data)
return json.Marshal(data_parsed)
}
// UserController.go (package Controllers)
type UserController struct {
mvc.C
*APIController
}
func (c *UserController) Post() ([]byte, error) {
return c.APIController.Post(c.Ctx.FormValues())
}
So far so good.
But I'm finding it difficult to replicate the same strategy for Models. This is what I've done so far
// Model.go (package Models)
type Model struct {
Id string `json:"_id"`
Created_at string `json:"created_at"`
Updated_at string `json:"updated_at"`
Deleted_at string `json:"deleted_at"`
}
// implements further set of functions to be used by 'child' models...
// User.go (package Models)
type User struct {
*Model
First_name string `json:"first_name"`
Last_name string `json:"last_name"`
Email string `json:"email"`
Username string `json:"username"`
Password string `json:"password"`
Last_login string `json:"last_login"`
}
// APIController.go (package Controllers)
type APIController struct {
mvc.C
Model Models.Model
}
// UserController.go (package Controllers)
type UserController struct {
mvc.C
*APIController
}
func (c *UserController) Post() ([]byte, error) {
c.APIController.Model = new(Models.User) //WRONG!
return c.APIController.Post(c.Ctx.FormValues())
}
As you can see, the APIController is expecting type Models.Model while UserController is passing *Models.User. The end goal is to have a generic model in APIController that any model from any controller and then is able to call all the functions defined in Models.Model so that I don't have to call those function everywhere.
Is it possible to do so? If not, what might be the best approach to avoid repeating the code?
Update
By using inheritance and single parent model and using that in parent APIController, I want to avoid replicating my filter/CRUD logic. For example, inside UserController, if I want to save a record, then instead of using User.Save(input_data) inside UserController, the Save should ideally be defined inside Models.Model and from APIController, I'm able to call Model.Save(input_data) rather than making the same call from child controllers individually.

First make Model an interface instead of a struct. Have it contain all methods that should be common to all models:
type Model interface {
// Common methods
}
Then, as long as User implements all those methods, you can have
c.APIController.Model = new(Models.User) // Now works!
One of the common methods could be Save. Otherwise make Save a non-method:
func Save(m Model) error {
...
}

Related

golang read private attribute of a struct in another package

I understand on golang we have public and private fields
package main
type User struct {
DisplayName string
title *string
}
Displayname is public so I can read it from another package. title is private I cannot read direclty
what about I add a public method like this
package main
type User struct {
DisplayName string
title *string
}
func (user *User) PublicTitle() string {
return user.title
}
type EmployeeUser User
So I should be able to read title by localUser.PublicTitle() in another package?
package utility
var localUser *main.EmployeeUser
localUser.PublicTitle()
I have tried it seems not working. I am a bit confused.
Thanks for help
The type EmployeeUser is a new type. When you define a new type based on an existing one, the methods of base type are not promoted to the new type.
To do that, you have to embed:
type EmployeeUser struct {
User
}

How do I store the data from DTO Gorm

I am using golang fiber and GORM. I am trying to store the data from DTO. For example, I have created a post entity and post dto like below.
type User struct {
gorm.Model
Name string
Address string
Phone string
}
This is my DTO
type PostDto struct {
Name string `json:"name"`
Address string `json:"address"`
Phone string `json:"phone"`
}
I am trying to store the data like below
var postDto models.PostDto
c.BodyParser(&postDto)
err := database.DB.Model(models.User{}).Create(&postDto).Error
fmt.Println(err.Error())
But I am getting the below error
panic: reflect: call of reflect.Value.Field on string Value
Could anyone help to resolve this issue?
Thanks in advance
I don't know what you are doing? this is go language, not java, why do you define two structs?
You can use tag to complete that you want.
type User struct {
gorm.Model `json:"-" gorm:"-"`
Name string `json:"name" gorm:"column:name"`
Address string `json:"address" gorm:"column:address"`
Phone string `json:"phone" gorm:"column:phone"`
}
var user User
err := database.DB.Model(User{}).Create(&user).Error
fmt.Println(err.Error())
User and PostDto structures are not same, using one model to get and save data from another is wrong.
Either create a function to convert from PostDto to User, adn use its output in Create
func (postDTo PostDTo) ToUser() *User {
return &User{Name: postDTo.Name, Address: postDTo.Address, Phone: postDTo.Phone}
}
OR, as User and PostDto structs are so similar, add json ignore to gorm.Model in User and use it instead of PostDto
type User struct {
gorm.Model `json:"-"`
Name string `json:"name"`
Address string `json:"address"`
Phone string `json:"phone"`
}
var postData models.User
c.BodyParser(&postData)

GORM embedded struct not working correctly

I receive this error
controllers/users.go:61:36: user.ID undefined (type models.User has no field or method ID)
when using
var user models.User
...
jwtToken, err := generateJWT(user.ID, user.Username)
The definition of User model:
package models
import "time"
type BaseModel struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
}
type User struct {
BaseModel BaseModel `gorm:"embedded"`
Username string `json:"username"`
Password string `json:"password"`
}
Actually I put BaseModel in different file but in the same package with User. Migration works fine as table users have all columns in BaseModel. What is the problem? I use golang 1.18 and latest version of GORM
You have used BaseModel as attribute in your model so even though gorm can very well map it to the table column to access it, you have to use the attribute name
To access you would do
jwtToken, err := generateJWT(user.BaseModel.ID, user.Username)
you could also try this next code to see if it works otherwise the above will work for sure
type BaseModel struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
}
type User struct {
BaseModel `gorm:"embedded"`
Username string `json:"username"`
Password string `json:"password"`
}
now you might be able to access it like your original pattern
jwtToken, err := generateJWT(user.ID, user.Username)

How add new fields to the existing type in another package in Golang

I have a custom AUTH package and there i have USER STRUCT.
So i want to import the auth package in my go project and override the struct or add new fields to the struct without modifying the package code.
package auth
type User struct {
gorm.Model
UserEmail string
UserPass string
}
Now i have main.go
package main
import "auth"
// WANT TO OVERRIDE OR EXTEND THE USER STRUCT
auth.User = {
UserAge string
}
function main() {
}
So i want to import the auth package in my go project and override the struct or add new fields to the struct without modifying the package code.
You simply cannot do this in Go. You have to redesign.
you can define a new type like this:
type NewUser struct {
auth.User
UserAge string
}

InsertOnSubmit with interfaces (LINQ to SQL)

In our code we have:
public interface ILogMagazine
{
string Text { get; set; }
DateTime DateAndTime { get; set; }
string DetailMessage { get; set; }
}
SimpleDataContext: DataContext
{
public Table<ILogMagazine> LogMagaines
{
get { return GetTable<ILogMagazine>(); }
}
}
We try to:
DataContext db = new SimpleDataContext("...");
ILogMagazine lg = new LogMagazine()
{
Text = "test",
DateAndTime = DateTime.Now,
DetailMessage = "test",
};
db.LogMagazines.InsertOnSubmit(lg); // Exception thrown
db.SubmitChanges();
Exception: System.InvalidOperationException: The type 'DataLayer.ILogMagazine' is not mapped as a Table..
How we can solve this problem?
The error is because you haven't applied the [Table] attribute (normally it'd go on a class type, in your case the interface type), but I don't see it working even if you did. That's how the mapping is done- when you call GetTable, it looks for the Table attribute to know where to insert/query the data from.
That said, I'm pretty sure you can't do this with an interface. The type on GetTable has to be concrete, because it uses the generic arg passed (or inferred) on GetTable to know what object to create for a query. While it might technically be able to work for inserts, the same GetTable is used for both inserts and queries- which it most certainly won't work for. Same reason XmlSerializer and DataContractSerializer don't work with interfaces.
You'll always need a concrete type, but your entity types can still implement interfaces. I'm guessing you're trying to shuttle these around somewhere (service layer, perhaps), and you'll probably need to rethink that a bit.

Resources