func (resolver *resolver) GenerateDependencies() { var addToDependencies = func(data interface{}) { if IsPublishableModel(data) { scope := resolver.DB.NewScope(data) var primaryValues [][]interface{} for _, field := range scope.PrimaryFields() { primaryValues = append(primaryValues, []interface{}{field.DBName, field.Field.Interface()}) } resolver.AddDependency(&dependency{Type: utils.ModelType(data), PrimaryValues: [][][]interface{}{primaryValues}}) } if event, ok := data.(PublishEventInterface); ok { resolver.Events = append(resolver.Events, event) } } for _, record := range resolver.Records { reflectValue := reflect.Indirect(reflect.ValueOf(record)) if reflectValue.Kind() == reflect.Slice { for i := 0; i < reflectValue.Len(); i++ { addToDependencies(reflectValue.Index(i).Interface()) } } else { addToDependencies(record) } } }
func New(db *gorm.DB) *Publish { tableHandler := gorm.DefaultTableNameHandler gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string { tableName := tableHandler(db, defaultTableName) if db != nil { if IsPublishableModel(db.Value) { // Set join table handler typ := utils.ModelType(db.Value) if !injectedJoinTableHandler[typ] { injectedJoinTableHandler[typ] = true scope := db.NewScope(db.Value) for _, field := range scope.GetModelStruct().StructFields { if many2many := utils.ParseTagOption(field.Tag.Get("gorm"))["MANY2MANY"]; many2many != "" { db.SetJoinTableHandler(db.Value, field.Name, &publishJoinTableHandler{}) db.AutoMigrate(db.Value) } } } var forceDraftTable bool if forceDraftTable, ok := db.Get("publish:force_draft_table"); ok { if forceMode, ok := forceDraftTable.(bool); ok && forceMode { forceDraftTable = true } } if IsDraftMode(db) || forceDraftTable { return DraftTableName(tableName) } } } return tableName } db.AutoMigrate(&PublishEvent{}) db.Callback().Create().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true)) db.Callback().Create().Before("gorm:commit_or_rollback_transaction"). Register("publish:sync_to_production_after_create", syncCreateFromProductionToDraft) db.Callback().Create().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent) db.Callback().Delete().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true)) db.Callback().Delete().Replace("gorm:delete", deleteScope) db.Callback().Delete().Before("gorm:commit_or_rollback_transaction"). Register("publish:sync_to_production_after_delete", syncDeleteFromProductionToDraft) db.Callback().Delete().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent) db.Callback().Update().Before("gorm:begin_transaction").Register("publish:set_table_to_draft", setTableAndPublishStatus(true)) db.Callback().Update().Before("gorm:commit_or_rollback_transaction"). Register("publish:sync_to_production", syncUpdateFromProductionToDraft) db.Callback().Update().Before("gorm:commit_or_rollback_transaction").Register("gorm:create_publish_event", createPublishEvent) db.Callback().RowQuery().Register("publish:set_table_in_draft_mode", setTableAndPublishStatus(false)) db.Callback().Query().Before("gorm:query").Register("publish:set_table_in_draft_mode", setTableAndPublishStatus(false)) return &Publish{DB: db} }
func afterUpdate(scope *gorm.Scope) { if !scope.HasError() { if isLocalizable(scope) { if locale, ok := getLocale(scope); ok { if scope.DB().RowsAffected == 0 && !scope.PrimaryKeyZero() { //is locale and nothing updated var count int var query = fmt.Sprintf("%v.language_code = ? AND %v.%v = ?", scope.QuotedTableName(), scope.QuotedTableName(), scope.PrimaryKey()) // if enabled soft delete, delete soft deleted records if scope.HasColumn("DeletedAt") { scope.NewDB().Unscoped().Where("deleted_at is not null").Where(query, locale, scope.PrimaryKeyValue()).Delete(scope.Value) } // if no localized records exist, localize it if scope.NewDB().Table(scope.TableName()).Where(query, locale, scope.PrimaryKeyValue()).Count(&count); count == 0 { scope.DB().Create(scope.Value) } } } else if syncColumns := syncColumns(scope); len(syncColumns) > 0 { // is global if mode, _ := scope.DB().Get("l10n:mode"); mode != "unscoped" { if scope.DB().RowsAffected > 0 { var primaryField = scope.PrimaryField() var syncAttrs = map[string]interface{}{} if updateAttrs, ok := scope.InstanceGet("gorm:update_attrs"); ok { for key, value := range updateAttrs.(map[string]interface{}) { for _, syncColumn := range syncColumns { if syncColumn == key { syncAttrs[syncColumn] = value break } } } } else { var fields = scope.Fields() for _, syncColumn := range syncColumns { if field, ok := fields[syncColumn]; ok && field.IsNormal { syncAttrs[syncColumn] = field.Field.Interface() } } } if len(syncAttrs) > 0 { db := scope.DB().Model(reflect.New(utils.ModelType(scope.Value)).Interface()).Set("l10n:mode", "unscoped").Where("language_code <> ?", Global) if !primaryField.IsBlank { db = db.Where(fmt.Sprintf("%v = ?", primaryField.DBName), primaryField.Field.Interface()) } scope.Err(db.UpdateColumns(syncAttrs).Error) } } } } } } }
// New initialize qor resource func New(value interface{}) *Resource { var ( name = utils.HumanizeString(utils.ModelType(value).Name()) res = &Resource{Value: value, Name: name} ) res.FindOneHandler = res.findOneHandler res.FindManyHandler = res.findManyHandler res.SaveHandler = res.saveHandler res.DeleteHandler = res.deleteHandler return res }
func (resolver *resolver) GetDependencies(dep *dependency, primaryKeys [][][]interface{}) { value := reflect.New(dep.Type) fromScope := resolver.DB.NewScope(value.Interface()) draftDB := resolver.DB.Set("publish:draft_mode", true).Unscoped() for _, field := range fromScope.Fields() { if relationship := field.Relationship; relationship != nil { if isPublishableModel(field.Field.Interface()) { toType := utils.ModelType(field.Field.Interface()) toScope := draftDB.NewScope(reflect.New(toType).Interface()) draftTable := draftTableName(toScope.TableName()) var dependencyKeys [][][]interface{} var rows *sql.Rows var err error var selectPrimaryKeys []string for _, field := range toScope.PrimaryFields() { selectPrimaryKeys = append(selectPrimaryKeys, fmt.Sprintf("%v", toScope.Quote(field.DBName))) } if relationship.Kind == "has_one" || relationship.Kind == "has_many" { sql := fmt.Sprintf("%v IN (%v)", toQueryCondition(toScope, relationship.ForeignDBNames), toQueryMarks(primaryKeys, relationship.AssociationForeignDBNames...)) rows, err = draftDB.Table(draftTable).Select(selectPrimaryKeys).Where("publish_status = ?", DIRTY).Where(sql, toQueryValues(primaryKeys, relationship.AssociationForeignDBNames...)...).Rows() } else if relationship.Kind == "belongs_to" { fromTable := draftTableName(fromScope.TableName()) // toTable := toScope.TableName() sql := fmt.Sprintf("%v IN (SELECT %v FROM %v WHERE %v IN (%v))", strings.Join(relationship.AssociationForeignDBNames, ","), strings.Join(relationship.ForeignDBNames, ","), fromTable, scopePrimaryKeys(fromScope, fromTable), toQueryMarks(primaryKeys)) rows, err = draftDB.Table(draftTable).Select(selectPrimaryKeys).Where("publish_status = ?", DIRTY).Where(sql, toQueryValues(primaryKeys)...).Rows() } if rows != nil && err == nil { defer rows.Close() columns, _ := rows.Columns() for rows.Next() { var primaryValues = make([]interface{}, len(columns)) for idx := range primaryValues { var value interface{} primaryValues[idx] = &value } rows.Scan(primaryValues...) var currentDependencyKeys [][]interface{} for idx, value := range primaryValues { currentDependencyKeys = append(currentDependencyKeys, []interface{}{columns[idx], value}) } dependencyKeys = append(dependencyKeys, currentDependencyKeys) } resolver.AddDependency(&dependency{Type: toType, PrimaryValues: dependencyKeys}) } } if relationship.Kind == "many_to_many" { dep.ManyToManyRelations = append(dep.ManyToManyRelations, relationship) } } } }
func IsPublishableModel(model interface{}) (ok bool) { if model != nil { _, ok = reflect.New(utils.ModelType(model)).Interface().(publishInterface) } return }