func SerializeModel(backend db.Backend, m kit.Model) (*ApiModel, []*ApiModel, apperror.Error) { modelData, err := backend.ModelToMap(m, true, false) if err != nil { return nil, nil, apperror.Wrap(err, "model_convert_error", "") } info := backend.ModelInfo(m.Collection()) data := &ApiModel{ Type: m.Collection(), Id: m.GetStrId(), Attributes: modelData, } // Build relationship data. includedModels := make([]*ApiModel, 0) // Check every model field. r := reflector.R(m).MustStruct() for fieldName, rel := range info.Relations() { // Retrieve the related model. field := r.Field(fieldName) if field != nil { return nil, nil, apperror.Wrap(err, "model_get_field_error") } // If field is zero value, skip. if field.IsZero() { continue } related := make([]kit.Model, 0) if !rel.IsMany() { // Make sure that we have a pointer. if !field.IsPtr() { field = field.Addr() } related = append(related, field.Interface().(kit.Model)) } else { slice := field.MustSlice() for _, item := range slice.Items() { if !item.IsPtr() { item = item.Addr() } related = append(related, item.Interface().(kit.Model)) } } for _, relatedModel := range related { // Convert the related model. relationData, included, err := SerializeModel(backend, relatedModel) if err != nil { return nil, nil, apperror.Wrap(err, "included_model_serialize_error", "") } // Build relation info and set in in relationships map. relation := &ApiModel{ Type: relatedModel.Collection(), Id: relatedModel.GetStrId(), } isSingle := !rel.IsMany() data.AddRelation(rel.MarshalName(), relation, isSingle) // Add related model to included data. includedModels = append(includedModels, relationData) // Add nested included models to included data. includedModels = append(includedModels, included...) } } return data, includedModels, nil }