mirror of
https://github.com/kataras/iris.git
synced 2025-12-28 23:37:04 +00:00
Update to 8.3.3 | Even better MVC. Read HISTORY.md
Former-commit-id: 88998c317117abff1a214cf396b205d8d8ea888c
This commit is contained in:
@@ -151,8 +151,8 @@ func ActivateController(base BaseController, bindValues []interface{},
|
||||
binder := newBinder(typ.Elem(), bindValues)
|
||||
if binder != nil {
|
||||
for _, bf := range binder.fields {
|
||||
golog.Debugf("MVC %s: binder loaded for '%s' with field index of: %d",
|
||||
ctrlName, bf.Name, bf.Index)
|
||||
golog.Debugf("MVC %s: binder loaded for '%s' with value:\n%#v",
|
||||
ctrlName, bf.getFullName(), bf.getValue())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,79 +66,19 @@ func (b *binder) lookup(elem reflect.Type) (fields []field) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
if elemField.Type == value.Type() {
|
||||
// we area inside the correct type
|
||||
// println("[0] prepare bind filed for " + elemField.Name)
|
||||
fields = append(fields, field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
Type: elemField.Type,
|
||||
Value: value,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
f := lookupStruct(elemField.Type, value)
|
||||
if f != nil {
|
||||
fields = append(fields, field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
Type: elemField.Type,
|
||||
embedded: f,
|
||||
})
|
||||
}
|
||||
|
||||
matcher := func(elemField reflect.StructField) bool {
|
||||
return elemField.Type == value.Type()
|
||||
}
|
||||
|
||||
handler := func(f *field) {
|
||||
f.Value = value
|
||||
}
|
||||
|
||||
fields = append(fields, lookupFields(elem, matcher, handler)...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupStruct(elem reflect.Type, value reflect.Value) *field {
|
||||
// ignore if that field is not a struct
|
||||
if elem.Kind() != reflect.Struct {
|
||||
// and it's not a controller because we don't want to accidentally
|
||||
// set fields to other user fields. Or no?
|
||||
// ||
|
||||
// (elem.Name() != "" && !strings.HasSuffix(elem.Name(), "Controller")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// search by fields.
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
if elemField.Type == value.Type() {
|
||||
// println("Types are equal of: " + elemField.Type.Name() + " " + elemField.Name + " and " + value.Type().Name())
|
||||
// we area inside the correct type.
|
||||
return &field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
Type: elemField.Type,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// if field is struct and the value is struct
|
||||
// then try inside its fields for a compatible
|
||||
// field type.
|
||||
if elemField.Type.Kind() == reflect.Struct && value.Type().Kind() == reflect.Struct {
|
||||
elemFieldEmb := elem.Field(i)
|
||||
f := lookupStruct(elemFieldEmb.Type, value)
|
||||
if f != nil {
|
||||
fp := &field{
|
||||
Index: i,
|
||||
Name: elemFieldEmb.Name,
|
||||
Type: elemFieldEmb.Type,
|
||||
embedded: f,
|
||||
}
|
||||
return fp
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *binder) handle(c reflect.Value) {
|
||||
// we could make check for middlewares here but
|
||||
// these could easly be used outside of the controller
|
||||
|
||||
217
mvc/activator/field.go
Normal file
217
mvc/activator/field.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package activator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type field struct {
|
||||
Name string // the field's original name
|
||||
// but if a tag with `name: "other"`
|
||||
// exist then this fill is filled, otherwise it's the same as the Name.
|
||||
TagName string
|
||||
Index int
|
||||
Type reflect.Type
|
||||
Value reflect.Value
|
||||
|
||||
embedded *field
|
||||
}
|
||||
|
||||
// getIndex returns all the "dimensions"
|
||||
// of the controller struct field's position that this field is referring to,
|
||||
// recursively.
|
||||
// Usage: elem.FieldByIndex(field.getIndex())
|
||||
// for example the {0,1} means that the field is on the second field of the first's
|
||||
// field of this struct.
|
||||
func (ff field) getIndex() []int {
|
||||
deepIndex := []int{ff.Index}
|
||||
|
||||
if emb := ff.embedded; emb != nil {
|
||||
deepIndex = append(deepIndex, emb.getIndex()...)
|
||||
}
|
||||
|
||||
return deepIndex
|
||||
}
|
||||
|
||||
// getType returns the type of the referring field, recursively.
|
||||
func (ff field) getType() reflect.Type {
|
||||
typ := ff.Type
|
||||
if emb := ff.embedded; emb != nil {
|
||||
return emb.getType()
|
||||
}
|
||||
|
||||
return typ
|
||||
}
|
||||
|
||||
// getFullName returns the full name of that field
|
||||
// i.e: UserController.SessionController.Manager,
|
||||
// it's useful for debugging only.
|
||||
func (ff field) getFullName() string {
|
||||
name := ff.Name
|
||||
|
||||
if emb := ff.embedded; emb != nil {
|
||||
return name + "." + emb.getFullName()
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// getTagName returns the tag name of the referring field
|
||||
// recursively.
|
||||
func (ff field) getTagName() string {
|
||||
name := ff.TagName
|
||||
|
||||
if emb := ff.embedded; emb != nil {
|
||||
return emb.getTagName()
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// checkVal checks if that value
|
||||
// is valid to be set-ed to the new controller's instance.
|
||||
// Used on binder.
|
||||
func checkVal(val reflect.Value) bool {
|
||||
return val.IsValid() && (val.Kind() == reflect.Ptr && !val.IsNil()) && val.CanInterface()
|
||||
}
|
||||
|
||||
// getValue returns a valid value of the referring field, recursively.
|
||||
func (ff field) getValue() interface{} {
|
||||
if ff.embedded != nil {
|
||||
return ff.embedded.getValue()
|
||||
}
|
||||
|
||||
if checkVal(ff.Value) {
|
||||
return ff.Value.Interface()
|
||||
}
|
||||
|
||||
return "undefinied value"
|
||||
}
|
||||
|
||||
// sendTo should be used when this field or its embedded
|
||||
// has a Value on it.
|
||||
// It sets the field's value to the "elem" instance, it's the new controller.
|
||||
func (ff field) sendTo(elem reflect.Value) {
|
||||
// note:
|
||||
// we don't use the getters here
|
||||
// because we do recursively search by our own here
|
||||
// to be easier to debug if ever needed.
|
||||
if embedded := ff.embedded; embedded != nil {
|
||||
if ff.Index >= 0 {
|
||||
embedded.sendTo(elem.Field(ff.Index))
|
||||
}
|
||||
return
|
||||
}
|
||||
elemField := elem.Field(ff.Index)
|
||||
if elemField.Kind() == reflect.Ptr && !elemField.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
elemField.Set(ff.Value)
|
||||
}
|
||||
|
||||
// lookupTagName checks if the "elemField" struct's field
|
||||
// contains a tag `name`, if it contains then it returns its value
|
||||
// otherwise returns the field's original Name.
|
||||
func lookupTagName(elemField reflect.StructField) string {
|
||||
vname := elemField.Name
|
||||
|
||||
if taggedName, ok := elemField.Tag.Lookup("name"); ok {
|
||||
vname = taggedName
|
||||
}
|
||||
return vname
|
||||
}
|
||||
|
||||
// lookupFields iterates all "elem"'s fields and its fields
|
||||
// if structs, recursively.
|
||||
// Compares them to the "matcher", if they passed
|
||||
// then it executes the "handler" if any,
|
||||
// the handler can change the field as it wants to.
|
||||
//
|
||||
// It finally returns that collection of the valid fields, can be empty.
|
||||
func lookupFields(elem reflect.Type, matcher func(reflect.StructField) bool, handler func(*field)) (fields []field) {
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
if matcher(elemField) {
|
||||
field := field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
TagName: lookupTagName(elemField),
|
||||
Type: elemField.Type,
|
||||
}
|
||||
|
||||
if handler != nil {
|
||||
handler(&field)
|
||||
}
|
||||
|
||||
// we area inside the correct type
|
||||
fields = append(fields, field)
|
||||
continue
|
||||
}
|
||||
|
||||
f := lookupStructField(elemField.Type, matcher, handler)
|
||||
if f != nil {
|
||||
fields = append(fields, field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
Type: elemField.Type,
|
||||
embedded: f,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// lookupStructField is here to search for embedded field only,
|
||||
// is working with the "lookupFields".
|
||||
// We could just one one function
|
||||
// for both structured (embedded) fields and normal fields
|
||||
// but we keep that as it's, a new function like this
|
||||
// is easier for debugging, if ever needed.
|
||||
func lookupStructField(elem reflect.Type, matcher func(reflect.StructField) bool, handler func(*field)) *field {
|
||||
// fmt.Printf("lookup struct for elem: %s\n", elem.Name())
|
||||
|
||||
// ignore if that field is not a struct
|
||||
if elem.Kind() != reflect.Struct {
|
||||
return nil
|
||||
}
|
||||
|
||||
// search by fields.
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
if matcher(elemField) {
|
||||
// we area inside the correct type.
|
||||
f := &field{
|
||||
Index: i,
|
||||
Name: elemField.Name,
|
||||
TagName: lookupTagName(elemField),
|
||||
Type: elemField.Type,
|
||||
}
|
||||
|
||||
if handler != nil {
|
||||
handler(f)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// if field is struct and the value is struct
|
||||
// then try inside its fields for a compatible
|
||||
// field type.
|
||||
if elemField.Type.Kind() == reflect.Struct { // 3-level
|
||||
elemFieldEmb := elem.Field(i)
|
||||
f := lookupStructField(elemFieldEmb.Type, matcher, handler)
|
||||
if f != nil {
|
||||
fp := &field{
|
||||
Index: i,
|
||||
Name: elemFieldEmb.Name,
|
||||
TagName: lookupTagName(elemFieldEmb),
|
||||
Type: elemFieldEmb.Type,
|
||||
embedded: f,
|
||||
}
|
||||
return fp
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -11,14 +11,16 @@ type modelControl struct {
|
||||
}
|
||||
|
||||
func (mc *modelControl) Load(t *TController) error {
|
||||
fields := lookupFields(t, func(f reflect.StructField) bool {
|
||||
matcher := func(f reflect.StructField) bool {
|
||||
if tag, ok := f.Tag.Lookup("iris"); ok {
|
||||
if tag == "model" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
fields := lookupFields(t.Type.Elem(), matcher, nil)
|
||||
|
||||
if len(fields) == 0 {
|
||||
// first is the `Controller` so we need to
|
||||
@@ -34,15 +36,22 @@ func (mc *modelControl) Handle(ctx context.Context, c reflect.Value, methodFunc
|
||||
elem := c.Elem() // controller should always be a pointer at this state
|
||||
|
||||
for _, f := range mc.fields {
|
||||
elemField := elem.Field(f.Index)
|
||||
|
||||
index := f.getIndex()
|
||||
typ := f.getType()
|
||||
name := f.getTagName()
|
||||
|
||||
elemField := elem.FieldByIndex(index)
|
||||
// check if current controller's element field
|
||||
// is valid, is not nil and it's type is the same (should be but make that check to be sure).
|
||||
if !elemField.IsValid() || (elemField.Kind() == reflect.Ptr && elemField.IsNil()) || elemField.Type() != f.Type {
|
||||
if !elemField.IsValid() ||
|
||||
(elemField.Kind() == reflect.Ptr && elemField.IsNil()) ||
|
||||
elemField.Type() != typ {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldValue := elemField.Interface()
|
||||
// fmt.Printf("setting %s to %#v", f.Name, fieldValue)
|
||||
ctx.ViewData(f.Name, fieldValue)
|
||||
ctx.ViewData(name, fieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,77 +6,31 @@ import (
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
type field struct {
|
||||
Name string // by-defaultis the field's name but if `name: "other"` then it's overridden.
|
||||
Index int
|
||||
Type reflect.Type
|
||||
Value reflect.Value
|
||||
|
||||
embedded *field
|
||||
}
|
||||
|
||||
func (ff field) sendTo(elem reflect.Value) {
|
||||
if embedded := ff.embedded; embedded != nil {
|
||||
if ff.Index >= 0 {
|
||||
embedded.sendTo(elem.Field(ff.Index))
|
||||
}
|
||||
return
|
||||
}
|
||||
elemField := elem.Field(ff.Index)
|
||||
if elemField.Kind() == reflect.Ptr && !elemField.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
elemField.Set(ff.Value)
|
||||
}
|
||||
|
||||
func lookupFields(t *TController, validator func(reflect.StructField) bool) (fields []field) {
|
||||
elem := t.Type.Elem()
|
||||
|
||||
for i, n := 0, elem.NumField(); i < n; i++ {
|
||||
elemField := elem.Field(i)
|
||||
valF := t.Value.Field(i)
|
||||
|
||||
// catch persistence data by tags, i.e:
|
||||
// MyData string `iris:"persistence"`
|
||||
if validator(elemField) {
|
||||
name := elemField.Name
|
||||
if nameTag, ok := elemField.Tag.Lookup("name"); ok {
|
||||
name = nameTag
|
||||
}
|
||||
|
||||
f := field{
|
||||
Name: name,
|
||||
Index: i,
|
||||
Type: elemField.Type,
|
||||
}
|
||||
|
||||
if valF.IsValid() || (valF.Kind() == reflect.Ptr && !valF.IsNil()) {
|
||||
val := reflect.ValueOf(valF.Interface())
|
||||
if val.IsValid() || (val.Kind() == reflect.Ptr && !val.IsNil()) {
|
||||
f.Value = val
|
||||
}
|
||||
}
|
||||
|
||||
fields = append(fields, f)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type persistenceDataControl struct {
|
||||
fields []field
|
||||
}
|
||||
|
||||
func (d *persistenceDataControl) Load(t *TController) error {
|
||||
fields := lookupFields(t, func(f reflect.StructField) bool {
|
||||
if tag, ok := f.Tag.Lookup("iris"); ok {
|
||||
matcher := func(elemField reflect.StructField) bool {
|
||||
if tag, ok := elemField.Tag.Lookup("iris"); ok {
|
||||
if tag == "persistence" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
handler := func(f *field) {
|
||||
valF := t.Value.Field(f.Index)
|
||||
if valF.IsValid() || (valF.Kind() == reflect.Ptr && !valF.IsNil()) {
|
||||
val := reflect.ValueOf(valF.Interface())
|
||||
if val.IsValid() || (val.Kind() == reflect.Ptr && !val.IsNil()) {
|
||||
f.Value = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fields := lookupFields(t.Type.Elem(), matcher, handler)
|
||||
|
||||
if len(fields) == 0 {
|
||||
// first is the `Controller` so we need to
|
||||
|
||||
Reference in New Issue
Block a user