From 91e937bac1dfa1ddd25ab131857cd74477aa1576 Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Wed, 29 Jan 2014 19:14:37 +0800 Subject: [PATCH] Add some documents --- doc/development.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++ scope.go | 38 +++++++++++++++++++++++---- 2 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 doc/development.md diff --git a/doc/development.md b/doc/development.md new file mode 100644 index 00000000..e1d6911d --- /dev/null +++ b/doc/development.md @@ -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 diff --git a/scope.go b/scope.go index fff9d379..dba091af 100644 --- a/scope.go +++ b/scope.go @@ -22,35 +22,43 @@ type Scope struct { skipLeft bool } +// NewScope create scope for callbacks, including DB's search information func (db *DB) NewScope(value interface{}) *Scope { db.Value = value 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 { return &Scope{db: scope.db.parent, Search: &search{}, Value: value} } +// NewDB create a new DB without search information func (scope *Scope) NewDB() *DB { return scope.db.new() } +// DB get *sql.DB func (scope *Scope) DB() sqlCommon { return scope.db.db } +// SkipLeft skip remaining callbacks func (scope *Scope) SkipLeft() { scope.skipLeft = true } +// Quote used to quote database column name according to database dialect func (scope *Scope) Quote(str string) string { return scope.Dialect().Quote(str) } +// Dialect get dialect func (scope *Scope) Dialect() dialect.Dialect { return scope.db.parent.dialect } +// Err write error func (scope *Scope) Err(err error) error { if err != nil { scope.db.err(err) @@ -58,22 +66,27 @@ func (scope *Scope) Err(err error) error { return err } +// Log print log message func (scope *Scope) Log(v ...interface{}) { scope.db.log(v...) } +// HasError check if there are any error func (scope *Scope) HasError() bool { return scope.db.Error != nil } +// PrimaryKey get the primary key's column name func (scope *Scope) PrimaryKey() string { return "id" } +// PrimaryKeyZero check the primary key is blank or not func (scope *Scope) PrimaryKeyZero() bool { return isBlank(reflect.ValueOf(scope.PrimaryKeyValue())) } +// PrimaryKeyValue get the primary key's value func (scope *Scope) PrimaryKeyValue() interface{} { data := reflect.Indirect(reflect.ValueOf(scope.Value)) @@ -85,11 +98,13 @@ func (scope *Scope) PrimaryKeyValue() interface{} { return 0 } +// HasColumn to check if has column func (scope *Scope) HasColumn(name string) bool { _, result := scope.FieldByName(name) return result } +// FieldByName to get column's value and existence func (scope *Scope) FieldByName(name string) (interface{}, bool) { data := reflect.Indirect(reflect.ValueOf(scope.Value)) @@ -103,6 +118,7 @@ func (scope *Scope) FieldByName(name string) (interface{}, bool) { return nil, false } +// SetColumn to set the column's value func (scope *Scope) SetColumn(column string, value interface{}) { if scope.Value == nil { return @@ -112,6 +128,7 @@ func (scope *Scope) SetColumn(column string, value interface{}) { setFieldValue(data.FieldByName(snakeToUpperCamel(column)), value) } +// CallMethod invoke method with necessary argument func (scope *Scope) CallMethod(name string) { if scope.Value == nil { 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 { scope.SqlVars = append(scope.SqlVars, value) return scope.Dialect().BinVar(len(scope.SqlVars)) } +// TableName get table name func (scope *Scope) TableName() string { if scope.Search != nil && len(scope.Search.TableName) > 0 { return scope.Search.TableName @@ -190,11 +209,13 @@ func (scope *Scope) TableName() string { } } +// CombinedConditionSql get combined condition sql func (scope *Scope) CombinedConditionSql() string { return scope.joinsSql() + scope.whereSql() + scope.groupSql() + scope.havingSql() + scope.orderSql() + scope.limitSql() + scope.offsetSql() } +// Fields get value's fields func (scope *Scope) Fields() []*Field { indirectValue := reflect.Indirect(reflect.ValueOf(scope.Value)) fields := []*Field{} @@ -259,11 +280,13 @@ func (scope *Scope) Fields() []*Field { return fields } +// Raw set sql func (scope *Scope) Raw(sql string) *Scope { scope.Sql = strings.Replace(sql, "$$", "?", -1) return scope } +// Exec invoke sql func (scope *Scope) Exec() *Scope { defer scope.Trace(time.Now()) @@ -274,22 +297,26 @@ func (scope *Scope) Exec() *Scope { return scope } -func (scope *Scope) Get(name string) (value interface{}, ok bool) { - value, ok = scope._values[name] - return -} - +// Set set value by name func (scope *Scope) Set(name string, value interface{}) *Scope { scope._values[name] = value 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) { if len(scope.Sql) > 0 { scope.db.slog(scope.Sql, t, scope.SqlVars...) } } +// Begin start a transaction func (scope *Scope) Begin() *Scope { if db, ok := scope.DB().(sqlDb); ok { if tx, err := db.Begin(); err == nil { @@ -300,6 +327,7 @@ func (scope *Scope) Begin() *Scope { return scope } +// CommitOrRollback commit current transaction if there is no error, otherwise rollback it func (scope *Scope) CommitOrRollback() *Scope { if _, ok := scope.Get("gorm:started_transaction"); ok { if db, ok := scope.db.db.(sqlTx); ok {