Add some documents

This commit is contained in:
Jinzhu 2014-01-29 19:14:37 +08:00
parent 5eed96457b
commit 91e937bac1
2 changed files with 98 additions and 5 deletions

65
doc/development.md Normal file
View File

@ -0,0 +1,65 @@
# Gorm Development
## Architecture
The most notable component of Gorm is `gorm.DB`, which hold database connection. It could be initialized like this:
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
Gorm has chainable API, `gorm.DB` is the bridge of chains, it save related information and pass it to the next chain.
Lets use below code to explain how it works:
db.Where("name = ?", "jinzhu").Find(&users)
// equivalent code
newdb := db.Where("name =?", "jinzhu")
newdb.Find(&user)
`newdb` is `db`'s clone, in addition, it contains search conditions from the `Where` method.
`Find` is a query method, it creates a `Scope` instance, and pass it as argument to query callbacks.
There are four kinds of callbacks corresponds to sql's CURD: create callbacks, update callbacks, query callbacks, delete callbacks.
## Callbacks
### Register a new callback
func updateCreated(scope *Scope) {
if scope.HasColumn("Created") {
scope.SetColumn("Created", time.Now())
}
}
db.Callback().Create().Register("update_created_at", updateCreated)
// register a callback for Create process
### Delete an existing callback
db.Callback().Create().Remove("gorm:create")
// delete callback `gorm:create` from Create callbacks
### Replace an existing callback
db.Callback().Create().Replace("gorm:create", newCreateFunction)
// replace callback `gorm:create` with new function `newCreateFunction` for Create process
### Register callback orders
db.Callback().Create().Before("gorm:create").Register("update_created_at", updateCreated)
db.Callback().Create().After("gorm:create").Register("update_created_at", updateCreated)
db.Callback().Query().After("gorm:query").Register("my_plugin:after_query", afterQuery)
db.Callback().Delete().After("gorm:delete").Register("my_plugin:after_delete", afterDelete)
db.Callback().Update().Before("gorm:update").Register("my_plugin:before_update", beforeUpdate)
db.Callback().Create().Before("gorm:create").After("gorm:before_create").Register("my_plugin:before_create", beforeCreate)
### Callback API
Gorm is powered by callbacks, so you could refer below links to learn how to write callbacks
[Create callbacks](https://github.com/jinzhu/gorm/blob/master/callback_create.go)
[Update callbacks](https://github.com/jinzhu/gorm/blob/master/callback_update.go)
[Query callbacks](https://github.com/jinzhu/gorm/blob/master/callback_create.go)
[Delete callbacks](https://github.com/jinzhu/gorm/blob/master/callback_delete.go)
View [https://github.com/jinzhu/gorm/blob/master/scope.go](https://github.com/jinzhu/gorm/blob/master/scope.go) for all available API

View File

@ -22,35 +22,43 @@ type Scope struct {
skipLeft bool skipLeft bool
} }
// NewScope create scope for callbacks, including DB's search information
func (db *DB) NewScope(value interface{}) *Scope { func (db *DB) NewScope(value interface{}) *Scope {
db.Value = value db.Value = value
return &Scope{db: db, Search: db.search, Value: value, _values: map[string]interface{}{}} return &Scope{db: db, Search: db.search, Value: value, _values: map[string]interface{}{}}
} }
// New create a new Scope without search information
func (scope *Scope) New(value interface{}) *Scope { func (scope *Scope) New(value interface{}) *Scope {
return &Scope{db: scope.db.parent, Search: &search{}, Value: value} return &Scope{db: scope.db.parent, Search: &search{}, Value: value}
} }
// NewDB create a new DB without search information
func (scope *Scope) NewDB() *DB { func (scope *Scope) NewDB() *DB {
return scope.db.new() return scope.db.new()
} }
// DB get *sql.DB
func (scope *Scope) DB() sqlCommon { func (scope *Scope) DB() sqlCommon {
return scope.db.db return scope.db.db
} }
// SkipLeft skip remaining callbacks
func (scope *Scope) SkipLeft() { func (scope *Scope) SkipLeft() {
scope.skipLeft = true scope.skipLeft = true
} }
// Quote used to quote database column name according to database dialect
func (scope *Scope) Quote(str string) string { func (scope *Scope) Quote(str string) string {
return scope.Dialect().Quote(str) return scope.Dialect().Quote(str)
} }
// Dialect get dialect
func (scope *Scope) Dialect() dialect.Dialect { func (scope *Scope) Dialect() dialect.Dialect {
return scope.db.parent.dialect return scope.db.parent.dialect
} }
// Err write error
func (scope *Scope) Err(err error) error { func (scope *Scope) Err(err error) error {
if err != nil { if err != nil {
scope.db.err(err) scope.db.err(err)
@ -58,22 +66,27 @@ func (scope *Scope) Err(err error) error {
return err return err
} }
// Log print log message
func (scope *Scope) Log(v ...interface{}) { func (scope *Scope) Log(v ...interface{}) {
scope.db.log(v...) scope.db.log(v...)
} }
// HasError check if there are any error
func (scope *Scope) HasError() bool { func (scope *Scope) HasError() bool {
return scope.db.Error != nil return scope.db.Error != nil
} }
// PrimaryKey get the primary key's column name
func (scope *Scope) PrimaryKey() string { func (scope *Scope) PrimaryKey() string {
return "id" return "id"
} }
// PrimaryKeyZero check the primary key is blank or not
func (scope *Scope) PrimaryKeyZero() bool { func (scope *Scope) PrimaryKeyZero() bool {
return isBlank(reflect.ValueOf(scope.PrimaryKeyValue())) return isBlank(reflect.ValueOf(scope.PrimaryKeyValue()))
} }
// PrimaryKeyValue get the primary key's value
func (scope *Scope) PrimaryKeyValue() interface{} { func (scope *Scope) PrimaryKeyValue() interface{} {
data := reflect.Indirect(reflect.ValueOf(scope.Value)) data := reflect.Indirect(reflect.ValueOf(scope.Value))
@ -85,11 +98,13 @@ func (scope *Scope) PrimaryKeyValue() interface{} {
return 0 return 0
} }
// HasColumn to check if has column
func (scope *Scope) HasColumn(name string) bool { func (scope *Scope) HasColumn(name string) bool {
_, result := scope.FieldByName(name) _, result := scope.FieldByName(name)
return result return result
} }
// FieldByName to get column's value and existence
func (scope *Scope) FieldByName(name string) (interface{}, bool) { func (scope *Scope) FieldByName(name string) (interface{}, bool) {
data := reflect.Indirect(reflect.ValueOf(scope.Value)) data := reflect.Indirect(reflect.ValueOf(scope.Value))
@ -103,6 +118,7 @@ func (scope *Scope) FieldByName(name string) (interface{}, bool) {
return nil, false return nil, false
} }
// SetColumn to set the column's value
func (scope *Scope) SetColumn(column string, value interface{}) { func (scope *Scope) SetColumn(column string, value interface{}) {
if scope.Value == nil { if scope.Value == nil {
return return
@ -112,6 +128,7 @@ func (scope *Scope) SetColumn(column string, value interface{}) {
setFieldValue(data.FieldByName(snakeToUpperCamel(column)), value) setFieldValue(data.FieldByName(snakeToUpperCamel(column)), value)
} }
// CallMethod invoke method with necessary argument
func (scope *Scope) CallMethod(name string) { func (scope *Scope) CallMethod(name string) {
if scope.Value == nil { if scope.Value == nil {
return return
@ -147,11 +164,13 @@ func (scope *Scope) CallMethod(name string) {
} }
} }
// AddToVars add value as sql's vars, gorm will escape them
func (scope *Scope) AddToVars(value interface{}) string { func (scope *Scope) AddToVars(value interface{}) string {
scope.SqlVars = append(scope.SqlVars, value) scope.SqlVars = append(scope.SqlVars, value)
return scope.Dialect().BinVar(len(scope.SqlVars)) return scope.Dialect().BinVar(len(scope.SqlVars))
} }
// TableName get table name
func (scope *Scope) TableName() string { func (scope *Scope) TableName() string {
if scope.Search != nil && len(scope.Search.TableName) > 0 { if scope.Search != nil && len(scope.Search.TableName) > 0 {
return scope.Search.TableName return scope.Search.TableName
@ -190,11 +209,13 @@ func (scope *Scope) TableName() string {
} }
} }
// CombinedConditionSql get combined condition sql
func (scope *Scope) CombinedConditionSql() string { func (scope *Scope) CombinedConditionSql() string {
return scope.joinsSql() + scope.whereSql() + scope.groupSql() + return scope.joinsSql() + scope.whereSql() + scope.groupSql() +
scope.havingSql() + scope.orderSql() + scope.limitSql() + scope.offsetSql() scope.havingSql() + scope.orderSql() + scope.limitSql() + scope.offsetSql()
} }
// Fields get value's fields
func (scope *Scope) Fields() []*Field { func (scope *Scope) Fields() []*Field {
indirectValue := reflect.Indirect(reflect.ValueOf(scope.Value)) indirectValue := reflect.Indirect(reflect.ValueOf(scope.Value))
fields := []*Field{} fields := []*Field{}
@ -259,11 +280,13 @@ func (scope *Scope) Fields() []*Field {
return fields return fields
} }
// Raw set sql
func (scope *Scope) Raw(sql string) *Scope { func (scope *Scope) Raw(sql string) *Scope {
scope.Sql = strings.Replace(sql, "$$", "?", -1) scope.Sql = strings.Replace(sql, "$$", "?", -1)
return scope return scope
} }
// Exec invoke sql
func (scope *Scope) Exec() *Scope { func (scope *Scope) Exec() *Scope {
defer scope.Trace(time.Now()) defer scope.Trace(time.Now())
@ -274,22 +297,26 @@ func (scope *Scope) Exec() *Scope {
return scope return scope
} }
func (scope *Scope) Get(name string) (value interface{}, ok bool) { // Set set value by name
value, ok = scope._values[name]
return
}
func (scope *Scope) Set(name string, value interface{}) *Scope { func (scope *Scope) Set(name string, value interface{}) *Scope {
scope._values[name] = value scope._values[name] = value
return scope return scope
} }
// Get get value by name
func (scope *Scope) Get(name string) (value interface{}, ok bool) {
value, ok = scope._values[name]
return
}
// Trace print sql log
func (scope *Scope) Trace(t time.Time) { func (scope *Scope) Trace(t time.Time) {
if len(scope.Sql) > 0 { if len(scope.Sql) > 0 {
scope.db.slog(scope.Sql, t, scope.SqlVars...) scope.db.slog(scope.Sql, t, scope.SqlVars...)
} }
} }
// Begin start a transaction
func (scope *Scope) Begin() *Scope { func (scope *Scope) Begin() *Scope {
if db, ok := scope.DB().(sqlDb); ok { if db, ok := scope.DB().(sqlDb); ok {
if tx, err := db.Begin(); err == nil { if tx, err := db.Begin(); err == nil {
@ -300,6 +327,7 @@ func (scope *Scope) Begin() *Scope {
return scope return scope
} }
// CommitOrRollback commit current transaction if there is no error, otherwise rollback it
func (scope *Scope) CommitOrRollback() *Scope { func (scope *Scope) CommitOrRollback() *Scope {
if _, ok := scope.Get("gorm:started_transaction"); ok { if _, ok := scope.Get("gorm:started_transaction"); ok {
if db, ok := scope.db.db.(sqlTx); ok { if db, ok := scope.db.db.(sqlTx); ok {