Spouk
Spouk

Reputation: 712

Golang. Refactoring || Pattern || any solution

Problem: There is a list of tables associated keys. It is necessary to implement CRUD for each table + some functional on the specifics of the table (pagination, etc.). Troubles: duplicated in each of the functional implementation for each table About 50% of the code. Question: What are the ways on the specifics of the language, of the optimization / refactoring / use similar code patterns?

Example code part

//------------------------------------------------------------------
//  user
//----------------------------------------------------------------------
func (d *Database) UserCreate(email, password, username string, role int) *Answer {
    //variables
    answer := d.initCall("UserCreate")
    user := new(User)
    //check exist user
    if userExist := d.DB.Find(&User{}, &User{Email:email}).Error; userExist == nil {
        answer.AnswerJSON.ErrorAnswer.Message = "[usercreate] user already exist"
        answer.AnswerJSON.Result = nil
        answer.AnswerJSON.Status = false
    } else {

        userinfo := UserInfo{Data:time.Now()}
        session := Session{Data:time.Now()}
        cook := Cook{}

        //make support  tables object
        //userinfo object
        userinfo.Password = password
        userinfo.Username = username
        //cook object
        cook.SessionID = session.ID
        cook.Cookie = d.cookgeneratenew("somesaltforcooksgenerate")
        cook.Status = true
        cook.Lastconnect = time.Now()
        cook.CountConnect += 1

        //user object
        user.Email = email
        user.Session = session
        user.UserInfo = userinfo
        user.Cook = cook
        user.Role = int64(role)

        //make request + //serizlizations to json answer
        if err := d.DB.Create(user).Error; err != nil {
            answer.AnswerJSON.ErrorAnswer.Message = err.Error()
            answer.AnswerJSON.Status = false
        } else {
            answer.AnswerJSON.Result = user
            answer.AnswerJSON.Status = true
        }
    }
    d.serilize(answer)
    return answer
}
func (d *Database) UserDelete(userid int64) *Answer {
    answer := d.initCall("UserDelete")
    user := new(User)
    //check exists
    if userExists := d.DB.Find(user, "ID = ?", userid).Error; userExists == nil {
        if userDelete := d.DB.Delete(user).Error; userDelete != nil {
            answer.AnswerJSON.ErrorAnswer.Message = userExists.Error()
            answer.AnswerJSON.Status = false
        } else {
            answer.AnswerJSON.Status = true
            answer.AnswerJSON.ErrorAnswer = nil
        }
    } else {
        answer.AnswerJSON.ErrorAnswer.Message = userExists.Error()
        answer.AnswerJSON.Status = false
    }
    d.serilize(answer)
    return answer
}
func (d *Database) UserRead(userid int64) *Answer {
    answer := d.initCall("UserRead")
    user := new(User)
    if userExists := d.DB.Find(user, "ID = ?", userid).Error; userExists != nil {
        answer.AnswerJSON.ErrorAnswer.Message = userExists.Error()
        answer.AnswerJSON.Status = false
    } else {
        if rel := d.DB.Model(user).Related(&user.Session).Error; rel != nil {
            answer.AnswerJSON.Result = nil
            answer.AnswerJSON.ErrorAnswer.Message = rel.Error()
            answer.AnswerJSON.Status = false
        } else if rel = d.DB.Model(user).Related(&user.UserInfo).Error; rel != nil {
            answer.AnswerJSON.Result = nil
            answer.AnswerJSON.ErrorAnswer.Message = rel.Error()
            answer.AnswerJSON.Status = false
        } else if rel = d.DB.Model(user).Related(&user.Cook).Error; rel != nil {
            answer.AnswerJSON.Result = nil
            answer.AnswerJSON.ErrorAnswer.Message = rel.Error()
            answer.AnswerJSON.Status = false
        } else {
            answer.AnswerJSON.Result = user
            answer.AnswerJSON.ErrorAnswer = nil
            answer.AnswerJSON.Status = true
        }
    }
    d.serilize(answer)
    return answer
}
func (d *Database) UserReadAll() *Answer {
    answer := d.initCall("UserReadAll")
    users := []User{}

    if usersGet := d.DB.Find(&users).Error; usersGet != nil {
        answer.AnswerJSON.ErrorAnswer.Message = usersGet.Error()
        answer.AnswerJSON.Status = false
    } else {
        for _, user := range users {
            d.DB.Model(user).Related(&user.Session)
            d.DB.Model(user).Related(&user.UserInfo)
            d.DB.Model(user).Related(&user.Cook)
        }
        answer.AnswerJSON.Result = users
        answer.AnswerJSON.ErrorAnswer = nil
        answer.AnswerJSON.Status = true
    }
    d.serilize(answer)
    return answer
}
func (d *Database) UserUpdate(userid int64, uu *UserUpdateStruct) *Answer {
    answer := d.initCall("UserUpdate")
    user := new(User)

    if userExists := d.DB.Find(user, "ID = ?", userid).Error; userExists != nil {
        answer.AnswerJSON.ErrorAnswer.Message = userExists.Error()
        answer.AnswerJSON.Status = false
    } else {
        //get relevating struct info
        d.DB.Model(user).Related(&user.Session)
        d.DB.Model(user).Related(&user.UserInfo)
        d.DB.Model(user).Related(&user.Cook)

        //update columns
        if uu.Email != "" {
            user.Email = uu.Email
        }
        if uu.Avatar > 0 {
            user.UserInfo.Avatar = uu.Avatar
        }
        if uu.Birthday != "" {
            user.UserInfo.Birthday = uu.Birthday
        }
        if uu.Location != "" {
            user.UserInfo.Location = uu.Location
        }
        if uu.Male != "" {
            user.UserInfo.Male = uu.Male
        }
        if uu.Password != "" {
            user.UserInfo.Password = uu.Password
        }
        if uu.Role > 0 {
            if roleErr := d.DB.Find(&Role{}).Error; roleErr != nil {
                //role not found
                fmt.Printf("[userupdate] role not found `%d`\n", uu.Role)
            } else {
                user.Role = uu.Role
            }
        }

        //update
        if errUpdate := d.DB.Save(user).Error; errUpdate != nil {
            answer.AnswerJSON.ErrorAnswer.Message = errUpdate.Error()
            answer.AnswerJSON.Status = false
        } else {
            answer.AnswerJSON.Result = user
            answer.AnswerJSON.ErrorAnswer = nil
            answer.AnswerJSON.Status = true
        }
    }
    d.serilize(answer)
    return answer
}
func (d *Database) UserPaginate(page, countPage int) *Answer {
    answer := d.initCall("UserPaginate")
    result := []*User{}
    if errPag := d.DB.Limit(countPage).Offset((page * countPage) - countPage).Find(&result).Error; errPag != nil {
        answer.AnswerJSON.ErrorAnswer.Message = errPag.Error()
        answer.AnswerJSON.Status = false
    } else {
        for _, user := range result {
            d.DB.Model(&user).Related(&user.Session)
            d.DB.Model(&user).Related(&user.UserInfo)
            d.DB.Model(&user).Related(&user.Cook)
        }
        answer.AnswerJSON.Result = result
        answer.AnswerJSON.ErrorAnswer = nil
        answer.AnswerJSON.Status = true
    }
    d.serilize(answer)
    return answer
}
//-------------------------------------------------------------------
//  category
//--------------------------------------------------------------------
func (d *Database) CategoryCreate(name string) *Answer {
    //variables
    answer := d.initCall("CategoryCreate")
    cat := new(Category)
    //check exist user
    if catExist := d.DB.Find(&Category{}, &Category{Name:name}).Error; catExist == nil {
        answer.AnswerJSON.ErrorAnswer.Message = "[categorycreate] category already exist"
        answer.AnswerJSON.Result = nil
        answer.AnswerJSON.Status = false
    } else {
        cat.Name = name
        //make request + //serizlizations to json answer
        if err := d.DB.Create(cat).Error; err != nil {
            answer.AnswerJSON.ErrorAnswer.Message = err.Error()
            answer.AnswerJSON.Status = false
        } else {
            answer.AnswerJSON.Result = cat
            answer.AnswerJSON.Status = true
        }
    }
    d.serilize(answer)
    return answer
}
func (d *Database) CategoryDelete(catid int64) *Answer {
    answer := d.initCall("CategoryDelete")
    cat := new(Category)
    //check exists
    if catExists := d.DB.Find(cat, "ID = ?", catid).Error; catExists == nil {
        if catDelete := d.DB.Delete(cat).Error; catDelete != nil {
            answer.AnswerJSON.ErrorAnswer.Message = catExists.Error()
            answer.AnswerJSON.Status = false
        } else {
            answer.AnswerJSON.Status = true
            answer.AnswerJSON.ErrorAnswer = nil
        }
    } else {
        answer.AnswerJSON.ErrorAnswer.Message = catExists.Error()
        answer.AnswerJSON.Status = false
    }
    d.serilize(answer)
    return answer
}
func (d *Database) CategoryRead(catid int64) *Answer {
    answer := d.initCall("CategoryRead")
    cat := new(Category)
    if catExists := d.DB.Find(cat, "ID = ?", catid).Error; catExists != nil {
        answer.AnswerJSON.ErrorAnswer.Message = catExists.Error()
        answer.AnswerJSON.Status = false
    } else {
        if rel := d.DB.Model(cat).Related(&cat.Post).Error; rel != nil {
            answer.AnswerJSON.Result = nil
            answer.AnswerJSON.ErrorAnswer.Message = rel.Error()
            answer.AnswerJSON.Status = false
        } else {
            for x, p := range cat.Post {
                d.DB.Model(&p).Related(&p.Comment)
                d.DB.Model(&p).Related(&p.PostSeo)
                cat.Post[x] = p
            }
            answer.AnswerJSON.Result = cat
            answer.AnswerJSON.ErrorAnswer = nil
            answer.AnswerJSON.Status = true
        }
    }
    d.serilize(answer)
    return answer
}
func (d *Database) CategoryReadAll() *Answer {
    answer := d.initCall("CategoryReadAll")
    cats := []Category{}

    if catsGet := d.DB.Find(&cats).Error; catsGet != nil {
        answer.AnswerJSON.ErrorAnswer.Message = catsGet.Error()
        answer.AnswerJSON.Status = false
    } else {
        //categorys
        for _, cat := range cats {
            d.DB.Model(cat).Related(&cat.Post)
            //get relete POST
            for x, p := range cat.Post {
                d.DB.Model(&p).Related(&p.Comment)
                d.DB.Model(&p).Related(&p.PostSeo)
                cat.Post[x] = p
            }
        }
        answer.AnswerJSON.Result = cats
        answer.AnswerJSON.ErrorAnswer = nil
        answer.AnswerJSON.Status = true
    }
    d.serilize(answer)
    return answer
}
func (d *Database) CategoryUpdate(catid int64, name string) *Answer {
    answer := d.initCall("CategoryUpdate")
    cat := new(Category)

    if catExists := d.DB.Find(cat, "ID = ?", catid).Error; catExists != nil {
        answer.AnswerJSON.ErrorAnswer.Message = catExists.Error()
        answer.AnswerJSON.Status = false
    } else {
        //get relevating struct info
        d.DB.Model(cat).Related(&cat.Post)

        //update columns
        if name != "" {
            cat.Name = name
        }
        //update
        if errUpdate := d.DB.Save(cat).Error; errUpdate != nil {
            answer.AnswerJSON.ErrorAnswer.Message = errUpdate.Error()
            answer.AnswerJSON.Status = false
        } else {
            answer.AnswerJSON.Result = cat
            answer.AnswerJSON.ErrorAnswer = nil
            answer.AnswerJSON.Status = true
        }
    }
    d.serilize(answer)
    return answer
}
func (d *Database) CategoryPaginate(page, countPage int) *Answer {
    answer := d.initCall("CategoryPaginate")
    result := []*Category{}
    if errPag := d.DB.Limit(countPage).Offset((page * countPage) - countPage).Find(&result).Error; errPag != nil {
        answer.AnswerJSON.ErrorAnswer.Message = errPag.Error()
        answer.AnswerJSON.Status = false
    } else {
        for _, cat := range result {
            d.DB.Model(&cat).Related(&cat.Post)
            //get relete POST
            for x, p := range cat.Post {
                d.DB.Model(&p).Related(&p.Comment)
                d.DB.Model(&p).Related(&p.PostSeo)
                cat.Post[x] = p
            }
        }
        answer.AnswerJSON.Result = result
        answer.AnswerJSON.ErrorAnswer = nil
        answer.AnswerJSON.Status = true
    }
    d.serilize(answer)
    return answer
}

Upvotes: 2

Views: 412

Answers (2)

Spouk
Spouk

Reputation: 712

I implemented this idea in the code. It turned out great.

Refactoring code

//------------------------------------------------------------------------
//  REFACTORING
//-----------------------------------------------------------------------
func (d *Database)Create(model string, params interface{}) *Answer {
    //variables
    answer := d.initCall("Create")
    tableType := d.Mapper[model]
    var (
        errorExists interface{}
        flag bool
    )

    //logic switch types
    switch tableType.(type) {
    case User:
        params := params.(UserUpdate)
        if errorExists = d.DB.Find(&User{}, &User{Email:params.Email}).Error; errorExists == nil {
            flag = false
        } else {
            flag = true
            //variables
            user := new(User)
            userinfo := UserInfo{Data:time.Now()}
            session := Session{Data:time.Now()}
            cook := Cook{}

            //make support  tables object
            //userinfo object
            userinfo.Password = params.Password
            userinfo.Username = params.Username
            //cook object
            cook.SessionID = session.ID
            cook.Cookie = d.cookgeneratenew("somesaltforcooksgenerate")
            cook.Status = true
            cook.Lastconnect = time.Now()
            cook.CountConnect += 1

            //user object
            user.Email = params.Email
            user.Session = session
            user.UserInfo = userinfo
            user.Cook = cook
            user.Role = int64(params.Role)

            //assign ready model for create
            tableType = user
        }

    case Category:
        params := params.(CategoryUpdate)
        if errorExists = d.DB.Find(&Category{}, &Category{Name:params.Name}).Error; errorExists == nil {
            //set logic switcher
            flag = false
        } else {
            //set logic switcher
            flag = true

            //speciffic functional
            cat := new(Category)
            cat.Name = params.Name

            //assign ready model for create
            tableType = cat
    }
    default:
        fmt.Printf("Wrong type table\n")
    }

    //call function create model
    if flag {
        if err := d.DB.Create(tableType).Error; err != nil {
            answer.AnswerJSON.ErrorAnswer.Message = err.Error()
            answer.AnswerJSON.Status = false
        } else {
            answer.AnswerJSON.Result = tableType
            answer.AnswerJSON.Status = true
        }
    } else {
        answer.AnswerJSON.ErrorAnswer.Message = "object already exist"
        answer.AnswerJSON.Result = nil
        answer.AnswerJSON.Status = false
    }
    //make answer
    d.serilize(answer)
    return answer
}

Upvotes: 0

pregmatch
pregmatch

Reputation: 2647

I can see you have a lot of redundancy in your code. Few months back I had a same problem and have great answer. Take a look at here: How to return dynamic type struct in Golang?.

Basically you will have same three functions(add/edit/delete/count) like this (i do not know what db are you using I use NoSql Rethinkdb, but the principle is same):

func (c Main) Get(modelname string) interface{} {

   //your query here for getting stuff from model name and return interface

   rows, err := rethink.Table(modelname).OrderBy(rethink.Asc("sortNo")).Run(c.DB)
   if err != nil {
       log.Println(err)
       return nil
   }

   if (modelType == "brand") {

       var brands  []*models.Brand
       rows.All(&brands)

       return brands

   }
   //rest of your models here or use some switch
})

I usually put all my functions that are doing stuff with database in some helper (this is mostly because of tests).

I think this is good directions because like this you code is easy to maintain and you will be changing only helper functions instead of going to million fictions that are doing add/delete/update for every model.

Upvotes: 2

Related Questions