Model

Model is a more advanced struct.With Relationships,Events,Connection,TimeStamps features.

Conventions

First,let’s see how to define a model.

  1. A model should be a struct with an embed anonymous struct pointer of type goeloquent.EloquentModel
  2. primaryKey should be int64 with tag goelo:"primaryKey"
  3. Each database table column should have a tag goelo:"column:databasefiledname"
  4. By default , we will use the model’s SnakeCase name as table name (e.g. ‘User’ as ‘user’,’UserInfo’ as ‘user_info’’)
    ,you can change this by implement type TableName interface
  5. By default , we will use the connection with name default.You can change this by
    implement type ConnectionName interface.
  6. When creating model,we will set timestamps for you if you have a tag goelo:"column:created_at;CREATED_AT"
  7. When updating model,we will set timestamps for you if you have a tag goelo:"column:updated_at;UPDATED_AT"

Example of User model

type UserT struct {
*goeloquent.EloquentModel
Id int64 `goelo:"column:id;primaryKey"`
UserName string `goelo:"column:name"`
Age int `goelo:"column:age"`
ViewedPosts []Post `goelo:"BelongsToMany:ViewedPostsRelation"`
Friends []UserT `goelo:"BelongsToMany:FriendsRelation"`
Address Address `goelo:"HasOne:AddressRelation"`
Posts []Post `goelo:"HasMany:PostsRelation"`

CreatedAt time.Time `goelo:"column:created_at;CREATED_AT"`
UpdatedAt sql.NullTime `goelo:"column:updated_at;UPDATED_AT"`
}
func (u *UserT) TableName() string {
return "user"
}
func (u *UserT) ConnectionName() string {
return "chat"
}

## Retrive Models

var us []UserT
DB.Model(&UserT{}).Where(“age”, “>”, 0).Get(&us)
//select * from user where age > ? [0] {23} 61.963146ms
fmt.Println(us)

var u UserT
DB.Model(&UserT{}).Find(&u,22)
//{select * from users where age > ? [0] {23} 76.868039ms}


## Create Model

var nu UserT
goeloquent.NewModel(&nu)
nu.Age = 23
nu.UserName = “hello”
nu.Phone = sql.NullString{
String: “+19785920200”,
Valid: true,
}
DB.Save(&nu)
//{insert into users ( user_name,phone,age,created_at ) values ( ? , ? , ? , ? ) [hello {+19785920200 true} 23 {2021-11-05 16:21:47.188486 +0800 CST m=+0.570200737 true}] 65.652297ms}


## Update Model

var u UserT
DB.Model(&UserT{}).Find(&u, 22)
u.Age = 23
u.Save()
//{update users set user_name = ? , nick_name = ? , phone = ? , age = ? , created_at = ? , updated_at = ?
//where id = ? [s {s true} {1965320233 true} 23 {2021-04-07 15:33:21 +0000 UTC true} {2021-11-05 16:13:19.467342 +0800 CST m=+0.424807671 true} 22] {0xc000162360 0xc00012d110} 64.493242ms}
DB.Save(&u)


## Mass Update

var u UserT
DB.Model(&u).Where(“age”, “>”, 200).Update(map[string]interface{}{
“age”: goeloquent.Expression{Value: “age + 10”},
“phone”: “unknown”,
})
//{update users set age = age + 10 , phone = ? where age > ? [unknown 200] {0xc000140090 0xc00001eea0} 72.325303ms}


You can use `Only/Except` to specify which columns to be inserted/updated

```golang
type Temp struct {
Id int64 `goelo:"column:tid;primaryKey"`
Name string `goelo:"column:name"`
Status int8 `goelo:"column:status"`
}
type TempB struct {
Id int64
Name string
}

var tagt, tag Temp
var tagtt TempB
tag.Name = "test"
tagt.Name = "test"
tagt.Status = 1
tagtt.Name = "test"

DB.Table("tag").Insert(tag)
//"insert into `tag` (`name`) values (?)"

DB.Table("tag").Only("name").Insert(tagt)
//"insert into `tag` (`name`) values (?)"

DB.Model().Except("id").Insert(&tagtt)
//"insert into `tag` (`name`) values (?)"

Scopes

FuncScope

func AgeScope(builder *goeloquent.Builder) *goeloquent.Builder {
return builder.Where("age", ">", 18)
}
b1 := DB.Query()
b1.Select().From("users").Scopes(AgeScope, ScopeFunc).Get(&us)
//{select * from `users` where `age` > ? order by `id` desc [18] <nil> 40.616291ms}

Model Global Scope

Add global scope to model

type Tag1 struct {
*goeloquent.EloquentModel
ID int64 `goelo:"column:tid;primaryKey"`
Name string `goelo:"column:name"`
}

func (t *Tag1) AddGlobalScopes() map[string]goeloquent.ScopeFunc {
return map[string]goeloquent.ScopeFunc{
"active": func(builder *goeloquent.Builder) *goeloquent.Builder {
return builder.Where("active", 1)
},
}
}

var tags []Tag1
b := DB.Model(&Tag1{})
_, err := b.Pretend().Get(&tags)
//"select * from `tag1` where `active` = ?"

Remove global scope

var tags []Tag1
b := DB.Model(&Tag1{})
_, err := b.Pretend().WithOutGlobalScopes("active").Get(&tags)
//"select * from `tag1`"

Events

Like Laravel,we have model events in model’s lifecycle.
We have saving,saved,creating,created,updating,updated,deleting,deleted model events.
Below is the event order.

For create saving->creating->insert operation->created->saved
For update saving->updating->update operation ->updated->saved
For delete deleting->delte operation->deleted
In any ing event return an error will abort the database operation

func (u *UserT) Creating(builder *goeloquent.Builder) (err error) {
if u.Age <= 0 {
u.Age = 10 //change it
}
fmt.Println(u.Exists)//false
u.GetOrigin()//map with default values
u.GetDirty() //map with non-zero values
u.GetChanges() //it's not saved to dasebase so empty
if strings.Contains(u.NickName.String, "shit") {
return errors.New("bad name") //return error to abort create
}
return nil //if it return error, abort sql execution with err "abort by func"
}
func (u *UserT) Created(builder *goeloquent.Builder)(err error) {
u.GetOrigin() //same with creating
u.GetDirty() //same with creating and an `id` value
u.GetChanges() //same with creating
return nil
}
func (u *UserT) Updating(builder *goeloquent.Builder) (err error) {
u.GetOrigin()//original databse value
u.GetDirty()//map with changed values
u.GetChanges() // empty map
return nil
}
func (u *UserT) Updated(builder *goeloquent.Builder)(err error) {
u.GetOrigin()//same with updating
u.GetDirty()//same with updating
u.GetChanges()//same with GetDirty
return nil

}
func (u *UserT) Saving(builder *goeloquent.Builder) (err error) {
u.GetOrigin()//when create same with creating,when update same with updating
u.GetDirty()//when create same with creating,when update same with updating
u.GetChanges()//when create same with creating,when update same with updating
return nil
}
func (u *UserT) Saved(builder *goeloquent.Builder)(err error) {
u.GetOrigin() //when create same with created
u.GetDirty()//when create same with created
u.GetChanges()//when create same with created
return nil

}
func (u *UserT) Deleting(builder *goeloquent.Builder)(err error) {
u.GetOrigin()
u.GetDirty()
u.GetChanges()
return nil

}
func (u *UserT) Deleted(builder *goeloquent.Builder)(err error) {
u.GetOrigin()
u.GetDirty()
u.GetChanges()
return nil

}

GetOrigin/GetDirty/GetChange

You can call GetOrigin to get the original attributes since you retrive it from database.
If it’s just created and haven’t been save to database it will be a map[string]interface with all default values.
You can get a map[string]interface with all changed value than hasn’t been save to databse yet by call GetDirty.

Mute Events

If you don’t want trigger event use Mute()

var u UserT
DB.Model(&UserT{}).Find(&u, 22)
u.Age = 23
u.Mute(goeloquent.EventUpdating,goeloquent.EventUpdated,goeloquent.EventSaving,goeloquent.EventSaved).Save()

If you are creating a model and wants to mute some event call DB.Boot(&u) before you use Mute