1
0
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:
kataras
2017-08-23 01:11:52 +03:00
parent e12513a534
commit 1ffe4479f7
13 changed files with 380 additions and 194 deletions

View File

@@ -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())
}
}

View File

@@ -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
View 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
}

View File

@@ -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)
}
}

View File

@@ -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